Unit 2: Introduction, Syntax, Variables, Identifiers, Constants and Comments
7 pages from PDFComplete content
Unit-2 Introduction, Syntax, Variables, Identifiers, Constants and Comments
Introduction
C++ was developed by Bjarne Stroustrup, as an extension to the C language.
C++ gives programmers a high level of control over system resources and memory.
C++ is a cross-platform language that can be used to create high-performance applications.
C++ is one of the world's most popular programming languages.
C++ can be found in today's operating systems, Graphical User Interfaces, and embedded systems.
🔥EXAM TOPIC: Structure, arrays and vectors (6 Marks).
C++ is an object-oriented programming language which gives a clear structure to programs and allows code to be reused.
C++ is portable and can be used to develop applications that can be adapted to multiple platforms.
Installation
A text editor, like Notepad, to write C++ code
A compiler, like GCC, to translate the C++ code into a language that the computer will understand. IDE
An IDE (Integrated Development Environment) is used to edit and compile the code.
Popular IDE's include CLion, CodeBlocks, Eclipse, and Visual Studio. These are all free, and they can be used to both edit and debug C++ code.
Web-based IDE's can work as well, but functionality is limited. Quick Start
🔥EXAM TOPIC: File handling and Exception handling (8 Marks). Expect a question on reading non-existing files.
Let's create our first C++ file. Open IDE and go to File > New > Empty File.
Write the following C++ code and save the file as myfirstprogram.cpp (File > Save File as):
#include <iostream> using namespace std;
int main() {
cout << "Hello World!";
return 0; }
Now click the play button to run (execute) the program and the output will be displayed in the console.
Syntax
🔥EXAM TOPIC: Classes, objects, constructors, and inheritance (5 Marks).
#include <iostream> is a header file library that allow to work with input and output objects, such as cout. Header files add functionality to C++ programs.
using namespace std means that we can use names for objects and variables from the standard library. You might see some C++ programs that run without the standard namespace line. The using namespace std; statement can be omitted, and replaced with the std keyword followed by the :: operator, for some objects (like std::cout).
A blank line. C++ ignores white space. But we use it to make the code more readable.
🔥EXAM TOPIC: Functions, parameters, and recursion (8 Marks). Very high chance of a recursion question.
Another thing that always appear in a C++ program is int main(). This is called a function. Any code inside its curly brackets {} will be executed.
cout (pronounced "see-out") is an object used together with the insertion operator (<<) to output/print text. In our example, it will output "Hello World!".
C++ is case-sensitive: "cout" and "Cout" has different meaning.
Every C++ statement ends with a semicolon ;.
The body of int main() could also been written as: int main () { cout << "Hello World! "; return 0; }
return 0; ends the main function.
Do not forget to add the closing curly bracket } to actually end the main function. Statements
In a programming language, instructions are called statements. For example:
cout << "Hello World!";
It is important that you end the statement with a semicolon ;
Most C++ programs contain many statements. The statements are executed, one by one, in the same order as they are written.
cout << "Hello World!"; cout << "Have a good day!"; return 0;
Output
The cout object, together with the << operator, is used to output values and print text. Just remember to surround the text with double quotes ("").
#include <iostream> using namespace std;
int main() {
cout << "Hello World!";
cout << "I am learning C++";
return 0; }
Print Numbers
You can also use cout() to print numbers. However, unlike text, we don't put numbers inside double quotes.
#include <iostream> using namespace std;
int main() {
cout << 3;
return 0; }
You can also perform mathematical calculations:
cout << 3 + 3; cout << 2 * 5;
New Lines
To insert a new line in your output, you can use the \n character.
#include <iostream> using namespace std;
int main() {
cout << "Hello World! \n";
cout << "I am learning C++";
return 0; }
You can also use another << operator and place the \n character after the text, like this:
#include <iostream> using namespace std;
int main() {
cout << "Hello World!" << "\n";
cout << "I am learning C++";
return 0; }
Two \n characters after each other will create a blank line:
#include <iostream> using namespace std;
int main() {
cout << "Hello World!" << "\n\n";
cout << "I am learning C++";
return 0; }
Another way to insert a new line, is with the endl manipulator:
#include <iostream> using namespace std;
int main() {
cout << "Hello World!" << endl;
cout << "I am learning C++";
return 0; }
🔥EXAM TOPIC: Loops and break/continue statements (4 Marks). Focus on nested loops.
Both \n and endl are used to break lines. However, \n is most used. The newline character (\n) is called an escape sequence, and it forces the cursor to change its position to the beginning of the next line on the screen. This results in a new line.
Likewise, \t is used to create a horizontal tab; \\ is used to insert a backslash character (\); \” is used to insert a double quote character.
Comments
Comments can be used to explain C++ code, and to make it more readable. It can also be used to prevent execution when testing alternative code. Comments can be singled-lined or multi-lined.
Single-line comments start with two forward slashes (//). Any text between
// and the end of the line is ignored by the compiler (will not be executed).
cout << "Hello World!";
// This is a comment
Multi-line comments start with /* and ends with */. Any text between /* and */ will be ignored by the compiler.
/* The code below will print the words Hello World! to the screen, and it is amazing */ cout << "Hello World!";
Variables
Variables are containers for storing data values.
In C++, there are different types of variables (defined with different keywords), for example:
int - stores integers (whole numbers), without decimals, such as 123 or -123
double - stores floating point numbers, with decimals, such as 19.99 or -19.99
char - stores single characters, such as 'a' or 'B'. Char values are surrounded by single quotes
🔥EXAM TOPIC: String and c-string functions (4 Marks).
string - stores text, such as "Hello World". String values are surrounded by double quotes
bool - stores values with two states: true or false Declaring (Creating) Variables
To create a variable, specify the type and assign it a value. Syntax:
type variableName = value;
Where type is one of C++ types (such as int), and variableName is the name of the variable (such as x or myName). The equal sign is used to assign values to the variable.
Create a variable called myNum of type int and assign it the value 15:
int myNum = 15; cout << myNum;
You can also declare a variable without assigning the value, and assign the value later: int myNum; myNum = 15; cout << myNum;
Changing Variable Values
Note that if you assign a new value to an existing variable, it will overwrite the previous value:
int myNum = 15;
// myNum is 15 myNum = 10;
// Now myNum is 10 cout << myNum;
// Outputs 10
Other Data Types
🔥EXAM TOPIC: Concept of data types and conversion/casting (4 Marks). Expect questions on implicit vs explicit casting.
A demonstration of other data types:
int myNum = 5;
// Integer (whole number without decimals) double myFloatNum = 5.99;
// Floating point number (with decimals)
char myLetter = 'D';
// Character string myText = "Hello";
// String (text) bool myBoolean = true;
// Boolean (true or false)
Display Variables
The cout object is used together with the << operator to display variables.
To combine both text and a variable, separate them with the << operator:
int myAge = 35; cout << "I am " << myAge << " years old.";
You can also combine different types
string name = "John"; int age = 35; double height = 6.1;
cout << name << " is " << age << " years old and " << height << " feet tall.";
Add Variables Together
To add a variable to another variable, you can use the + operator:
int x = 5; int y = 6; int sum = x + y; cout << sum;
Declaring More Variables
To declare more than one variable of the same type, use a comma-separated list:
int x = 5, y = 6, z = 50; cout << x + y + z;
One Value to Multiple Variables
You can also assign the same value to multiple variables in one line:
int x, y, z; x = y = z = 50; cout << x + y + z;
Identifiers
All C++ variables must be identified with unique names. These unique names are called identifiers.
Identifiers can be short names (like x and y) or more descriptive names (age, sum, totalVolume).
It is recommended to use descriptive names in order to create understandable and maintainable code.
// Good int minutesPerHour = 60;
// OK, but not so easy to understand what m actually is int m = 60;
The general rules for naming variables are:
Names can contain letters, digits and underscores
Names must begin with a letter or an underscore (_)
Names are case-sensitive (myVar and myvar are different variables)
Names cannot contain whitespaces or special characters like !, #, %, etc.
Reserved words (like C++ keywords, such as int) cannot be used as names
Constants
When you do not want others (or yourself) to change existing variable values, use the const keyword (this will declare the variable as "constant", which means unchangeable and read-only).
const int myNum = 15;
// myNum will always be 15 myNum = 10;
// error: assignment of read-only variable 'myNum'
You should always declare the variable as constant when you have values that are unlikely to change. When you declare a constant variable, it must be assigned with a value:
const int minutesPerHour = 60;
Something as follows will not work:
const int minutesPerHour; minutePerHour = 60;
// Error
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
What is the difference between a variable and a constant in C++? Give examples.
Explain the rules for naming identifiers in C++.
Why do we use comments? Write a single-line and multi-line comment.
What are keywords in C++? Can they be used as variable names?
Unit 3: Input, Output, Data Types, Operators, Strings, C-Strings, Math, Booleans, Conditionals and Loops
28 pages from PDFComplete content
🔥EXAM TOPIC: Concept of data types and conversion/casting (4 Marks). Expect questions on implicit vs explicit casting.
Unit-3 Input, Output, Data Types, Operators, Strings, C-Strings, Math, Booleans, Conditionals and Loops
User Input
Both cin and cout belongs to the <iostream> library, which is short for standard input / output streams.
You have already learned that cout is used to output (print) values. Now we will use cin to get user input.
cin is a predefined variable that reads data from the keyboard with the extraction operator (>>).
In the following example, the user can input a number, which is stored in the variable x. Then we print the value of x:
int x;
cout << "Type a number: ";
// Type a number and press enter cin >> x;
// Get user input from the keyboard cout << "Your number is: " << x;
// Display the input value
In this example, the user must input two numbers. Then we print the sum by calculating (adding) the two numbers:
int x, y; int sum; cout << "Type a number: "; cin >> x; cout << "Type another number: "; cin >> y; sum = x + y; cout << "Sum is: " << sum;
Data Types
As explained in the Variables chapter, a variable in C++ must be a specified data type:
int myNum = 5;
// Integer (whole number) float myFloatNum = 5.99;
// Floating point number double myDoubleNum = 9.98;
// Floating point number char myLetter = 'D';
// Character bool myBoolean = true;
// Boolean string myText = "Hello";
// String
Basic Data Types
The data type specifies the size and type of information the variable will store:
Data Type Size Description bool 1 byte Stores true or false values char 1 byte Stores a single character/letter/number, or ASCII values int 2 or 4 bytes Stores whole numbers, without decimals float 4 bytes Stores fractional numbers, containing one or more decimals. Sufficient for storing 6-7 decimal digits double 8 bytes Stores fractional numbers, containing one or more decimals. Sufficient for storing 15 decimal digits
Numeric Types
Use int when you need to store a whole number without decimals, like 35 or 1000, and float or double when you need a floating point number (with decimals), like 9.99 or 3.14515.
The char data type is used to store a single character. The character must be surrounded by single quotes, like 'A' or 'c':
char myGrade = 'B'; cout << myGrade;
Alternatively, if you are familiar with ASCII, you can use ASCII values to display certain characters:
char a = 65, b = 66, c = 67; cout << a; cout << b; cout << c;
🔥EXAM TOPIC: String and c-string functions (4 Marks).
String Types
🔥EXAM TOPIC: File handling and Exception handling (8 Marks). Expect a question on reading non-existing files.
To use strings, you must include an additional header file in the source code, the <string> library:
// Include the string library
#include <string>
// Create a string variable string greeting = "Hello";
// Output string value cout << greeting;
The string type is used to store a sequence of characters (text). This is not a built-in type, but it behaves like one in its most basic usage. String values must be surrounded by double quotes:
string greeting = "Hello"; cout << greeting;
The auto Keyword
Started in C++11, auto became a powerful way to let the compiler figure out the type based on the value you assign.
The auto keyword automatically detects the type of a variable based on the value you assign to it. It helps you write cleaner code and avoid repeating types, especially for long or complex types.
For example: Instead of writing int x = 5; you can write:
auto x = 5;
// x is automatically treated as int
Here's an example showing how auto can be used to create variables of different types, based on the values you assign:
// Creating auto variables auto myNum = 5;
// int auto myFloatNum = 5.99f;
// float auto myDoubleNum = 9.98;
// double
auto myLetter = 'D';
// char auto myBoolean = true;
// bool auto myString = string("Hello");
// std::string
C++ Operators
Operators are used to perform operations on variables and values.
In the example below, we use the + operator to add together two values:
int x = 100 + 50;
Although the + operator is often used to add together two values, like in the example above, it can also be used to add together a variable and a value, or a variable and another variable:
C++ divides the operators into the following groups:
Arithmetic Operators
Arithmetic operators are used to perform common mathematical operations.
Operator Name Description Example + Addition Adds together two values x + y - Subtraction Subtracts one value from another x - y * Multiplication Multiplies two values x * y / Division Divides one value by another x / y % Modulus Returns the division remainder x % y ++ Increment Increases the value of a variable by 1 ++x -- Decrement Decreases the value of a variable by 1 --x
Here is an example using different arithmetic operators in one example:
int x = 10; int y = 3;
cout << (x + y) << "\n";
// 13 cout << (x - y) << "\n";
// 7 cout << (x * y) << "\n";
// 30 cout << (x / y) << "\n";
// 3 (integer division) cout << (x % y) << "\n";
// 1
int z = 5;
++z; cout << z << "\n";
// 6 --z; cout << z << "\n";
// 5
When dividing two integers in C++, the result will also be an integer. For example, 10 / 3 gives 3. If you want a decimal result, use float or double values, like 10.0 / 3.
int x = 10; int y = 3; cout << (x / y) << "\n";
// Integer division, result is 3 double a = 10.0; double b = 3.0; cout << (a / b) << "\n";
// Decimal division, result is 3.333...
Increment and Decrement
🔥EXAM TOPIC: Loops and break/continue statements (4 Marks). Focus on nested loops.
Increment and decrement are very common in programming, especially when working with counters, loops, and arrays.
The ++ operator increases a value by 1, while the -- operator decreases a value by 1:
int x = 5;
++x;
// Increment x by 1 cout << x << "\n";
// 6
int x = 5; --x;
// Decrement x by 1 cout << x << "\n";
// 4
Assignment Operators
Assignment operators are used to assign values to variables.
In the example below, we use the assignment operator (=) to assign the value 10 to a variable called x:
int x = 10;
The addition compound assignment operator (+=) adds a value to a variable:
int x = 10; x += 5;
// same as x = x + 5
A list of all assignment operators:
Operator Example Same As = x = 5 x = 5 += x += 3 x = x + 3 -= x -= 3 x = x - 3 *= x *= 3 x = x * 3 /= x /= 3 x = x / 3 %= x %= 3 x = x % 3 &= x &= 3 x = x & 3 |= x |= 3 x = x | 3 ^= x ^= 3 x = x ^ 3 >>= x >>= 3 x = x >> 3 <<= x <<= 3 x = x << 3
Compound Assignment Operators
They are called Compound assignment operators because they combine a regular operator (like +, -, *, etc.) with the assignment operator (=) into one single operator. For example, += is a combination of + and =.
Compound assignment operators are a shorter way of writing operations where you use a variable in both sides of an assignment. For example, instead of writing x = x + 5;, you can simply write x += 5;.
int x = 10;
x += 5;
// same as x = x + 5 cout << x << "\n";
// 15
x *= 2;
// same as x = x * 2 cout << x << "\n";
// 30
Comparison Operators
Comparison operators are used to compare two values (or variables). This is important in programming, because it helps us to find answers and make decisions.
The return value of a comparison is either 1 or 0, which means true (1) or false (0). These values are known as Boolean values, and you will learn more about them in the Booleans and If..Else chapter.
In the following example, we use the greater than operator (>) to find out if 5 is greater than 3:
int x = 5; int y = 3; cout << (x > y);
// returns 1 (true) because 5 is greater than 3
A list of all comparison operators:
Operator Name Example == Equal to x == y != Not equal x != y > Greater than x > y < Less than x < y >= Greater than or equal to x >= y <= Less than or equal to x <= y
Logical Operators
As with comparison operators, you can also test for true (1) or false (0) values with logical operators. Logical operators are used to determine the logic between variables or values:
Operator Name Description Example &&
Logical and Returns true if both statements are true x < 5 &&
x < 10 ||
Logical or Returns true if one of the statements is true x < 5 || x < 4 ! Logical not Reverse the result, returns false if the result is true !(x < 5 && x < 10)
Operator Precedence
When a calculation contains more than one operator, C++ follows order of operations rules to decide which part to calculate first. For example, multiplication happens before addition:
In 2 + 3 * 4, the multiplication is done first, so the answer is 14. If you want the addition to happen first, you must use parentheses: (2 + 3) * 4, which gives 20.
Order of Operations: Here are some common operators in C++, from highest to lowest priority:
() - Parentheses
*, /, % - Multiplication, Division, Modulus
+, - - Addition, Subtraction
>, <, >=, <= - Comparison
==, != - Equality
&& - Logical AND
|| - Logical OR
= - Assignment Strings
To use strings, you must include an additional header file in the source code, the <string> library:
// Include the string library
#include <string>
// Create a string variable string greeting = "Hello";
// Print the string cout << greeting;
Strings are used for storing text/characters. For example, "Hello World" is a string.
A string variable contains a collection of characters surrounded by double quotes (""). Strings can contain multiple words, spaces, and punctuation. string greeting = "Hello and welcome!"; cout << greeting; String Concatenation
The + operator can be used between strings to add them together to make a new string. This is called concatenation:
In the example above, we added a space after firstName to create a space between John and Doe on output. However, you could also add a space with quotes (" " or ' '):
🔥EXAM TOPIC: Functions, parameters, and recursion (8 Marks). Very high chance of a recursion question.
A string in C++ is actually an object, which contain functions that can perform certain operations on strings. For example, you can also concatenate strings with the append() function: string firstName = "John "; string lastName = "Doe"; string fullName = firstName.append(lastName); cout << fullName;
String Length
To get the length of a string, use the length() function:
string txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; cout << "The length of the txt string is: " << txt.length();
You might see some C++ programs that use the size() function to get the length of a string. This is just an alias of length(). It is completely up to you if you want to use length() or size().
string txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; cout << "The length of the txt string is: " << txt.size();
Access Strings
You can access the characters in a string by referring to its index number inside square brackets [].
This example prints the first character in myString:
string myString = "Hello"; cout << myString[0];
// Outputs H
To print the last character of a string, you can use the following code:
To change the value of a specific character in a string, refer to the index number, and use single quotes:
string myString = "Hello"; myString[0] = 'J'; cout << myString;
// Outputs Jello instead of Hello
Adding Numbers and Strings
If you add two numbers, the result will be a number:
int x = 10; int y = 20; int z = x + y;
// z will be 30 (an integer)
If you add two strings, the result will be a string concatenation:
string x = "10"; string y = "20"; string z = x + y;
// z will be 1020 (a string)
The at() function
The <string> library also has an at() function that can be used to access characters in a string: string myString = "Hello"; cout << myString;
// Outputs Hello
cout << myString.at(0);
// First character cout << myString.at(1);
// Second character cout << myString.at(myString.length() - 1);
// Last character
myString.at(0) = 'J'; cout << myString;
// Outputs Jello
C++ string Functions
The <string> library has many functions that allow you to perform tasks on strings.
Function Description append() Adds characters or another string to the end of the current string at() Returns the character at a specified index, with bounds checking back() Accesses the last character in the string begin() Returns an iterator pointing to the first character of the string c_str() Returns a C-style null-terminated string clear() Removes all characters, making the string empty compare() Compares the string with another string and returns the result
🔥EXAM TOPIC: Structure, arrays and vectors (6 Marks).
copy() Copies characters from the string into a character array data() Returns a pointer to the string's internal character array empty() Checks whether the string is empty end() Returns an iterator pointing just past the last character erase() Deletes part of the string by position and length find() Finds the first occurrence of a character or substring front() Accesses the first character in the string insert() Inserts characters or a substring at a specified position length() Returns the number of characters in the string max_size() Returns the maximum number of characters of a string operator[] Returns the character at a given index pop_back() Removes the last character from the string push_back() Adds a single character to the end of the string replace() Replaces part of the string with new content rfind() Finds the last occurrence of a character or substring resize() Changes the size of the string, either trimming or padding it size() Alias of length(); returns the string's length substr() Returns a portion of the string, starting at a given index and length swap() Exchanges the contents of two strings
Strings - Special Characters
Because strings must be written within quotes, C++ will misunderstand this string, and generate an error:
string txt = "We are the so-called "Vikings" from the north.";
The solution to avoid this problem, is to use the backslash escape character. The backslash (\) escape character turns special characters into string characters:
Escape character Result Description \' ' Single quote \" " Double quote \\ \ Backslash
The sequence \"
inserts a double quote in a string:
string txt = "We are the so-called \"Vikings\" from the north.";
The sequence \'
inserts a single quote in a string: string txt = "It\'s alright.";
The sequence \\
inserts a single backslash in a string:
string txt = "The character \\ is called backslash.";
Other popular escape characters in C++ are: Escape Character Result \n New Line \t Tab
User Input Strings
It is possible to use the extraction operator >> on cin to store a string entered by a user:
string firstName; cout << "Type your first name: "; cin >> firstName;
// get user input from the keyboard cout << "Your name is: " << firstName;
// Type your first name: John
// Your name is: John
However, cin considers a space (whitespace, tabs, etc) as a terminating character, which means that it can only store a single word (even if you type many words):
string fullName; cout << "Type your full name: "; cin >> fullName; cout << "Your name is: " << fullName;
// Type your full name: John Doe
// Your name is: John
From the example above, you would expect the program to print "John Doe", but it only prints "John". That's why, when working with strings, we often use the getline() function to read a line of text. It takes cin as the first parameter, and the string variable as second:
string fullName; cout << "Type your full name: "; getline (cin, fullName); cout << "Your name is: " << fullName;
// Type your full name: John Doe
// Your name is: John Doe
String Namespace
🔥EXAM TOPIC: Classes, objects, constructors, and inheritance (5 Marks).
You might see some C++ programs that run without the standard namespace library. The using namespace std line can be omitted and replaced with the std keyword, followed by the :: operator for string (and cout) objects:
#include <iostream>
#include <string>
// using namespace std; - Remove this line
int main() {
std::string greeting = "Hello";
std::cout << greeting;
return 0; }
C-Style Strings
C-style strings are created with the char type instead of string. The name comes from the C language, which, unlike many other programming languages, does not have a string type for easily creating string variables. Instead, you must use the char type and create an array of characters to make a "string" in C.
As C++ was developed as an extension of C, it continued to support this way of creating strings in C++: string greet1 = "Hello";
It is more convenient to work with the standard string type, rather than C-style strings. However, one reason some users continue to use C-style strings is that they have access to functions from the C standard library.
The <cstring> library has many functions that allow you to perform tasks on arrays and C-style strings. A C-style string is an array of characters, created with the char type.
C++ C-Style string Functions
The <cstring> library has many functions that allow you to perform tasks on arrays and C-style strings.
C-style strings are different than regular strings. A C-style string is an array of characters, created with the char type.
A list of all cstring functions can be found in the table below.
🔥EXAM TOPIC: Pointers and pass/call by reference (5 Marks).
Function Description memchr() Returns a pointer to the first occurrence of a value in a block of memory memcmp() Compares two blocks of memory to determine which one represents a larger numeric value memcpy() Copies data from one block of memory to another
memmove() Copies data from one block of memory to another accounting for the possibility that the blocks of memory overlap memset() Sets all of the bytes in a block of memory to the same value strcat() Appends one C-style string to the end of another strchr() Returns a pointer to the first occurrence of a character in a C-style string strcmp() Compares the ASCII values of characters in two C-style strings to determine which string has a higher value strcoll() Compares the locale-based values of characters in two C-style strings to determine which string has a higher value strcpy() Copies the characters of a C-style string into the memory of another string strcspn() Returns the length of a C-style string up to the first occurrence of one of the specified characters strerror() Returns a C-style string describing the meaning of an error code strlen() Return the length of a C-style string strncat() Appends a number of characters from a C-style string to the end of another string strncmp() Compares the ASCII values of a specified number of characters in two C-style strings to determine which string has a higher value strncpy() Copies a number of characters from one C-style string into the memory of another string strpbrk() Returns a pointer to the first position in a C-style string which contains one of the specified characters strrchr() Returns a pointer to the last occurrence of a character in a C-style string strspn() Returns the length of a C-style string up to the first character which is not one of the specified characters strstr() Returns a pointer to the first occurrence of a C-style string in another string strtok() Splits a string into pieces using delimiters strxfrm() Convert characters in a C-style string from ASCII encoding to the encoding of the current locale
C++ Math
C++ has many functions that allows you to perform mathematical tasks on numbers.
The max(x,y) function can be used to find the highest value of x and y:
cout << max(5, 10);
The min(x,y) function can be used to find the lowest value of x and y:
cout << min(5, 10);
Other functions, such as sqrt (square root), round (rounds a number) and log (natural logarithm), can be found in the <cmath> header file:
// Include the cmath library
#include <cmath>
cout << sqrt(64); cout << round(2.6); cout << log(2);
Boolean Values
Very often, in programming, you will need a data type that can only have one of two values, like:
YES / NO
ON / OFF
TRUE / FALSE
For this, C++ has a bool data type, which can take the values true (1) or false (0).
A boolean variable is declared with the bool keyword and can take the values true or false: bool isCodingFun = true; bool isFishTasty = false;
A Boolean expression is a piece of code that compares values or variables and returns a boolean value: 1 (true) or 0 (false). Boolean expressions are the foundation of decision making in programming - they let your program decide what to do based on whether something is true or false.
You can use a comparison operator, such as the greater than (>) operator, to find out if an expression (or variable) is true or false:
int x = 10; int y = 9; cout << (x > y);
// returns 1 (true), because 10 is higher than 9 cout << (10 > 9);
// returns 1 (true), because 10 is higher than 9
In the examples below, we use the equal to (==) operator to evaluate an expression:
int x = 10; cout << (x == 10);
// returns 1 (true), because the value of x is equal to 10 cout << (10 == 15);
// returns 0 (false), because 10 is not equal to 15
You can also store the result of a comparison in a bool variable:
int x = 10; int y = 9;
bool isGreater = x > y;
cout << isGreater;
// returns 1 (true)
Type Conversion/ Casting
Sometimes, you have to convert the value of one data type to another type. This is known as type conversion. For example, if you try to divide two integers 5 by 2, you might expect the result to be 2.5. But since we are working with integers (and not floating-point values), the following example will just output 2.
There are two types of conversion in C++:
Implicit Conversion (automatically): Implicit conversion is done automatically by the compiler when you mix types in an expression or assign a value of one type to another. For example, assigning an float to integer:
#include <iostream> using namespace std;
int main() { int myInt = 9.99;
// Automatic conversion: float to int
cout << myInt;
// 9 return 0; }
Explicit Conversion (manually): Explicit conversion is done manually. In C++ you should prefer the static_cast<> operator because it is clear and type-safe.
#include <iostream> using namespace std;
int main() { int num1 = 5; int num2 = 2; double sum = static_cast<double>(num1) / num2;
cout << sum;
// 2.5 return 0; }
You may also see the C-style cast syntax, for example (double)num1. It works, but modern C++ encourages using static_cast<> because it is clearer and helps the compiler find mistakes.
#include <iostream> using namespace std;
int main() { int a = 7, b = 4; double q = (double)a / b;
// 1.75
cout << q; return 0; }
If you want to control the number of decimals in the output, use iostream formatting:
#include <iostream>
#include <iomanip>
// for fixed and setprecision using namespace std;
int main() { int num1 = 5; int num2 = 2; double sum = static_cast<double>(num1) / num2;
cout << fixed << setprecision(1) << sum;
// 2.5 return 0; }
Conditional Statements
You already know that C++ supports familiar comparison conditions from mathematics, such as:
Less than: a < b
Less than or equal to: a <= b
Greater than: a > b
Greater than or equal to: a >= b
Equal to: a == b
Not equal to: a != b
🔥EXAM TOPIC: Conditional statements - nested or ladder if..else (4 Marks). Practice nested if-else logic.
C++ has the following conditional statements:
Use if to specify a block of code to be executed, if a condition is true
Use else to specify a block of code to be executed, if the same condition is false
Use else if to specify a new condition to test, if the first condition is false
Use switch to specify many alternative blocks of code to be executed
The if Statement
Use the if statement to specify a block of C++ code to be executed if a condition is true.
Syntax: if (condition) {
// block of code to be executed if the condition is true }
In the example below, we test two values to find out if 20 is greater than 18. If the condition is true, we print a message:
if (20 > 18) {
cout << "20 is greater than 18"; }
We can also use variables in conditions: we use two variables, x and y, to test whether x is greater than y. Because 20 is greater than 18, the condition is true, and the message is printed.
int x = 20; int y = 18;
if (x > y) {
cout << "x is greater than y"; }
The else Statement
Use the else statement to specify a block of code to be executed if the condition is false.
Syntax:
if (condition) {
// block of code to be executed if the condition is true } else {
// block of code to be executed if the condition is false }
In the example below, the program checks the value of time. If it is less than 18, it prints "Good day". Otherwise, it prints "Good evening": Because time is 20, the condition time < 18 is false, so the code inside the else block runs and prints "Good evening.". If time was less than 18, the program would print "Good day." instead.
int time = 20; if (time < 18) {
cout << "Good day."; } else {
cout << "Good evening."; }
// Outputs "Good evening."
The else if Statement
Use the else if statement to specify a new condition to test if the first condition is false. You can use else if to check multiple conditions, one after another.
Syntax:
if (condition1) {
// block of code to be executed if condition1 is true } else if (condition2) {
// block of code to be executed if condition1 is false and condition2 is true } else {
// block of code to be executed if both conditions are false }
The conditions are checked from top to bottom. As soon as one condition is true, its block of code is executed, and the rest are skipped.
In the example below, we decide which message to print based on the value of time: The value of time is 16. The first condition (time < 12) is false, but the second condition (time < 18) is true. Because of this, the code inside the else if block runs, and "Good day." is printed. If the value of time was 22, none of the conditions would be true, and the program would print "Good evening." instead.
int time = 16; if (time < 12) {
cout << "Good morning."; } else if (time < 18) {
cout << "Good day."; } else {
cout << "Good evening."; }
// Outputs "Good day."
Short Hand If...Else (Ternary Operator)
There is also a short-hand if...else, known as the ternary operator because it uses three operands. The ternary operator returns a value based on a condition: if the condition is true, it returns the first value; otherwise, it returns the second value.
It can be used to replace multiple lines of code with a single line, and is often used to replace simple if...else statements. Syntax:
Example: int time = 20; string result = (time < 18) ? "Good day." : "Good evening."; cout << result;
You can nest ternary operators to handle more than two outcomes, but it can make your code harder to read:
int time = 22; string message = (time < 12) ? "Good morning."
: (time < 18) ? "Good afternoon."
: "Good evening."; cout << message;
Nested If
You can also place an if statement inside another if. This is called a nested if statement. A nested if lets you check for a condition only if another condition is already true.
Syntax:
if (condition1) {
// code to run if condition1 is true if (condition2) {
// code to run if both condition1 and condition2 are true } }
In this example, we first check if x is greater than 10. If it is, we then check if y is greater than 20:
int x = 15; int y = 25;
if (x > 10) { cout << "x is greater than 10\n";
// Nested if if (y > 20) { cout << "y is also greater than 20\n"; } }
Logical Operators in Conditions
You can combine or reverse conditions using logical operators. These work together with if, else, and else if to build more complex decisions.
&& (AND) - all conditions must be true
|| (OR) - at least one condition must be true
! (NOT) - reverses a condition (true → false, false → true) AND (&&)
Use AND (&&) when both conditions must be true. For example: Test if a is greater than b, and if c is greater than a:
int a = 200; int b = 33; int c = 500;
if (a > b && c > a) { cout << "Both conditions are true"; }
OR (||)
Use OR (||) when at least one of the conditions can be true. For example: Test if a is greater than b, or if a is greater than c:
int a = 200; int b = 33; int c = 500;
if (a > b || a > c) { cout << "At least one condition is true"; }
NOT (!)
The NOT operator (!) reverses a condition:
If a condition is true, ! makes it false.
If a condition is false, ! makes it true
This is useful when you want to check that something is not the case. For example: Test if a is not greater than b:
int a = 33; int b = 200;
if (!(a > b)) { cout << "a is NOT greater than b"; }
Switch Case Statement
Use the switch statement to select one of many code blocks to be executed. Syntax: switch(expression) {
The value of the expression is compared with the values of each case
If there is a match, the associated block of code is executed
The example below uses the weekday number to calculate the weekday name:
int day = 4; switch (day) {
case 1:
cout << "Monday";
break;
case 2:
cout << "Tuesday";
break;
case 3:
cout << "Wednesday";
break;
case 4:
cout << "Thursday";
break;
case 5:
cout << "Friday";
break;
case 6:
cout << "Saturday";
break;
case 7:
cout << "Sunday";
break; }
// Outputs "Thursday" (day 4)
When C++ reaches a break keyword, it breaks out of the switch block. This will stop the execution of more code and case testing inside the block. When a match is found, and the job is done, it's time for a break. There is no need for more testing.
The default keyword specifies some code to run if there is no case match:
Loops/ Iterations
Loops can execute a block of code as long as a specified condition is reached. Loops are handy because they save time, reduce errors, and they make code more readable.
The While Loop
The while loop loops through a block of code as long as a specified condition is true. Syntax:
while (condition) {
// code block to be executed }
In the example below, the code in the loop will run, over and over again, as long as a variable (i) is less than 5:
int i = 0; while (i < 5) {
cout << i << "\n";
i++; }
This example counts down from 3 to 1 and then displays "Happy New Year!!" at the end:
int countdown = 3;
while (countdown > 0) {
cout << countdown << "\n";
countdown--; }
cout << "Happy New Year!!\n";
The Do/While Loop
The do/while loop is a variant of the while loop. This loop will execute the code block once, before checking if the condition is true. Then it will repeat the loop as long as the condition is true.
Syntax:
do {
// code block to be executed } while (condition);
The example below uses a do/while loop. The loop will always be executed at least once, even if the condition is false, because the code block is executed before the condition is tested.
int i = 0; do {
cout << i << "\n";
i++; } while (i < 5);
The do/while loop always runs at least once, even if the condition already false. This is different from a regular while loop, which would skip the loop entirely if the condition is false at the start. The behavior makes do/while useful when you want to ensure something happens at least once, like showing a message or asking for user input.
The For Loop
When you know exactly how many times you want to loop through a block of code, use the for loop instead of a while loop. Syntax:
for (statement 1; statement 2; statement 3) {
// code block to be executed }
Statement 1 is executed (one time) before the execution of the code block. Statement 2 defines the condition for executing the code block. Statement 3 is executed (every time) after the code block has been executed.
The example below will print the numbers 0 to 4:
for (int i = 0; i < 5; i++) {
cout << i << "\n"; }
This example prints even values between 0 and 10:
for (int i = 0; i <= 10; i = i + 2) {
cout << i << "\n"; }
This example calculates the sum of numbers from 1 to 5:
int sum = 0; for (int i = 1; i <= 5; i++) {
sum = sum + i;
} cout << "Sum is " << sum;
This example prints a countdown from 5 to 1:
for (int i = 5; i > 0; i--) {
cout << i << "\n"; }
Nested Loops
It is also possible to place a loop inside another loop. This is called a nested loop. The "inner loop" will be executed one time for each iteration of the "outer loop":
// Outer loop for (int i = 1; i <= 2; ++i) {
cout << "Outer: " << i << "\n";
// Executes 2 times
// Inner loop
for (int j = 1; j <= 3; ++j) {
cout << " Inner: " << j << "\n";
// Executes 6 times (2 * 3)
} }
This example uses nested loops to print a simple multiplication table (1 to 3):
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
cout << i * j << " ";
}
cout << "\n"; }
The foreach Loop
The for-each loop was introduced in C++ version 11 (2011). There is also a "for-each loop" (also known as ranged-based for loop), which is used to loop through elements in an array. Syntax:
for (type variableName : arrayName) {
// code block to be executed }
The following example outputs all elements in an array, using a "for-each loop":
int myNumbers[5] = {10, 20, 30, 40, 50}; for (int num : myNumbers) {
cout << num << "\n"; }
You can also use a for-each loop to loop through characters in a string:
string word = "Hello"; for (char c : word) {
cout << c << "\n"; }
Break and Continue
The break statement can also be used to jump out of a loop. This example jumps out of the loop when i is equal to 4:
for (int i = 0; i < 10; i++) {
if (i == 4) {
break;
}
cout << i << "\n"; }
The continue statement breaks one iteration (in the loop), if a specified condition occurs, and continues with the next iteration in the loop. This example skips the value of 4: for (int i = 0; i < 10; i++) {
if (i == 4) {
continue;
}
cout << i << "\n"; }
You can also use break and continue in while loops:
//Break Example int i = 0; while (i < 10) {
cout << i << "\n";
i++;
if (i == 4) {
break;
} }
//Continue Example int i = 0; while (i < 10) {
if (i == 4) {
i++;
continue;
}
cout << i << "\n";
i++; }
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
Explain different data types available in C++ with their sizes.
Write a C++ program to find the largest of three numbers using a nested if-else.
Differentiate between implicit and explicit type conversion.
Explain break and continue statements with suitable examples inside loops.
Unit 4: Arrays, Vectors, Structures, Enums, References and Pointers
20 pages from PDFComplete content
🔥EXAM TOPIC: Structure, arrays and vectors (6 Marks).
Unit-4 Arrays, Vectors, Structures, Enum and Pointers
C++ Arrays
Arrays are used to store multiple values in a single variable, instead of declaring separate variables for each value.
To declare an array, define the variable type, specify the name of the array followed by square brackets and specify the number of elements it should store:
string cars[4];
🔥EXAM TOPIC: String and c-string functions (4 Marks).
We have now declared a variable that holds an array of four strings. To insert values to it, we can use an array literal - place the values in a comma-separated list, inside curly braces:
To create an array of three integers, you could write:
int myNum[3] = {10, 20, 30};
Access the Elements of an Array
You access an array element by referring to the index number inside square brackets [].
This statement accesses the value of the first element in cars: string cars[4] = {"Volvo", "BMW", "Ford", "Mazda"}; cout << cars[0];
// Outputs Volvo
Change an Array Element
To change the value of a specific element, refer to the index number:
cars[0] = "Opel";
Example:
string cars[4] = {"Volvo", "BMW", "Ford", "Mazda"}; cars[0] = "Opel"; cout << cars[0];
// Now outputs Opel instead of Volvo
Loop Through an Array
🔥EXAM TOPIC: Loops and break/continue statements (4 Marks). Focus on nested loops.
You can loop through the array elements with the for loop. The following example outputs all elements in the cars array:
// Create an array of strings string cars[5] = {"Volvo", "BMW", "Ford", "Mazda", "Tesla"};
// Loop through strings for (int i = 0; i < 5; i++) {
cout << cars[i] << "\n"; }
This example outputs the index of each element together with its value:
string cars[5] = {"Volvo", "BMW", "Ford", "Mazda", "Tesla"}; for (int i = 0; i < 5; i++) {
cout << i << " = " << cars[i] << "\n"; }
And this example shows how to loop through an array of integers:
int myNumbers[5] = {10, 20, 30, 40, 50}; for (int i = 0; i < 5; i++) {
cout << myNumbers[i] << "\n"; }
The foreach Loop
There is also a "for-each loop" (introduced in C++ version 11 (2011)), which is used exclusively to loop through elements in an array (and other data structures, like vectors and lists).
Syntax:
for (type variableName : arrayName) {
// code block to be executed }
The following examples output all elements in an array using a "for-each loop":
// Create an array of integers int myNumbers[5] = {10, 20, 30, 40, 50};
// Loop through integers for (int num : myNumbers) {
cout << num << "\n"; }
Example: Loop through strings:
// Create an array of strings string cars[5] = {"Volvo", "BMW", "Ford", "Mazda", "Tesla"};
// Loop through strings for (string car : cars) {
cout << car << "\n"; }
Omit Array Size
In C++, you don't have to specify the size of the array. The compiler is smart enough to determine the size of the array based on the number of inserted values:
string cars[] = {"Volvo", "BMW", "Ford"};
// Three array elements
The example above is equal to:
string cars[3] = {"Volvo", "BMW", "Ford"};
// Also three array elements
However, the last approach is considered as "good practice", because it will reduce the chance of errors in your program.
Omit Elements on Declaration
It is also possible to declare an array without specifying the elements on declaration, and add them later:
Note: The example above only works when you have specified the size of the array. If you don't specify the array size, an error occurs.
string cars[];
// Array size is not specified cars[0] = "Volvo"; cars[1] = "BMW"; cars[2] = "Ford"; cars[3] = "Mazda"; cars[4] = "Tesla";
// error: array size missing in 'cars'
Fixed Size (Arrays) vs. Dynamic Size (Vectors)
You will often hear the terms "fixed size" and "dynamic size" when discussing arrays in C++. This is because the size of an array in C++ is fixed, meaning you cannot add or remove elements after it is created.
Arrays - Fixed Size Example
/ An array with 3 elements string cars[3] = {"Volvo", "BMW", "Ford"};
// Trying to add another element (a fourth element) to the cars array will result in an error cars[3] = "Tesla";
Vectors
Vector is a sequence container that represents a dynamic array which can automatically resize itself when elements are added or removed. Unlike standard arrays whose size is fixed at compile-time, vectors manage their own storage and grow as needed at runtime.
For operations that require adding and removing array elements, C++ provides vectors, which are resizable arrays. The size of a vector is dynamic, meaning it can grow and shrink as needed.
🔥EXAM TOPIC: Functions, parameters, and recursion (8 Marks). Very high chance of a recursion question.
Vectors are found in the <vector> library, and they come with many useful functions to add, remove and modify elements.
Vectors - Dynamic Size Example:
// A vector with 3 elements vector<string> cars = {"Volvo", "BMW", "Ford"};
// Adding an element at the end of the vector cars.push_back("Tesla");
// Removing an element from the end of the vector cars.pop_back();
// Returns the total number of elements currently stored cars.size();
// Access a specific element
cars.at(1);
// Returns the last element cars.back();
// Clear out all the elements cars.clear();
// Loop over vector for (string car : cars){ cout << car; }
Get the Size of an Array
To get the size of an array, you can use the sizeof() operator:
Why did the result show 20 instead of 5, when the array contains 5 elements? It is because the sizeof() operator returns the size of a type in bytes.
🔥EXAM TOPIC: Concept of data types and conversion/casting (4 Marks). Expect questions on implicit vs explicit casting.
You learned from the Data Types chapter that an int type is usually 4 bytes, so from the example above, 4 x 5 (4 bytes x 5 elements) = 20 bytes.
To find out how many elements an array has, you have to divide the size of the array by the size of the first element in the array:
Example: int myNumbers[5] = {10, 20, 30, 40, 50}; int getArrayLength = sizeof(myNumbers) / sizeof(myNumbers[0]); cout << getArrayLength; //5 Loop Through an Array with sizeof()
In the Arrays and Loops Chapter, we wrote the size of the array in the loop condition (i < 5). This is not ideal, since it will only work for arrays of a specified size.
However, by using the sizeof() approach from the example above, we can now make loops that work for arrays of any size, which is more sustainable.
Instead of writing:
int myNumbers[5] = {10, 20, 30, 40, 50}; for (int i = 0; i < 5; i++) {
cout << myNumbers[i] << "\n"; }
It is better to write:
int myNumbers[5] = {10, 20, 30, 40, 50}; for (int i = 0; i < sizeof(myNumbers) / sizeof(myNumbers[0]); i++) {
cout << myNumbers[i] << "\n"; }
Note that, in C++ version 11 (2011), you can also use the "for-each" loop, which is even cleaner and simpler:
int myNumbers[5] = {10, 20, 30, 40, 50}; for (int num : myNumbers) {
cout << num << "\n"; }
To demonstrate a practical example of using arrays, let's create a program that calculates the average of different ages:
// An array storing different ages int ages[8] = {20, 22, 18, 35, 48, 26, 87, 70};
float avg, sum = 0; int i;
// Get the length of the array int length = sizeof(ages) / sizeof(ages[0]);
// Loop through the elements of the array for (int age : ages) {
sum += age; }
// Calculate the average by dividing the sum by the length avg = sum / length;
// Print the average cout << "The average age is: " << avg << "\n";
And in this example, we create a program that finds the lowest age among different ages:
// An array storing different ages int ages[8] = {20, 22, 18, 35, 48, 26, 87, 70};
int i;
// Create a variable and assign the first array element of ages to it int lowestAge = ages[0];
// Loop through the elements of the ages array to find the lowest age
for (int age : ages) {
if (lowestAge > age) {
lowestAge = age;
} }
// Print the lowest age cout << "The lowest age is: " << lowestAge << "\n";
Multi-Dimensional Arrays
A multi-dimensional array is an array of arrays. To declare a multi-dimensional array, define the variable type, specify the name of the array followed by square brackets which specify how many elements the main array has, followed by another set of square brackets which indicates how many elements the sub-arrays have:
string letters[2][4];
As with ordinary arrays, you can insert values with an array literal - a comma-separated list inside curly braces. In a multi-dimensional array, each element in an array literal is another array literal.
Each set of square brackets in an array declaration adds another dimension to an array. An array like the one above is said to have two dimensions.
Arrays can have any number of dimensions. The more dimensions an array has, the more complex the code becomes. The following array has three dimensions:
string letters[2][2][2] = {
{
{ "A", "B" },
{ "C", "D" }
},
{
{ "E", "F" },
{ "G", "H" }
} };
Access the Elements of a Multi-Dimensional Array
To access an element of a multi-dimensional array, specify an index number in each of the array's dimensions.
This statement accesses the value of the element in the first row (0) and third column (2) of the letters array.
string letters[2][4] = {
{ "A", "B", "C", "D" },
{ "E", "F", "G", "H" } };
cout << letters[0][2];
// Outputs "C"
Change Elements in a Multi-Dimensional Array
To change the value of an element, refer to the index number of the element in each of the dimensions:
string letters[2][4] = {
{ "A", "B", "C", "D" },
{ "E", "F", "G", "H" } }; letters[0][0] = "Z";
cout << letters[0][0];
// Now outputs "Z" instead of "A"
Loop Through a Multi-Dimensional Array
To loop through a multi-dimensional array, you need one loop for each of the array's dimensions. The following example outputs all elements in the letters array:
This example shows how to loop through a three-dimensional array:
string letters[2][2][2] = {
{
{ "A", "B" },
{ "C", "D" }
},
{
{ "E", "F" },
{ "G", "H" }
} };
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
cout << letters[i][j][k] << "\n";
}
} }
Why Multi-Dimensional Arrays?
Multi-dimensional arrays are great at representing grids. This example shows a practical use for them. In the following example we use a multi-dimensional array to represent a small game of Battleship:
// We put "1" to indicate there is a ship. bool ships[4][4] = {
{ 0, 1, 1, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 1, 0 },
{ 0, 0, 1, 0 } };
// Keep track of how many hits the player has and how many turns they have played in these variables int hits = 0; int numberOfTurns = 0;
// Allow the player to keep going until they have hit all four ships while (hits < 4) {
int row, column;
cout << "Selecting coordinates\n";
// Ask the player for a row
cout << "Choose a row number between 0 and 3: ";
cin >> row;
// Ask the player for a column
cout << "Choose a column number between 0 and 3: ";
cin >> column;
// Check if a ship exists in those coordinates
if (ships[row][column]) {
// If the player hit a ship, remove it by setting the value to zero.
ships[row][column] = 0;
// Increase the hit counter
hits++;
// Tell the player that they have hit a ship and how many ships are
//left
cout << "Hit! " << (4-hits) << " left.\n\n";
} else {
// Tell the player that they missed
cout << "Miss\n\n";
}
// Count how many turns the player has taken
numberOfTurns++; }
cout << "Victory!\n"; cout << "You won in " << numberOfTurns << " turns"; C++ Structures (struct)
A structure (struct) is a user-defined data type that allows you to group variables of different data types under a single name. Unlike arrays which store elements of the same type, structs enable you to package heterogeneous data into a single entity.
Each variable in the structure is known as a member of the structure. Unlike an array, a structure can contain many different data types: int, string, bool, etc.
Create a Structure
To create a structure, use the struct keyword and declare each of its members inside curly braces. After the declaration, specify the name of the structure variable (myStructure in the example below):
struct {
// Structure declaration
int myNum;
// Member (int variable)
string myString;
// Member (string variable) } myStructure;
// Structure variable
Access Structure Members
To access members of a structure, use the dot syntax (.):
// Create a structure variable called myStructure struct {
int myNum;
string myString; } myStructure;
// Assign values to members of myStructure myStructure.myNum = 1; myStructure.myString = "Hello World!";
// Print members of myStructure cout << myStructure.myNum << "\n"; cout << myStructure.myString << "\n"; One Structure in Multiple Variables
You can use a comma (,) to use one structure in many variables:
struct {
int myNum;
string myString; } myStruct1, myStruct2, myStruct3;
// Multiple structure variables separated with commas
This example shows how to use a structure in two different variables: struct {
string brand;
string model;
int year; } myCar1, myCar2;
// We can add variables by separating them with a comma here
// Put data into the first structure myCar1.brand = "BMW"; myCar1.model = "X5"; myCar1.year = 1999;
// Put data into the second structure myCar2.brand = "Ford"; myCar2.model = "Mustang"; myCar2.year = 1969;
// Print the structure members cout << myCar1.brand << " " << myCar1.model << " " << myCar1.year << "\n";
cout << myCar2.brand << " " << myCar2.model << " " << myCar2.year << "\n";
Named Structures
By giving a name to the structure, you can treat it as a data type. This means that you can create variables with this structure anywhere in the program at any time. To create a named structure, put the name of the structure right after the struct keyword:
struct car {
// This structure is now named "car"
string brand;
string model;
int year; };
To declare a variable that uses the structure, use the name of the structure as the data type of the variable:
car myCar1;
Now the structure can be reused anywhere by using car as the data type:
// Declare a structure named "car" struct car {
string brand;
string model;
int year; };
int main() {
// Create a car structure and store it in myCar1;
car myCar1;
myCar1.brand = "BMW";
myCar1.model = "X5";
myCar1.year = 1999;
// Create another car structure and store it in myCar2;
car myCar2;
myCar2.brand = "Ford";
myCar2.model = "Mustang";
myCar2.year = 1969;
// Print the structure members
cout << myCar1.brand << " " << myCar1.model << " " << myCar1.year << "\n";
cout << myCar2.brand << " " << myCar2.model << " " << myCar2.year
<< "\n";
return 0; }
C++ Enumeration (enum)
An enum is user-defined data type that groups together a set of named integer constants.
They replace obscure "magic numbers" with descriptive names to improve code readability and maintainability.
C++ supports two types of enums: Unscoped Enums (traditional C-style) and Scoped Enums (introduced in C++11).
Unscoped Enums
Unscoped enums export their constant names directly into the surrounding scope. This means two different unscoped enums cannot share the same constant names. They also implicitly convert to plain integers.
To create an enum, use the enum keyword, followed by the name of the enum, and separate the enum items with a comma:
enum Level {
LOW,
MEDIUM,
HIGH };
int main() {
Level myVar = MEDIUM;
// Implicitly converts to its underlying integer value (1)
cout << "Level is: " << myVar << endl;
return 0; }
To access the enum, you must create a variable of it. Inside the main() method, specify the enum keyword, followed by the name of the enum (Level) and then the name of the enum variable (myVar in this example): enum Level myVar;
Now that you have created an enum variable (myVar), you can assign a value to it. The assigned value must be one of the items inside the enum (LOW, MEDIUM or HIGH):
enum Level myVar = MEDIUM;
By default, the first item (LOW) has the value 0, the second (MEDIUM) has the value 1, etc. If you now try to print myVar, it will output 1, which represents MEDIUM:
int main() {
// Create an enum variable and assign a value to it
enum Level myVar = MEDIUM;
// Print the enum variable
cout << myVar;
return 0; }
Customizing Integer Values
By default, the compiler assigns 0 to the first item and increments by 1 for each subsequent item. You can easily override these values manually. Unassigned constants automatically increment from the previous explicit value.
enum Level {
LOW = 25,
MEDIUM = 50,
HIGH = 75 };
int main() {
enum Level myVar = MEDIUM;
cout << myVar;
// Now outputs 50
return 0; }
Note that if you assign a value to one specific item, the next items will update their numbers accordingly:
enum Level {
LOW = 5,
MEDIUM,
// Now 6
HIGH
// Now 7 };
Enum in a Switch Statement
Enums are often used in switch statements to check for corresponding values:
enum Level {
LOW = 1,
MEDIUM,
HIGH };
int main() {
enum Level myVar = MEDIUM;
switch (myVar) {
case 1:
cout << "Low Level";
break;
case 2:
cout << "Medium level";
break;
case 3:
cout << "High level";
break;
}
return 0; }
C++ References
A reference variable is an alias for an existing variable. It is created using the & operator:
If you change the value of a reference variable, the original variable will also change (and vice versa), because they both refer to the same memory location:
The & operator was used to create a reference variable. But it can also be used to get the memory address of a variable; which is the location of where the variable is stored on the computer.
When a variable is created in C++, a memory address is assigned to the variable. And when we assign a value to the variable, it is stored in this memory address. To access it, use the & operator, and the result will represent where the variable is stored:
You learned from the previous chapter, that we can get the memory address of a variable by using the & operator:
string food = "Pizza";
// A food variable of type string
cout << food;
// Outputs the value of food (Pizza) cout << &food;
// Outputs the memory address of food (0x6dfed4)
🔥EXAM TOPIC: Pointers and pass/call by reference (5 Marks).
A pointer however, is a variable that stores the memory address as its value. A pointer variable points to a data type (like int or string) of the same type, and is created with the * operator. The address of the variable you're working with is assigned to the pointer:
string food = "Pizza";
// A food variable of type string string* ptr = &food;
// A pointer variable, with the name ptr, that stores the address of food
// Output the value of food (Pizza) cout << food << "\n";
// Output the memory address of food (0x6dfed4) cout << &food << "\n";
// Output the memory address of food with the pointer (0x6dfed4) cout << ptr << "\n";
There are three ways to declare pointer variables, but the first way is preferred:
string* mystring;
// Preferred string *mystring; string * mystring;
C++ Dereference
We used the pointer variable to get the memory address of a variable (used together with the & reference operator). However, you can also use the pointer to get the value of the variable, by using the * operator (the dereference operator):
string food = "Pizza";
// Variable declaration string* ptr = &food;
// Pointer declaration
// Reference: Output the memory address of food with the pointer (0x6dfed4) cout << ptr << "\n";
// Dereference: Output the value of food with the pointer (Pizza) cout << *ptr << "\n";
The * sign can be confusing here, as it does two different things in our code:
When used in declaration (string* ptr), it creates a pointer variable.
When not used in declaration, it acts as a dereference operator. Modify the Pointer Value
You can also change the pointer's value. But note that this will also change the value of the original variable:
string food = "Pizza"; string* ptr = &food;
// Output the value of food (Pizza) cout << food << "\n";
// Output the memory address of food (0x6dfed4) cout << &food << "\n";
// Access the memory address of food and output its value (Pizza) cout << *ptr << "\n";
// Change the value of the pointer *ptr = "Hamburger";
// Output the new value of the pointer (Hamburger) cout << *ptr << "\n";
// Output the new value of the food variable (Hamburger) cout << food << "\n";
Here is another example:
int main() {
int value = 42;
// 1. Regular pointer pointing to 'value'
int* ptr1 = &value;
// 2. Double pointer pointing to 'ptr1'
int** ptr2 = &ptr1;
cout << "Direct value: " << value << "\n";
// Dereferencing once (*) gives the address stored in ptr1
cout << "Value via ptr1: " << *ptr1 << "\n";
// Dereferencing twice (**) travels through ptr1 to get 'value'
cout << "Value via ptr2: " << **ptr2 << "\n";
// Modifying the value using the double pointer
**ptr2 = 99;
cout << "New direct value: " << value << "\n";
return 0; }
Memory in C++
Memory management is the process of controlling how much memory your program uses - and how it is used. This includes creating, using, and releasing memory when it's no longer needed.
When you create a variable in C++, the compiler automatically reserves space in memory for it. For example: int myNumber = 10;
The line above tells the program: "I need space to store an integer." C++ handles this memory automatically, so in this case, you don’t have to worry about managing memory.
You can check how much memory a variable type uses with the sizeof operator:
For normal variables (like int x = 10;), C++ takes care of the memory for you. But when you want to create memory manually while the program is running (for example: based on user input), you need to manage it yourself and clean it up when you're done.
In C++, you can use pointers to access and change memory directly.
The new Keyword
The new keyword lets you manage memory yourself. In the example below, we create memory space for an integer using new, store the value 35 in it, and print it using a pointer: int* ptr = new int; *ptr = 35; cout << *ptr; The delete Keyword
When you create something with new, it's your job to remove it when you're done. To do that, use delete: delete ptr; Using new and delete with Arrays
You can also use the new keyword to create dynamic arrays. Dynamic arrays are useful when you don't know the size of the array in advance - like when the size depends on user input or other values that are not known at the start of the program.
For example, imagine you run a hotel. Since you don't know how many guests will arrive, you ask the user for the number and create that many rooms - one to store each guest's name:
#include <iostream>
#include <string> using namespace std;
int main() {
int numGuests;
cout << "How many guests? ";
cin >> numGuests;
// Check for invalid input
if (numGuests <= 0) {
cout << "Number of guests must be at least 1.\n";
return 0;
}
// Create memory space for x guests (an array of strings)
string* guests = new string[numGuests];
// Ignore the leftover newline character after reading numGuests
cin.ignore();
// Enter guest names
for (int i = 0; i < numGuests; i++) {
cout << "Enter name for guest " << (i + 1) << ": ";
getline(cin, guests[i]);
// Read the full name (including spaces)
}
// Show all guests
cout << "\nGuests checked in:\n";
for (int i = 0; i < numGuests; i++) {
cout << guests[i] << "\n";
}
delete[] guests;
// Clean up memory
return 0; }
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
What is an array? Write a program to find the sum of all elements in a 1D array.
Differentiate between structures and arrays.
Explain pointer arithmetic. Write a program to access array elements using a pointer.
What are references? How do they differ from pointers?
Unit 5: Functions, Recursion and Lambda
18 pages from PDFComplete content
🔥EXAM TOPIC: Functions, parameters, and recursion (8 Marks). Very high chance of a recursion question.
Unit-5 Functions, Recursion and Lambda
C++ Functions
A function is a block of code which only runs when it is called. You can pass data, known as parameters, into a function. Functions are used to perform certain actions, and they are important for reusing code: Define the code once, and use it many times.
Create a Function
C++ provides some pre-defined functions, such as main(), which is used to execute code. But you can also create your own functions to perform certain actions. To create (often referred to as declare) a function, specify the name of the function, followed by parentheses ():
void myFunction() {
// code to be executed }
Call a Function
Declared functions are not executed immediately. They are "saved for later use", and will be executed later, when they are called. To call a function, write the function's name followed by two parentheses () and a semicolon ;
In the following example, myFunction() is used to print a text (the action), when it is called:
// Create a function void myFunction() {
cout << "I just got executed!"; }
int main() {
myFunction();
// call the function
return 0; }
// Outputs "I just got executed!"
A function can be called multiple times:
void myFunction() {
cout << "I just got executed!\n"; }
int main() {
myFunction();
myFunction();
myFunction();
return 0; }
// I just got executed!
// I just got executed!
// I just got executed!
Function Declaration and Definition
A C++ function consist of two parts:
Declaration: the return type, the name of the function, and parameters (if any)
Definition: the body of the function (code to be executed)
If a user-defined function, such as myFunction() is declared after the main() function, an error will occur:
int main() {
myFunction();
return 0; }
void myFunction() {
cout << "I just got executed!"; }
// Error
However, it is possible to separate the declaration and the definition of the function - for code optimization. You will often see C++ programs that have function declaration above main(), and function definition below main(). This will make the code better organized and easier to read:
// Function declaration void myFunction();
// The main method int main() {
myFunction();
// call the function
return 0; }
// Function definition void myFunction() {
cout << "I just got executed!"; }
Parameters and Arguments
Information can be passed to functions as a parameter. Parameters act as variables inside the function.
Parameters are specified after the function name, inside the parentheses. You can add as many parameters as you want, just separate them with a comma:
void functionName(parameter1, parameter2, parameter3) {
// code to be executed }
🔥EXAM TOPIC: String and c-string functions (4 Marks).
The following example has a function that takes a string called fname as parameter. When the function is called, we pass along a first name, which is used inside the function to print the full name:
When a parameter is passed to the function, it is called an argument. So, from the example above: fname is a parameter, while Liam, Jenny and Anja are arguments.
Default Parameter Value
You can also use a default parameter value, by using the equals sign (=). If we call the function without an argument, it uses the default value ("Norway"):
void myFunction(string country = "Norway") {
cout << country << "\n"; }
int main() {
myFunction("Sweden");
myFunction("India");
myFunction();
myFunction("USA");
return 0; }
// Sweden
// India
// Norway
// USA
A parameter with a default value, is often known as an "optional parameter". From the example above, country is an optional parameter and "Norway" is the default value.
Multiple Parameters
Inside the function, you can add as many parameters as you want:
void myFunction(string fname, int age) {
cout << fname << " Refsnes. " << age << " years old. \n"; }
int main() {
myFunction("Liam", 3);
myFunction("Jenny", 14);
myFunction("Anja", 30);
return 0; }
// Liam Refsnes. 3 years old.
// Jenny Refsnes. 14 years old.
// Anja Refsnes. 30 years old.
When you are working with multiple parameters, the function call must have the same number of arguments as there are parameters, and the arguments must be passed in the same order.
Return Values
🔥EXAM TOPIC: Concept of data types and conversion/casting (4 Marks). Expect questions on implicit vs explicit casting.
The void keyword, used in the previous examples, indicates that the function should not return a value. If you want the function to return a value, you can use a data type (such as int, string, etc.) instead of void, and use the return keyword inside the function:
This example returns the sum of a function with two parameters:
int myFunction(int x, int y) {
return x + y; }
int main() {
cout << myFunction(5, 3);
return 0; }
// Outputs 8 (5 + 3)
You can also store the result in a variable:
int myFunction(int x, int y) {
return x + y; }
int main() {
int z = myFunction(5, 3);
cout << z;
return 0; }
// Outputs 8 (5 + 3)
Here is a simple and fun "game example" using a function with return to double a number five times:
int doubleGame(int x) {
return x * 2; }
int main() {
for (int i = 1; i <= 5; i++) {
cout << "Double of " << i << " is " << doubleGame(i) << endl;
}
return 0; }
Pass By Reference
In the examples from the previous page, we used normal variables when we passed parameters to a function. You can also pass a reference to the function. This can be useful when you need to change the value of the argument(s):
Pass an integer by reference:
void changeValue(int &num) {
num = 50; }
int main() {
int value = 10;
changeValue(value);
// Call the function and change the value to 50
cout << value;
return 0; }
Pass two integers by reference:
void swapNums(int &x, int &y) {
int z = x;
x = y;
y = z; }
int main() {
int firstNum = 10;
int secondNum = 20;
cout << "Before swap: " << "\n";
cout << firstNum << secondNum << "\n";
// Call the function,
//which will change the values of firstNum and secondNum
swapNums(firstNum, secondNum);
cout << "After swap: " << "\n";
cout << firstNum << secondNum << "\n";
return 0; }
Pass a string by reference:
void modifyStr(string &str) {
str += " World!"; }
int main() {
string greeting = "Hello";
modifyStr(greeting);
cout << greeting;
return 0; }
Pass Arrays as Function Parameters
🔥EXAM TOPIC: Structure, arrays and vectors (6 Marks).
You can also pass arrays to a function:
void myFunction(int myNumbers[5]) {
for (int i = 0; i < 5; i++) {
cout << myNumbers[i] << "\n";
}
}
int main() {
int myNumbers[5] = {10, 20, 30, 40, 50};
myFunction(myNumbers);
return 0; }
when you call the function, you only need to use the name of the array when passing it as an argument myFunction(myNumbers). However, the full declaration of the array is needed in the function parameter (int myNumbers[5]).
Pass Structure to a Function
You can also pass a structure to a function. This is useful when you want to work with grouped data inside a function:
struct Car {
string brand;
int year; };
void myFunction(Car c) {
cout << "Brand: " << c.brand << ", Year: " << c.year << "\n"; }
int main() {
Car myCar = {"Toyota", 2020};
myFunction(myCar);
return 0; }
Since the structure is passed by value, the function gets a copy of the structure. This means that the original data is not changed.
🔥EXAM TOPIC: Pointers and pass/call by reference (5 Marks).
Pass by Reference
You can also pass a structure by reference, using &. This allows the function to modify the original data:
struct Car {
string brand;
int year; };
void updateYear(Car &c) {
c.year++; }
int main() {
Car myCar = {"Toyota", 2020};
updateYear(myCar);
cout << "The " << myCar.brand << " is now from year " << myCar.year << ".\n";
return 0; }
Use reference if you want the function to change the structure's data, or to avoid copying large structures.
Let's create a program that converts a value from fahrenheit to celsius:
// Function to convert Fahrenheit to Celsius float toCelsius(float fahrenheit) {
return (5.0 / 9.0) * (fahrenheit - 32.0); }
int main() {
// Set a fahrenheit value
float f_value = 98.8;
// Call the function with the fahrenheit value
float result = toCelsius(f_value);
// Print the fahrenheit value
cout << "Fahrenheit: " << f_value << "\n";
// Print the result
cout << "Convert Fahrenheit to Celsius: " << result << "\n";
return 0; }
Function Overloading
🔥EXAM TOPIC: Polymorphism (compile time / run time) (5 Marks).
Function overloading allows multiple functions to have the same name, as long as their parameters are different in type or number. This lets you use the same function name for similar tasks.
int myFunction(int x) float myFunction(float x) double myFunction(double x, double y)
Without Function Overloading
Consider the following example, which have two functions that add numbers of different type. We had to create two different function names for the same logic.
int plusFuncInt(int x, int y) {
return x + y; }
double plusFuncDouble(double x, double y) {
return x + y; }
int main() {
int myNum1 = plusFuncInt(8, 5);
double myNum2 = plusFuncDouble(4.3, 6.26);
cout << "Int: " << myNum1 << "\n";
cout << "Double: " << myNum2;
return 0; }
Using Function Overloading
Instead of defining two functions that should do the same thing, it is better to overload one. In the example below, we overload the plusFunc function to work for both int and double: int plusFunc(int x, int y) {
Multiple functions can have the same name as long as the number and/or type of parameters are different.
Function Overloading by Number of Parameters
In this example, we overload a function by using a different number of parameters:
int plusFunc(int x, int y) {
return x + y; }
int plusFunc(int x, int y, int z) {
return x + y + z; }
int main() {
int result1 = plusFunc(3, 7);
int result2 = plusFunc(1, 2, 3);
cout << "Sum of 2 numbers: " << result1 << "\n";
cout << "Sum of 3 numbers: " << result2;
return 0; }
Variable Scope
Now that you understand how functions work, it is important to learn how variables act inside and outside of functions. In C++, variables are only accessible inside the region they are created. This is called scope.
Local Scope
A variable created inside a function belongs to the local scope of that function, and can only be used inside that function:
void myFunction() {
// Local variable that belongs to myFunction
int x = 5;
// Print the variable x
cout << x; }
int main() {
myFunction();
return 0; }
A local variable cannot be used outside the function it belongs to. If you try to access it outside the function, an error occurs:
void myFunction() {
// Local variable that belongs to myFunction
int x = 5; }
int main() {
myFunction();
// Print the variable x in the main function
cout << x;
return 0;
Global Scope
A variable created outside of a function, is called a global variable and belongs to the global scope. Global variables are available from within any scope, global and local:
// Global variable x int x = 5;
void myFunction() {
// We can use x here
cout << x << "\n"; }
int main() {
myFunction();
// We can also use x here
cout << x;
return 0; } Naming Variables
If you operate with the same variable name inside and outside of a function, C++ will treat them as two separate variables; One available in the global scope (outside the function) and one available in the local scope (inside the function):
For example: The function will print the local x, and then the code will print the global x:
// Global variable x int x = 5;
void myFunction() {
// Local variable with the same name as the global variable (x)
int x = 22;
cout << x << "\n";
// Refers to the local variable x }
int main() {
myFunction();
cout << x;
// Refers to the global variable x
return 0; }
However, you should avoid using the same variable name for both globally and locally variables as it can lead to errors and confusion. In general, you should be careful with global variables, since they can be accessed and modified from any function:
// Global variable x int x = 5;
void myFunction() {
cout << ++x << "\n";
// Increment the value of x by 1 and print it }
int main() {
myFunction();
cout << x;
// Print the global variable x
return 0; }
// The value of x is now 6 (no longer 5)
Use local variables (with good variable names) as much as you can. This will make your code easier to maintain and better to understand. However, you may find global variables when working on existing C++ programs or while collaborating with others. Therefore, it is good to understand how the scope works and how to use it effectively to make sure your code is clear and functional.
Recursion
🔥EXAM TOPIC: Loops and break/continue statements (4 Marks). Focus on nested loops.
Recursion is the technique of making a function call itself. This technique provides a way to break complicated problems down into simple problems which are easier to solve.
In the following example, recursion is used to add a range of numbers together by breaking it down into the simple task of adding two numbers: int sum(int k) {
if (k > 0) {
return k + sum(k - 1);
} else {
return 0;
} }
int main() {
int result = sum(10);
cout << result;
return 0; }
When the sum() function is called, it adds parameter k to the sum of all numbers smaller than k and returns the result. When k becomes 0, the function just returns 0. When running, the program follows these steps:
Since the function does not call itself when k is 0, the program stops there and returns the result.
This example demonstrates how to use recursion to create a countdown function:
void countdown(int n) {
if (n > 0) {
cout << n << " ";
countdown(n - 1);
} }
int main() {
countdown(5); }
The function calls itself with n - 1 until n becomes 0.
This example uses a recursive function to calculate the factorial of 5:
int factorial(int n) {
if (n > 1) {
return n * factorial(n - 1);
} else {
return 1;
} }
int main() {
cout << "Factorial of 5 is " << factorial(5);
return 0; }
Factorial means multiplying a number by every number below it, down to 1 (for example, the factorial of 5 is: 5 * 4 * 3 * 2 * 1 = 120).
Lambda Functions
A lambda function is a small, anonymous function you can write directly in your code. It's useful when you need a quick function without naming it or declaring it separately. Think of it as a "mini function on the fly."
Syntax:
[capture] (parameters) { code };
Here, message holds a lambda function that prints a message to the screen:
int main() {
auto message = []() {
cout << "Hello World!\n";
};
message();
return 0; }
//Hello World!
Lambda with Parameters
You can pass values into a lambda just like a regular function:
#include <iostream> using namespace std;
int main() {
auto add = [](int a, int b) {
return a + b;
};
cout << add(3, 4);
return 0; }
//7
Passing Lambdas to Functions
You can also pass a lambda function as an argument to another function. This is useful when you want to tell a function what to do, not just what data to use.
In the example below, we send a small lambda function to another function, which then runs it twice:
#include <iostream>
#include <functional>
// Needed for std::function using namespace std;
// A function that takes another function as parameter void myFunction(function<void()> func) {
func();
func(); }
int main() {
auto message = []() {
cout << "Hello World!\n";
};
myFunction(message);
return 0; }
//Hello World! //Hello World!
You must include the <functional> library for this example to work.
Using Lambdas in Loops
You can define and use a lambda function inside a loop, which are great for quick actions:
#include <iostream> using namespace std;
int main() {
for (int i = 1; i <= 3; i++) {
auto show = [i]() {
cout << "Number: " << i << "\n";
};
show();
}
return 0; }
//Number: 1 //Number: 2 //Number: 3
Capture Clause []
You can use the [ ] brackets to give a lambda access to variables outside of it. This is called the capture clause.
In this example, the lambda captures the variable x by value (a copy): int main() {
int x = 10;
auto show = [x]() {
cout << x;
};
show();
return 0; }
//10
The lambda uses a copy of x. If you change x after defining the lambda, it won't affect the value inside the lambda. You can also use [&] to capture by reference. Capture by Reference
If you want the lambda to use the latest value of a variable (not just a copy), you can use [&] to capture it by reference. This means the lambda will work with the original variable, not a separate copy: int main() {
int x = 10;
auto show = [&x]() {
cout << x;
};
x = 20;
// Change x after the lambda is created
show();
return 0; }
//20
The lambda sees the original x variable, so when you change x, the lambda uses the updated value.
Regular Functions Vs Lambda Functions
Both regular functions and lambda functions let you group code and run it later, but they are used in slightly different situations.
Use a regular function when:
You plan to reuse the function in multiple places
You want to give the function a clear, meaningful name
The logic is long or complex
Use a lambda function when:
You only need the function once
The code is short and simple
You want to pass a quick function into another function
Both of these examples do the same thing. They return the sum of two numbers:
//Regular Function int add(int a, int b) {
return a + b; }
//Lambda Function auto add = [](int a, int b) {
return a + b; };
The lambda version is great when you don't need to reuse the function later. It's quick and works well inside blocks or as arguments to other functions.
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
What is recursion? Write a recursive function to find the factorial of a number.
Differentiate between pass by value and pass by reference with a code example.
What are global and local variables? Explain their scope.
Write a program to demonstrate function overloading with different parameters.
Unit 6: Sorting and Searching Algorithms
17 pages from PDFComplete content
🔥EXAM TOPIC: Sorting and Searching algorithms (10 Marks). Focus on Bubble/Insertion/Selection combined with Binary Search.
Unit-6 Sorting and Searching Algorithms
Bubble Sort Algorithm
🔥EXAM TOPIC: Structure, arrays and vectors (6 Marks).
Bubble sort is a sorting algorithm that compares two adjacent elements and swaps them repeatedly until they are in the intended order. Each element of an array is moved to the end in each iteration. it makes the highest values 'bubble up'. Therefore, it is called a bubble sort.
Steps in Bubble Sort Algorithm for sorting the elements in ascending order:
First Iteration (Compare and Swap) 1. Starting from the first index, compare the first and the second elements. 2. If the first element is greater than the second element, they are swapped. 3. Now, compare the second and the third elements. Swap them if they are not in order. 4. The comparison goes on until the last element is set as a greatest of all.
Remaining Iteration 5. The same process goes on for the remaining iterations. 6. After each iteration, the largest element among the unsorted elements is placed at the end.
7. In each iteration, the comparison takes place up to the last unsorted element.
8. The array is sorted when all the unsorted elements are placed at their correct positions.
Bubble Sort Algorithm
bubbleSort(array)
for i <- 1 to sizeOfArray - 1
for j <- 1 to sizeOfArray - 1 - i
if leftElement > rightElement
swap leftElement and rightElement end bubbleSort
Bubble Sort Program Code
// Bubble sort in C++
#include <iostream> using namespace std;
// perform bubble sort void bubbleSort(int array[], int size) {
// loop to access each array element
for (int step = 0; step < size -1; ++step) {
// loop to compare array elements
for (int i = 0; i < size - step - 1; ++i) {
// compare two adjacent elements
// change > to < to sort in descending order
if (array[i] > array[i + 1]) {
// swapping elements if elements
// are not in the intended order
int temp = array[i];
array[i] = array[i + 1];
array[i + 1] = temp;
}
}
} }
// print array void printArray(int array[], int size) {
for (int i = 0; i < size; ++i) {
cout << "
" << array[i];
}
cout << "\n";
}
int main() {
int data[] = {-2, 45, 0, 11, -9};
// find array's length
int size = sizeof(data) / sizeof(data[0]);
bubbleSort(data, size);
cout << "Sorted Array in Ascending Order:\n";
printArray(data, size);
}
Optimized Bubble Sort Algorithm
In the above algorithm, all the comparisons are made even if the array is already sorted. This increases the execution time. To solve this, we can introduce an extra variable swapped. The value of swapped is set true if there occurs swapping of elements. Otherwise, it is set false.
After an iteration, if there is no swapping, the value of swapped will be false. This means elements are already sorted and there is no need to perform further iterations. This will reduce the execution time and helps to optimize the bubble sort.
Algorithm for optimized bubble sort is
bubbleSort(array)
for i <- 1 to sizeOfArray - 1
swapped <- false
for j <- 1 to sizeOfArray - 1 - i
if leftElement > rightElement
swap leftElement and rightElement
swapped <- true
if swapped == false
🔥EXAM TOPIC: Loops and break/continue statements (4 Marks). Focus on nested loops.
break end bubbleSort
Optimized Bubble Sort Program Code
// Optimized bubble sort in C++
#include <iostream> using namespace std;
// perform bubble sort void bubbleSort(int array[], int size) {
// loop to access each array element
for (int step = 0; step < (size-1); ++step) {
// check if swapping occurs
int swapped = 0;
// loop to compare two elements
for (int i = 0; i < (size-step-1); ++i) {
// compare two array elements
// change > to < to sort in descending order
if (array[i] > array[i + 1]) {
// swapping occurs if elements
// are not in intended order
int temp = array[i];
array[i] = array[i + 1];
array[i + 1] = temp;
swapped = 1;
}
}
// no swapping means the array is already sorted
// so no need of further comparison
if (swapped == 0)
break;
} }
// print an array void printArray(int array[], int size) {
for (int i = 0; i < size; ++i) {
cout << "
" << array[i];
}
cout << "\n"; }
int main() {
int data[] = {-2, 45, 0, 11, -9};
// find the array's length
int size = sizeof(data) / sizeof(data[0]);
bubbleSort(data, size);
cout << "Sorted Array in Ascending Order:\n";
printArray(data, size); }
Bubble Sort Complexity
Bubble Sort compares the adjacent elements. Cycle Number of Comparisons 1st (n-1) 2nd (n-2) 3rd (n-3) ....... ...... last 1
Hence, the number of comparisons is
(n-1) + (n-2) + (n-3) +.....+ 1 = n(n-1)/2
nearly equals to n2
Hence, Complexity: O(n2)
Selection Sort Algorithm
Selection sort is a sorting algorithm that selects the smallest element from an unsorted list in each iteration and places that element at the beginning of the unsorted list.
Steps in Selection Sort Algorithm for sorting the elements in ascending order: 1. Set the first element as minimum.
2. Compare minimum with the second element. If the second element is smaller than minimum, assign the second element as minimum. 3. Compare minimum with the third element. Again, if the third element is smaller, then assign minimum to the third element otherwise do nothing. The process goes on until the last element.
4. After each iteration, minimum is placed in the front of the unsorted list.
5. For each iteration, indexing starts from the first unsorted element. Step 1 to 4 are repeated until all the elements are placed at their correct positions.
Selection Sort Algorithm
selectionSort(array, size)
for i from 0 to size - 1 do
set i as the index of the current minimum
for j from i + 1 to size - 1 do
if array[j] < array[current minimum]
set j as the new current minimum index
if current minimum is not i
swap array[i] with array[current minimum] end selectionSort
Selection Sort Program Code
// Selection sort in C++
#include <iostream> using namespace std;
// function to swap the the position of two elements void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp; }
// function to print an array void printArray(int array[], int size) {
for (int i = 0; i < size; i++) {
cout << array[i] << " ";
}
cout << endl; }
void selectionSort(int array[], int size) {
for (int step = 0; step < size - 1; step++) {
int min_idx = step;
for (int i = step + 1; i < size; i++) {
// To sort in descending order, change > to < in this line.
// Select the minimum element in each loop.
if (array[i] < array[min_idx])
min_idx = i;
}
// put min at the correct position
swap(&array[min_idx], &array[step]);
} }
// driver code int main() {
int data[] = {20, 12, 10, 15, 2};
int size = sizeof(data) / sizeof(data[0]);
selectionSort(data, size);
cout << "Sorted array in Acsending Order:\n";
printArray(data, size); }
Selection Sort Complexity
Cycle Number of Comparisons 1st (n-1) 2nd (n-2) 3rd (n-3) ....... ...... last 1
The number of comparisons:
(n-1) + (n-2) + (n-3) +.....+ 1 = n(n-1)/2
nearly equals to n2
Hence, Complexity: O(n2)
Insertion Sort Algorithm
Insertion sort is a sorting algorithm that places an unsorted element at its suitable place in each iteration.
Insertion sort works similarly as we sort cards in our hand in a card game. We assume that the first card is already sorted then, we select an unsorted card. If the unsorted card is greater than the card in hand, it is placed on the right otherwise, to the left. In the same way, other unsorted cards are taken and put in their right place.
Steps in Insertion Sort Algorithm for sorting the elements in ascending order:
1. The first element in the array is assumed to be sorted. Take the second element and store it separately in key. 2. Compare key with the first element. If the first element is greater than key, then key is placed in front of the first element.
3. Now, the first two elements are sorted. Take the third element and compare it with the elements on the left of it. Placed it just behind the element smaller than it. If there is no element smaller than it, then place it at the beginning of the array.
4. Similarly, place every unsorted element at its correct position.
Insertion Sort Algorithm
insertionSort(array)
mark first element as sorted
for each unsorted element X
'extract' the element X
for j <- lastSortedIndex down to 0
if current element j > X
move sorted element to the right by 1
break loop and insert X here end insertionSort
Insertion Sort Program Code
// Insertion sort in C++
#include <iostream> using namespace std;
// Function to print an array void printArray(int array[], int size) {
for (int i = 0; i < size; i++) {
cout << array[i] << " ";
}
cout << endl; }
void insertionSort(int array[], int size) {
for (int step = 1; step < size; step++) {
int key = array[step];
int j = step - 1;
// Compare key with each element on the left of it until an element smaller than
// it is found.
// For descending order, change key<array[j] to key>array[j].
while (j >=0 && key < array[j]) {
array[j + 1] = array[j];
--j;
}
array[j + 1] = key;
} }
// Driver code int main() {
int data[] = {9, 5, 1, 4, 3};
int size = sizeof(data) / sizeof(data[0]);
insertionSort(data, size);
cout << "Sorted array in ascending order:\n";
printArray(data, size); }
Insertion Sort Complexity
Suppose, an array is in ascending order, and you want to sort it in descending order. In this case, worst case complexity occurs.
Each element has to be compared with each of the other elements so, for every nth element, (n-1) number of comparisons are made.
Thus, the total number of comparisons = n*(n-1) ~ n2
nearly equals to n2
Hence, Complexity: O(n2)
Binary Search
Binary Search is a searching algorithm for finding an element's position in a sorted array. In this approach, the element is always searched in the middle of a portion of an array.
Binary search can be implemented only on a sorted list of items. If the elements are not sorted already, we need to sort them first.
Binary Search Algorithm can be implemented in two ways: 1. Iterative Method 2. Recursive Method
The general steps for both methods are: 1. The array in which searching is to be performed is:
🔥EXAM TOPIC: Pointers and pass/call by reference (5 Marks).
2. Let x = 4 be the element to be searched. 3. Set two pointers low and high at the lowest and the highest positions respectively.
4. Find the middle position mid of the array ie. mid = (low + high)/2 and arr[mid] = 6.
5. If x == arr[mid], then return mid. Else, compare the element to be searched with arr[mid]. 6. If x > arr[mid], compare x with the middle element of the elements on the right side of arr[mid]. This is done by setting low to low = mid + 1. 7. Else, compare x with the middle element of the elements on the left side of arr[mid]. This is done by setting high to high = mid – 1.
8. Repeat steps 4 to 7 until low meets high.
9. x = 4 is found.
Binary Search Algorithm 1. Iteration Method
do until the pointers low and high meet each other.
mid = (low + high)/2
if (x == arr[mid])
return mid
else if (x > arr[mid])
// x is on the right side
low = mid + 1
else
// x is on the left side
high = mid - 1
2. Recursive Method
binarySearch(arr, x, low, high)
if low > high
return False
else
mid = (low + high) / 2
if x == arr[mid]
return mid
else if x > arr[mid]
// x is on the right side
return binarySearch(arr, x, mid + 1, high)
else
// x is on the left side
return binarySearch(arr, x, low, mid - 1)
Binary Search Program Code: 1. Iteration Method
// Binary Search in C++
#include <iostream> using namespace std;
int binarySearch(int array[], int x, int low, int high) {
// Repeat until the pointers low and high meet each other
while (low <= high) {
int mid = low + (high - low) / 2;
if (x == array[mid])
return mid;
if (x > array[mid])
low = mid + 1;
else
high = mid - 1;
}
return -1; }
int main(void) {
int array[] = {3, 4, 5, 6, 7, 8, 9};
int x = 4;
int n = sizeof(array) / sizeof(array[0]);
int result = binarySearch(array, x, 0, n - 1);
if (result == -1)
printf("Not found");
else
printf("Element is found at index %d", result); }
2. Recursive Method
// Binary Search in C++
#include <iostream> using namespace std;
int binarySearch(int array[], int x, int low, int high) {
if (high >= low) {
int mid = low + (high - low) / 2;
// If found at mid, then return it
if (x == array[mid])
return mid;
// Search the right half
if (x > array[mid])
return binarySearch(array, x, mid + 1, high);
// Search the right half
return binarySearch(array, x, low, mid - 1);
}
return -1; }
int main(void) {
int array[] = {3, 4, 5, 6, 7, 8, 9};
int x = 4;
int n = sizeof(array) / sizeof(array[0]);
int result = binarySearch(array, x, 0, n - 1);
if (result == -1)
printf("Not found");
else
printf("Element is found at index %d", result); }
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
Write an algorithm and C++ code for Bubble Sort.
Write an algorithm for Binary Search. What is its prerequisite?
Compare Linear Search and Binary Search.
Trace the steps of Selection Sort on the array [64, 25, 12, 22, 11].
Unit 7: Object Oriented Programming
35 pages from PDFComplete content
🔥EXAM TOPIC: Classes, objects, constructors, and inheritance (5 Marks).
Unit-7 Object Oriented Programming
🔥EXAM TOPIC: Functions, parameters, and recursion (8 Marks). Very high chance of a recursion question.
Object-Oriented Programming (OOP) is a programming paradigm that organizes programs around classes and objects. A class defines the data members and member functions, while an object is an instance of a class.
OOP helps build modular, reusable, and maintainable software by modeling real-world entities.
Promotes code reusability through inheritance.
Improves code maintainability and scalability.
Provides data security through encapsulation.
Models real-world entities using classes and objects.
Object-Oriented Programming is characterized by several fundamental concepts that make software more modular, reusable, and easier to maintain.
Class
🔥EXAM TOPIC: Concept of data types and conversion/casting (4 Marks). Expect questions on implicit vs explicit casting.
A class in C++ is a user-defined data type that acts as a blueprint for creating objects. It defines the data members and member functions that describe the properties and behavior of the objects.
A class is a user-defined data type.
It contains data members and member functions.
Multiple objects can be created from a single class.
For Example: A Car represents a class (blueprint), while BMW, Mercedes, and Audi are objects (instances) created from that class.
Object
An object in C++ is an instance of a class that represents a real-world entity. It is used to access the data members and member functions defined in the class.
State: Represents the current values of an object's data members.
Behavior: Represents the actions an object can perform through its member functions.
Identity: Every object has a unique memory location that distinguishes it from other objects, even if they contain the same data.
For Example: Dog is a class, while Tommy is an object (instance) of that class.
Constructor
A constructor is a special member function of a class that is automatically called when an object is created. It is mainly used to initialize the object's data members and establish its initial state.
A constructor has the same name as the class.
It does not have a return type, not even void.
It is automatically invoked whenever an object of the class is created.
It helps initialize object properties during object creation.
For Example: When creating a Car object, a constructor can automatically initialize data members such as brand, model, and color.
#include <iostream> using namespace std;
class A { public:
// Constructor without any parameters
A() {
cout << "Constructor called" << endl;
} };
int main() {
A obj1;
return 0; }
Types of Constructors
C++ provides different types of constructors that are used to initialize objects in different ways.
Default Constructor: A constructor that takes no parameters and initializes an object with default values. A default constructor is automatically created by the compiler if no constructor is defined. It takes no arguments and initializes members with default values, and it is not generated if the programmer defines any constructor.
#include <iostream> using namespace std;
// Class with no explicity defined constructors class A { public: };
int main() {
// Creating object
A a;
return 0; }
Parameterized Constructor: A constructor that accepts one or more parameters to initialize an object with user-defined values. A parameterized constructor lets us pass arguments to initialize an object's members. It is created by adding parameters to the constructor and using them to set the values of the data members.
#include <iostream> using namespace std;
class A { public:
int val;
// Parameterized Constructor
A(int x) {
val = x;
} };
int main() {
// Creating object with a parameter
A a(10);
cout << a.val;
return 0; }
Copy Constructor: A constructor that creates a new object by copying the state of an existing object of the same class. A copy constructor is a member function that initializes an object using another object of the same class. Copy constructor takes a reference to an object of the same class as an argument.
#include <iostream> using namespace std;
class A { public:
int val;
// Parameterized constructor
A(int x) {
val = x;
}
// Copy constructor
A(A& a) {
val = a.val;
} };
int main() {
A a1(20);
// Creating another object from a1
A a2(a1);
cout << a2.val;
return 0; }
Move Constructor (C++11): A constructor that transfers the resources of a temporary object to a new object instead of copying them, improving performance. A move constructor in C++ transfers resources from one object to another instead of copying them. It uses move semantics to avoid unnecessary copies and improve performance, especially with temporary objects.
#include <iostream>
#include <vector> using namespace std;
class MyClass { private:
int b;
public:
// Constructor
MyClass(int &&a) : b(move(a)) {
cout << "Move constructor called!" << endl;
}
void display() {
cout << b <<endl;
} };
int main() {
int a = 4;
MyClass obj1(move(a));
// Move constructor is called
obj1.display();
return 0; }
Destructor
🔥EXAM TOPIC: File handling and Exception handling (8 Marks). Expect a question on reading non-existing files.
A Destructor is a special member function that is automatically invoked when an object is destroyed. It is primarily used to release resources such as dynamically allocated memory, open files, or network connections.
Destructor name is the same as the class name, preceded by a tilde (~).
A class can have only one destructor.
It does not accept parameters and has no return type.
It is automatically called when an object goes out of scope or is deleted.
For Example: When a File object is destroyed, its destructor can automatically close the file and release associated resources.
#include <iostream> using namespace std;
class Test { public:
// User-Defined Constructor
Test() {
cout << "Constructor Called"
<< endl;
}
// User-Defined Destructor
~Test() {
cout << "Destructor Called"
<< endl;
} }; int main() {
Test t;
return 0; }
#include <iostream> using namespace std;
class MyClass { private:
// Pointer to dynamically
// allocated memory
int* data;
public:
MyClass(int value) {
data = new int;
*data = value;
cout << *data << endl;
}
// User-defined destructor: Free
// the dynamically allocated memory
~MyClass() {
// Deallocate the dynamically
// allocated memory
delete data;
cout << "Destructor: Memory deallocated";
} };
int main() {
MyClass obj1(10);
return 0; }
#include <iostream> using namespace std;
int Count = 0; class Test { public:
Test(){
// Number of times constructor is called
Count++;
cout << "No. of Object created: "
<< Count << endl;
}
~Test() {
// It will print count in decending order
cout << "No. of Object destroyed: " << Count
<< endl;
Count--;
} };
int main() {
Test t, t1, t2, t3;
return 0;
}
Access Specifiers
Access specifiers control the visibility and accessibility of class members. They help implement encapsulation by restricting access to data and member functions.
Access modifiers in C++ are keywords that control the accessibility of class members (data members and member functions). They are an important part of encapsulation and data hiding, allowing developers to restrict access to sensitive data and expose only the required functionality.
Control the visibility of class members.
Help implement data hiding and encapsulation.
Improve security and maintainability of code.
Access modifiers specify the access level of class members and define where they can be used. C++ provides three access modifiers:
public: Members can be accessed from anywhere in the program through an object of the class.
Public members can be accessed both inside and outside the class.
Commonly used for functions and data that should be available to users of the class.
Helps expose the required functionality of a class.
#include <iostream> using namespace std;
class Student { public:
string name; };
int main() {
Student s;
// Accessing public member
s.name = "John";
cout << s.name;
return 0; }
private: Members can only be accessed within the same class. The private access specifier restricts access to class members so that they can only be accessed within the class itself.
Private members cannot be accessed directly outside the class.
Used to hide sensitive data and implementation details.
Helps achieve data hiding and encapsulation.
By default, all members of a C++ class are private.
#include <iostream> using namespace std;
class Employee { private:
// salary and empId cannot be accessed
// from outside the Class
double salary;
int empID;
public:
string name;
// Name can be accessed anywhere
Employee(string n, double s, int id) {
name = n;
salary = s;
empID = id;
} };
int main() {
Employee emp("Fedrick", 50000, 101);
cout << "Name: " << emp.name << endl;
return 0; }
protected: Members can be accessed within the class and its derived classes, while preventing direct access from outside the class.
Accessible within the same class.
Accessible in derived (child) classes through inheritance.
Cannot be accessed directly using objects of the class.
Commonly used when implementing inheritance.
#include <iostream> using namespace std;
class Employee { private:
double salary;
protected:
int empID;
public:
string name;
Employee(string n, double s, int id) {
name = n;
salary = s;
empID = id;
} };
// Derived class (inherits from Employee) class Manager : public Employee { public:
Manager(string n, double s, int id) : Employee(n, s, id) {}
void showDetails() {
cout << "Manager Name: " << name << endl;
cout << "Manager ID: " << empID << endl;
} };
int main() {
Employee emp("Fedrick", 50000, 101);
cout << "Employee Name: " << emp.name << endl;
Manager m("Rohit", 70000, 102);
m.showDetails();
return 0; }
For Example: A bank account stores its balance as a private member while allowing controlled access through public member functions such as deposit() and withdraw().
Data Hiding
Data hiding is the practice of restricting direct access to an object's data and allowing it to be accessed only through controlled member functions. In C++, it is achieved using access specifiers such as private and protected.
Prevents unauthorized access to class data.
Protects the internal state of an object from accidental modification.
Provides controlled access through public member functions (getters and setters).
For Example: A BankAccount class keeps its balance private. Users cannot modify it directly and must use functions such as deposit(), withdraw(), or getBalance()
Four Pillars of OOP in C++
Object-Oriented Programming in C++ is built on four fundamental principles that help create modular, reusable, and maintainable software.
1. Abstraction
Abstraction is the process of hiding implementation details and exposing only the essential features of an object. It allows users to interact with an object without knowing how its functionality is implemented internally.
Hides implementation details: Users access only the required functionality while the internal logic remains hidden.
Improves maintainability: Changes to the implementation do not affect the code using the abstraction.
Enhances flexibility: Achieved using abstract classes and pure virtual functions.
For Example: An ATM or a coffee machine demonstrates abstraction, where users perform operations such as withdrawing cash or selecting a beverage without knowing the internal implementation.
Abstraction in C++ is achieved using abstract classes and pure virtual functions. An abstract class defines a common interface, while derived classes provide the implementation for the abstract functions.
Abstract Class: A class containing at least one pure virtual function. It serves as a base class that defines a common interface while leaving the implementation of specific operations to derived classes. Objects of an abstract class cannot be created.
#include <iostream>
#include <string> using namespace std;
// Abstract base class as there is a
// pure virtual method class Shape{ protected:
string color;
public:
Shape(string color) : color(color){}
// Abstract or Pure virtual method
virtual double area() = 0;
// Concrete method
string getColor(){
return color;
}
virtual ~Shape() {} };
// Derived class: Rectangle class Rectangle : public Shape {
double length, width;
public:
Rectangle(string color, double length, double width) : Shape(color){
this->length = length;
this->width = width;
}
double area() override {
return length * width;
} };
int main() {
Shape* s = new Rectangle("Yellow", 2, 4);
cout<<"Rectangle color is "<<s->getColor()<<" and area is : "<<s->area()<<endl;
return 0; }
Pure Virtual Function: A class containing only pure virtual functions is commonly used as a pure abstract class to provide complete abstraction. Such classes define only the required operations without providing any implementation. A virtual function declared using = 0 that must be overridden by derived classes.
#include <iostream> using namespace std;
// Pure Abstract Class acting as an Interface class Printable { public:
virtual void print() = 0;
// pure virtual function
virtual void scan() = 0;
// pure virtual function
// Virtual destructor is a good practice for base classes
virtual ~Printable() {} };
// Derived class must implement all functions class Document : public Printable { public:
void print() override {
cout << "Printing document..." << endl;
}
void scan() override {
cout << "Scanning document..." << endl;
} };
// Another derived class implementing the same interface class Photo : public Printable { public:
void print() override {
cout << "Printing photo..." << endl;
}
void scan() override {
cout << "Scanning photo..." << endl;
} };
int main() {
// Base class pointer pointing to derived objects
Printable* p1 = new Document();
Printable* p2 = new Photo();
// Call interface functions - runtime polymorphism
p1->print();
p1->scan();
p2->print();
p2->scan();
// Free memory
delete p1;
delete p2;
return 0; }
For Example: An abstract class Shape can declare a pure virtual function draw(), while derived classes such as Circle and Rectangle provide their own
implementations of draw(). This allows users to work with the common Shape interface without knowing the implementation details of each shape.
2. Encapsulation
Encapsulation is the process of combining data members and member functions into a single unit (class) while restricting direct access to the data. It helps protect data by allowing controlled access through member functions.
Data members are typically hidden using the private access specifier.
Public member functions (getters and setters) provide controlled access to the data.
Improves data security, maintainability, and modularity.
For Example: A Bank Account hides its balance using the private access specifier. The balance can only be accessed or modified through public functions such as deposit(), withdraw(), and getBalance().
Inheritance is a fundamental OOP concept in C++ that allows a class to acquire the data members and member functions of another class. It establishes an "is-a" relationship between classes and promotes code reuse.
The class being inherited is called the base class, and the inheriting class is called the derived class.
A derived class can access the members of the base class (subject to access specifiers) and can also define its own members.
Promotes code reusability, extensibility, and hierarchical relationships.
For Example: Dog, Cat, and Cow can be derived classes of the Animal base class because each one is an Animal.
#include <iostream> using namespace std;
class Animal {
public:
void sound()
{
cout << "Animal makes a sound" << endl;
} };
class Dog : public Animal {
public:
void sound()
{
cout << "Dog barks" << endl;
} };
class Cat : public Animal {
public:
void sound()
{
cout << "Cat meows" << endl;
} };
class Cow : public Animal {
public:
void sound()
{
cout << "Cow moos" << endl;
} };
int main() {
Dog d;
d.sound();
Cat c;
c.sound();
Cow cow;
cow.sound();
return 0; }
C++ supports the following types of inheritance:
Single Inheritance: In single inheritance, a sub-class is derived from only one super class. It inherits the properties and behavior of a single-parent class. Sometimes, it is also known as simple inheritance. A derived class inherits from one base class.
#include <iostream> using namespace std;
class Vehicle { public:
Vehicle() {
cout << "This is a Vehicle" << endl;
} };
class Car : public Vehicle { public:
Car() {
cout << "This Vehicle is Car" << endl;
} };
int main() {
Car obj;
return 0; }
Multiple Inheritance: A derived class inherits from more than one base class.
#include <iostream> using namespace std;
class LandVehicle {
public:
void landInfo()
{
cout << "This is a LandVehicle" << endl;
} };
class WaterVehicle {
public:
void waterInfo()
{
cout << "This is a WaterVehicle" << endl;
} };
// Derived class inheriting from both base classes class AmphibiousVehicle : public LandVehicle, public WaterVehicle {
public:
AmphibiousVehicle()
{
cout << "This is an AmphibiousVehicle" << endl;
} };
int main() {
AmphibiousVehicle obj;
obj.waterInfo();
obj.landInfo();
return 0; }
Multilevel Inheritance: A class is derived from another derived class, forming a chain of inheritance.
#include <iostream> using namespace std;
class Vehicle {
public:
Vehicle()
{
cout << "This is a Vehicle" << endl;
}
};
// Derived class from Vehicle class FourWheeler : public Vehicle {
public:
FourWheeler()
{
cout << "4 Wheeler Vehicles" << endl;
} };
// Derived class from FourWheeler class Car : public FourWheeler {
public:
Car()
{
cout << "This 4 Wheeler Vehicle is a Car" << endl;
} };
int main() {
Car obj;
return 0; }
Hierarchical Inheritance: Multiple derived classes inherit from a single base class.
#include <iostream> using namespace std;
class Vehicle {
public:
Vehicle()
{
cout << "This is a Vehicle" << endl;
} };
class Car : public Vehicle {
public:
Car()
{
cout << "This Vehicle is Car" << endl;
} };
class Bus : public Vehicle {
public:
Bus()
{
cout << "This Vehicle is Bus" << endl;
} };
int main() {
Car obj1;
Bus obj2;
return 0; }
Hybrid Inheritance: A combination of two or more types of inheritance, such as multiple and multilevel inheritance.
#include <iostream> using namespace std;
class Vehicle {
public:
Vehicle()
{
cout << "This is a Vehicle" << endl;
} };
class Fare {
public:
Fare()
{
cout << "Fare of Vehicle" << endl;
} };
class Car : public Vehicle {
public:
Car()
{
cout << "This Vehicle is a Car" << endl;
} };
class Bus : public Vehicle, public Fare {
public:
Bus()
{
cout << "This Vehicle is a Bus with Fare";
} };
int main() {
Bus obj2;
return 0; }
🔥EXAM TOPIC: Polymorphism (compile time / run time) (5 Marks).
4. Polymorphism
Polymorphism means "many forms", where the same interface can perform different actions depending on the object or context. In C++, it allows the same function or operator to exhibit different behavior in different situations.
Allows the same interface to perform different actions.
Achieved through function overloading, operator overloading (compile-time polymorphism), and function overriding using virtual functions (run-time polymorphism).
Improves code flexibility, reusability, and extensibility.
For Example: Different animals demonstrate polymorphism, where the same speak() function produces different outputs such as Bark, Meow, and Moo depending on whether the object is of type Dog, Cat, or Cow.
Polymorphism in C++ is mainly classified into the following two types:
Compile-time polymorphism is resolved by the compiler during compilation. Compile-time polymorphism in C++ is achieved using:
Function Overloading: Multiple functions have the same name but different parameter lists. The difference can be in:
Number of parameters
Type of parameters
#include <bits/stdc++.h> using namespace std;
class Geeks { public:
// Function to add two integers
void add(int a, int b) {
cout << "Integer Sum = " << a + b
<< endl;
}
// Function to add two floating point values
void add(double a, double b) {
cout << "Float Sum = " << a + b
<< endl ;
} };
int main() {
Geeks gfg;
// add() called with int values
gfg.add(10, 2);
// add() called with double value
gfg.add(5.3, 6.2);
return 0; }
Operator Overloading: Operators are redefined to work with user-defined types. It allows operators like +, -, and * to work with user-defined data types such as classes. It lets us define how an operator should behave when used with custom objects.
#include <iostream> using namespace std;
class Complex { public:
int real, imag;
Complex(int r, int i) :
real(r), imag(i) {}
// Overloading the '+' operator
Complex operator+(const Complex& obj) {
return Complex(real + obj.real, imag + obj.imag);
} };
int main() {
Complex c1(10, 5), c2(2, 4);
// Adding c1 and c2 using + operator
Complex c3 = c1 + c2;
cout << c3.real << " + i" << c3.imag;
return 0; }
2. Run-time Polymorphism (Dynamic Polymorphism)
Runtime polymorphism is also known as dynamic polymorphism or late binding. Run-time polymorphism is resolved during program execution based on the actual type of the object, instead of compilation. Run-time polymorphism in C++ is achieved using:
Function Overriding: A derived class provides its own implementation of a virtual function defined in the base class. It occurs when a derived class defines one or more member functions of the base class. That base function is said to be overridden. The base class function must be declared as virtual function for runtime polymorphism to happen.
#include <bits/stdc++.h> using namespace std;
class Base { public:
// Virtual function
virtual void display() {
cout << "Base class function";
} };
class Derived : public Base { public:
// Overriding the base class function
void display() override {
cout << "Derived class function";
} };
int main() {
// Creating a pointer of type Base
Base* basePtr;
// Creating an object of Derived class
Derived derivedObj;
// Pointing base class pointer to
// derived class object
basePtr = &derivedObj;
// Calling the display function
// using base class pointer
basePtr->display();
return 0; }
Runtime Vs Compiler Time Polymorphism
Compile Time Polymorphism Run time Polymorphism Also called static binding Also called dynamic binding Achieved using function overloading and operator overloading Achieved using virtual functions and function overriding Decision made by the compiler at compile time Decision made at runtime using vtables Faster due to early binding More flexible but slightly slower
Virtual Functions
🔥EXAM TOPIC: Pointers and pass/call by reference (5 Marks).
A virtual function is a member function declared with the virtual keyword in a base class and overridden in a derived class. It allows function calls through base class pointers or references to invoke the implementation corresponding to the actual object type at runtime.
Enables runtime polymorphism through dynamic dispatch.
Allows derived classes to provide their own implementation of base class functions.
Invoked using base class pointers or references.
For Example: A base class Shape defines a virtual draw() function, while derived classes such as Circle and Rectangle provide their own implementations.
#include <iostream> using namespace std;
class Shape {
public:
// Virtual function
virtual void calculate()
{
cout << "Area of your Shape ";
}
// Virtual destructor
virtual ~Shape()
{
cout << "Shape Destructor called\n";
} };
// Derived class: Rectangle class Rectangle : public Shape {
public:
int width, height, area;
void calculate() override
{
width = 5;
height = 10;
area = height * width;
cout << "Area of Rectangle: " << area << "\n";
}
~Rectangle()
{
cout << "Rectangle Destructor called\n";
} };
// Derived class: Square class Square : public Shape {
public:
int side, area;
void calculate() override
{
side = 7;
area = side * side;
cout << "Area of Square: " << area << "\n";
}
~Square()
{
cout << "Square Destructor called\n";
} };
int main() {
Shape *S;
Rectangle r;
S = &r;
S->calculate();
Square sq;
S = &sq;
S->calculate();
return 0; }
Relationships Between Classes in C++
Classes in C++ can be related to one another in different ways to model real-world relationships. These relationships describe how objects interact and whether one object owns another.
Helps represent real-world relationships between classes.
Defines how objects of different classes interact with each other.
Association
Association is a relationship between two or more classes where their objects interact with each other while remaining independent. It represents a "uses-a" relationship between objects.
Represents a relationship between independent objects.
Does not imply ownership between objects.
The lifetime of one object does not depend on the other.
For Example: A Teacher teaches Students, but both can exist independently.
Association in C++ is commonly classified into the following types.
Aggregation (Weak Association)
Aggregation represents a "has-a" relationship where one class contains or refers to another class, but both objects have independent lifetimes.
Represents a weak ownership relationship.
Parent and child objects can exist independently.
Destroying the parent object does not destroy the child object.
🔥EXAM TOPIC: Loops and break/continue statements (4 Marks). Focus on nested loops.
For Example: A Company has Employees, but employees can continue to exist even if the company no longer exists.
#include <iostream>
#include <string>
class Teacher { public:
std::string name;
Teacher(const std::string& n) : name(n) {} };
class Department {
private:
// Aggregation: Department refers to Teacher
Teacher* teacher;
public:
Department(Teacher* t) : teacher(t) {}
void showTeacher() const {
if (teacher)
std::cout << "Teacher: " << teacher->name << std::endl;
} };
int main() {
Teacher t("Dr. Geek");
Department dept(&t);
dept.showTeacher();
return 0; }
Composition (Strong Association)
Composition is a stronger form of association in which one class owns another class. The lifetime of the child object depends on the parent object.
Represents a strong ownership relationship.
Parent and child objects have dependent lifetimes.
Destroying the parent object also destroys the child object.
For Example: A House is composed of Rooms. If the house is destroyed, the rooms are also destroyed.
#include <iostream> using namespace std;
// Simple class class A { public:
int x;
// COnstructor initializing
// the data members
A() { x = 0; }
A(int a)
{
cout << "Constructor A(int a) is invoked" << endl;
x = a;
}
};
// Complex class class B {
int data;
A objA;
public:
// COnstructor initializing the
// data members
B(int a)
: objA(a)
{
data = a;
}
// Function to print values
// of data members in class
// A and B
void display()
{
cout << "Data in object of class B = " << data
<< endl;
cout << "Data in member object of "
<< "class A in class B = " << objA.x;
} };
// Driver code int main() {
// Creating object of class B
B objb(25);
// Invoking display function
objb.display();
return 0; }
Friend Class and Function
In C++, friend functions and friend classes are concepts that allow certain functions or classes to access the private and protected members of another class.
A friend class can access private and protected members of other classes in which it is declared as a friend.
Remember one thing, friendship is not mutual. If class A is a friend of B, then B doesn't become a friend of A automatically.
We can declare a friend class in C++ by using the friend keyword.
#include <iostream> using namespace std;
class Geeks { private:
int private_variable;
protected:
int protected_variable;
public:
Geeks() {
private_variable = 10;
protected_variable = 99;
}
// friend class declaration
friend class GFG; };
// class GFG is declared as a friend
// inside class Geeks, therefore
// Class GFG can access private members
// of class Geeks. class GFG { public:
void display(Geeks& t) {
cout << "The value of Private Variable = "
<< t.private_variable << endl;
cout << "The value of Protected Variable = "
<< t.protected_variable;
} };
int main() {
Geeks g;
GFG fri;
fri.display(g);
return 0; }
Like friend classes, a friend function can be granted special access to private and protected members of a class in C++. They are not the member functions of the class but can access and manipulate the private and protected members of that class for they are declared as friends.
A friend function can be:
1. Global Function as Friend Function
We can declare any global function as a friend function. The following example demonstrates how to declare a global function as a friend function in C++. The keyword "friend" is placed only in the function declaration of the friend function and not in the function definition or call.
#include <iostream> using namespace std;
class base { private:
int private_variable;
protected:
int protected_variable;
public:
base() {
private_variable = 10;
protected_variable = 99;
}
// Friend function declaration
friend void friendFunction(base& obj); };
// friend function definition void friendFunction(base& obj) {
cout << "Private Variable: "
<< obj.private_variable << endl;
cout << "Protected Variable: "
<< obj.protected_variable; }
int main() {
base object1;
friendFunction(object1);
return 0; }
2. Member Function of Another Class as Friend Function
We can also declare a member function of another class as a friend function in C++. Forward declaration of the class is needed if we want to make a member function of another class a friend inside that class.
#include <iostream> using namespace std;
// Forward Declaration needed class base;
// Another class in which function is declared class GFG { public:
void GFG_Function(base& obj); };
// Base class declare a frined
// function of another class class base { private:
int private_variable;
protected:
int protected_variable;
public:
base() {
private_variable = 10;
protected_variable = 99;
}
// Friend function declaration
friend void GFG::GFG_Function(base&); };
// Friend function definition void GFG::GFG_Function(base& obj) {
cout << "Private Variable: " <<
obj.private_variable
<< endl;
cout << "Protected Variable: " <<
obj.protected_variable; }
int main() {
base object1;
GFG object2;
object2.GFG_Function(object1);
return 0; }
//Function Friendly to Multiple Classes
#include <iostream> using namespace std;
// Forward declaration class ABC;
class XYZ {
int x;
public:
void set_data(int a)
{
x = a;
}
friend void max(XYZ, ABC); };
class ABC {
int y;
public:
void set_data(int a)
{
y = a;
}
friend void max(XYZ, ABC); };
void max(XYZ t1, ABC t2) {
if (t1.x > t2.y)
cout << t1.x;
else
cout << t2.y; }
// Driver code int main() {
ABC _abc;
XYZ _xyz;
_xyz.set_data(20);
_abc.set_data(35);
// calling friend function
max(_xyz, _abc);
return 0; }
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
Explain the four main pillars of OOP (Encapsulation, Abstraction, Inheritance, Polymorphism).
What is a constructor? Explain default, parameterized, and copy constructors.
What is the difference between private, protected, and public access specifiers?
Write a program to demonstrate operator overloading for the '+' operator.
Unit 8: Files, Exceptions, Templates, STL
12 pages from PDFComplete content
🔥EXAM TOPIC: File handling and Exception handling (8 Marks). Expect a question on reading non-existing files.
Unit-8 Files, Exceptions, Templates, STL
Files
File handling in C++ refers to reading data from and writing data to files such as .txt, .csv, and binary files.
It allows programs to store and retrieve data from secondary storage.
The fstream library allows us to work with files.
To use the fstream library, include both the standard <iostream> and the <fstream> header file.
🔥EXAM TOPIC: Classes, objects, constructors, and inheritance (5 Marks).
There are three classes included in the fstream library, which are used to create, write or read file.
Class Description ofstream Creates and writes to files ifstream Reads from files fstream A combination of ofstream and ifstream: creates, reads, and writes to files
Before reading from or writing to a file, we need to open it. Opening a file creates a connection between the program and the file; it does not load the entire file into memory. Data is transferred when read or write operations are performed.
Create and Write to a File
To create a file, use either the ofstream or fstream class, and specify the name of the file.
To write to the file, use the insertion operator (<<).
#include <iostream>
#include <fstream> using namespace std;
int main() {
// Create and open a text file
ofstream myFile("filename.txt");
// Write to the file
myFile << "Files can be tricky, but it is fun enough!";
// Close the file
myFile.close();
return 0;
}
Read a File
To read from a file, use either the ifstream or fstream class, and the name of the file.
🔥EXAM TOPIC: Loops and break/continue statements (4 Marks). Focus on nested loops.
Note that we also use a while loop together with the getline() function (which belongs to the ifstream class) to read the file line by line, and to print the content of the file.
#include <iostream>
#include <fstream> using namespace std;
int main(){
// Create a text string, which is used to output the text file
string myText;
// Read from the text file ifstream MyReadFile("filename.txt");
// Use a while loop together with the getline() function to read the file line by line while (getline (MyReadFile, myText)) {
// Output the text from the file
cout << myText; }
// Close the file myReadFile.close();
return 0; }
Exceptions
Different types of errors can occur while running a program - such as coding mistakes, invalid input, or unexpected situations.
When an error occurs, C++ will normally stop and generate an error message.
Exception Handling in C++ is a mechanism used to handle runtime errors and abnormal conditions, allowing a program to continue execution smoothly even in the presence of errors. ® Handles abnormal conditions that occur during program execution. ® Helps maintain program stability by preventing unexpected program termination.
Exception Handling (try and catch)
The try block contains code that might throw an exception, while the catch block handles the exception if it occurs.
Exception handling lets you catch and handle errors during runtime - so your program doesn't crash. It uses three keywords:
try - defines the code to test
throw - triggers an exception
catch - handles the error
#include <iostream>
using namespace std;
int main()
{
int n = 10;
int m = 0;
try {
if (m == 0)
throw "Division by zero";
cout << "Answer: " << n / m;
}
catch (const char* msg) {
cout << "Error: " << msg;
}
return 0;
}
When an exception occurs, try-catch block works in the following ways.
The runtime executes code inside the try block.
If an exception is thrown, the remaining code inside the try block is skipped.
The runtime searches for a matching catch block.
If found, the exception is handled.
If no matching handler is found, terminate() is called.
During this process, local objects are destroyed automatically.
throw Keyword
The throw keyword is used to explicitly throw an exception.
#include <iostream>
using namespace std;
void checkAge(int age) {
if (age < 18)
throw "Age must be 18 or above";
} int main() {
try {
checkAge(15);
}
catch (const char* msg) {
cout << msg;
}
return 0;
}
C++ Exception Hierarchy
Most standard exceptions in C++ derive from the exception class.
Types of Exceptions
🔥EXAM TOPIC: Concept of data types and conversion/casting (4 Marks). Expect questions on implicit vs explicit casting.
There are mainly three types of exceptions in C++: 1. Built-in Exceptions- Built-in exceptions involve throwing primitive data types such as int, char, or float. Although simple, built-in exceptions provide limited information about the error.
#include <bits/stdc++.h> using namespace std;
int main() {
int x = 7;
try {
if (x % 2 != 0) {
// Throwing int
throw -1;
}
}
// Catching int
catch (int e) {
cout << "Exception Caught: " << e;
}
return 0; }
2. Standard Exceptions- C++ provides a hierarchy of standard exception classes defined in <exception> and <stdexcept>. Some commonly used standard exceptions are:
runtime_error
logic_error
out_of_range
invalid_argument
overflow_error
🔥EXAM TOPIC: Functions, parameters, and recursion (8 Marks). Very high chance of a recursion question.
All standard exceptions derive from std::exception and provide the what() function.
#include <bits/stdc++.h> using namespace std;
int main() {
vector<int> v = {1, 2, 3};
try {
// Accessing out of bound element
v.at(10);
}
catch (out_of_range e) {
cout << "Caught: " << e.what();
}
return 0; }
3. Custom Exceptions- When standard exceptions are insufficient, custom exception classes can be created.
#include <iostream>
#include <exception> using namespace std;
// Custom exception class class NegativeValueException : public exception { private:
int value; public:
// Constructor
NegativeValueException(int val) : value(val) {}
// Override what() method
const char* what() const noexcept override {
return "Negative value error occurred!";
}
// Optional: method to get the invalid value
int getValue() const {
return value;
} };
// Function that throws the custom exception void checkValue(int x) {
if (x < 0) {
throw NegativeValueException(x);
}
else {
cout << "Value is: " << x << endl;
} }
int main() {
int numbers[] = {10, -5, 20};
for (int n : numbers) {
try {
checkValue(n);
}
catch (NegativeValueException &e) {
cout << "Exception caught: " << e.what()
<< " Value = " << e.getValue() << endl;
}
}
return 0; }
Nested try-catch
In Nested try-catch, one try-catch block can be placed inside another.
T is a placeholder for a data type (like int, float, etc.).
You can use any name instead of T, but T is common.
template <typename T> T add(T a, T b) {
return a + b; }
int main() {
cout << add<int>(5, 3) << "\n";
cout << add<double>(2.5, 1.5) << "\n";
return 0; }
In the example above, add<int>(5, 3) tells the compiler to use int for T, while add<double>(2.5, 1.5) tells it to use double.
Class Templates- You can also use templates to make classes that work with any data type:
Syntax:
template <typename T> class ClassName {
// members and methods using T };
🔥EXAM TOPIC: String and c-string functions (4 Marks).
The example below defines a template class Box that can store and display a value of any data type, and then creates one box for an int and one for a string
template <typename T> class Box {
public:
T value;
Box(T v) {
value = v;
}
void show() {
cout << "Value: " << value << "\n";
} };
int main() {
Box<int> intBox(50);
Box<string> strBox("Hello");
intBox.show();
strBox.show();
return 0; }
And this example defines a template class Pair that stores two values of different types and displays them, then creates one pair for a person's name and age, and another for an ID and score.
template <typename T1, typename T2> class Pair {
public:
T1 first;
T2 second;
Pair(T1 a, T2 b) {
first = a;
second = b;
}
void display() {
cout << "First: " << first << ", Second: " << second << "\n";
} };
int main() {
Pair<string, int> person("John", 30);
Pair<int, double> score(51, 9.5);
person.display();
score.display();
return 0; }
Standard Template Library (STL)
🔥EXAM TOPIC: Structure, arrays and vectors (6 Marks).
STL (Standard Template Library) is a library that consist of different data structures and algorithms to effectively store and manipulate data.
It is a collection of pre-built classes and functions in C++ used for efficient data handling and programming. It provides ready-to-use data structures and algorithms that simplify development.
The most common data structures are:
Data Structure Description Vector Stores elements like an array but can dynamically change in size. Adding and removing of elements are usually done at the end. Elements can be accessed by index. List Stores elements sequentially, where each element is connected to the next. Adding and removing of elements can be done at both ends. Not accessible by index. Stack Stores elements in a specific order, called LIFO (Last In, First Out), where elements can only be added and removed from the top. Not accessible by index. Queue Stores elements in a specific order, called FIFO (First In, First Out), where elements are added at the end and removed from the front. Not accessible by index. Deque Stores elements in a double-ended queue, where elements can be added and removed from both ends. Elements can be accessed by index. Set Stores unique elements. Not accessible by index. Map Stores elements in "key/value" pairs. Accessible by keys (not by index).
Which one to use depends on your specific needs. One thing they all have in common is that you must include the appropriate header file to use them.
Example:
// Include the vector library
#include <vector>
// Include the list library
#include <list>
// Include the set library
#include <set>
// Include the map library
#include <map>
// Include the stack library
#include <stack>
// Include the queue library
#include <queue>
Components of STL
The components of STL are the features provided by STL in C++ that can be classified into 3 types.
Containers
Containers are the data structures used to store objects and data according to the requirement.
Each container is implemented as a template class that also contains the methods to perform basic operations on it.
Every STL container is defined inside its own header file.
Containers can be further classified into 4 types:
STL algorithms offer a wide range of functions to perform common operations on data (mainly containers).
🔥EXAM TOPIC: Sorting and Searching algorithms (10 Marks). Focus on Bubble/Insertion/Selection combined with Binary Search.
These functions implement the most efficient version of the algorithm for tasks such as sorting, searching, modifying and manipulating data in containers, etc.
Most STL algorithms are defined in <algorithm> and <numeric>, while some specialized algorithms and utilities are available in other headers like <memory>, <functional>, and <iterator>.
Some of the most frequently used algorithms are:
Sort : Arranges elements in ascending order (default).
Binary Search : Checks whether a value exists in a sorted range.
Find : Searches for the first occurrence of a given value.
Count : Counts how many times a value appears in the given range.
Reverse : Reverses the order of elements in the given range.
Accumulate : Computes the sum of all elements in the range.
Unique : Removes consecutive duplicate elements.
Lower bound : Returns iterator to the first element ≥ value in a sorted range.
Upper bound : Returns iterator to the first element > value in a sorted range.
Replace : Replaces all occurrences of old value with new value in the given range.
Iterators
Iterators provide a common way to access and traverse elements stored in STL containers.
🔥EXAM TOPIC: Pointers and pass/call by reference (5 Marks).
Act as pointer-like objects for accessing container elements.
Connect STL containers with generic algorithms.
Support operations such as traversing, accessing, and modifying elements.
Different containers provide different types of iterators based on their capabilities.
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
Write a C++ program to open a file, write data into it, and read it back.
What is exception handling? Explain try, catch, and throw with an example.
What are templates? Write a template function to swap two numbers.
Explain the use of STL vectors. How do you add and remove elements from a vector?
Access Modifiers
4 pages from PDFComplete content
Tutorials- Access Modifier
1. Public Access Modifier- Members declared as public are accessible throughout the program.
//Public Access Modifier
#include <iostream> using namespace std;
class Circle { public:
double radius;
// Accessible anywhere
double getArea() {
return 3.14159 * radius * radius;
} };
int main() {
Circle c1;
c1.radius = 2.2;
// Direct access to public member
cout << c1.getArea();
return 0; }
🔥EXAM TOPIC: Functions, parameters, and recursion (8 Marks). Very high chance of a recursion question.
2. Private Access Modifier- Members declared as private can only be accessed by member functions inside the same class. In C++, if you do not specify an access modifier inside a class, all members are private by default.
#include <iostream>
#include <iomanip> using namespace std;
class BankAccount { private:
double balance;
// Hidden from the other class
public:
BankAccount(double bal){
balance = bal;
}
void deposit(double amount) {
if (amount > 0){
balance += amount;
}
}
void print(){
cout<< setprecision(5)<<balance;
} };
int main() {
BankAccount myAcc(149.22);
// myAccount.balance = 997.25;
// 'balance' is private (Compile Error)
myAcc.deposit(9.50);
// 'deposit' is public
myAcc.print();
return 0; }
🔥EXAM TOPIC: Classes, objects, constructors, and inheritance (5 Marks).
3. Protected Access Modifier- Members declared as protected can be accessed by member functions within the same class and the derived (child) classes through inheritance. Objects of the base class still cannot access them directly from the outside.
#include <iostream> using namespace std;
class Employee { protected:
int empId;
// Child classes can access, main() cannot. };
class Manager : public Employee { public:
Manager(int id) {
empId = id;
// Derived class accessing protected member
}
void print(){
cout<<"Employee Id is: "<<empId;
} };
int main() {
Employee emp;
// emp.empId = 101;
// Cannot access directly through employee object
Manager mgr(5001);
mgr.print();
return 0; }
4. Private members accessing using public setter and getter functions.
#include <iostream> using namespace std;
class Employee {
private:
int empId;
public:
void setter(int empId){
this -> empId = empId;
}
int getter(){
return this -> empId;
} };
int main() {
Employee emp;
emp.setter(5001);
cout<<"Employee Id is: "<<emp.getter();
return 0; }
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
What happens if a class member is declared without an access modifier?
Can a protected member be accessed outside the class?
Why is encapsulation important in OOP?
Friend Keyword
3 pages from PDFComplete content
Tutorials- Friend Keyword
🔥EXAM TOPIC: Functions, parameters, and recursion (8 Marks). Very high chance of a recursion question.
The friend keyword is used to grant a non-member function or another class full access to a class's private and protected members.
🔥EXAM TOPIC: Classes, objects, constructors, and inheritance (5 Marks).
1. A friend function is a regular, standalone function (or a member function of another class) that is given special permission to access the private data of a specific class.
1. 1 Global function as friend function
//Global function as friend function
#include <iostream> using namespace std;
class myApp1 { private:
int num1;
protected:
int num2;
public:
myApp1(){
num1 = 55;
num2 = 76;
}
friend void display();
};
void display(){
myApp1 ap1;
cout<<"Private num1: "<<ap1.num1<<endl;;
cout<<"Protected num2: "<<ap1.num2<<endl; }
int main() {
display();
return 0; }
1. 2 Member function of another class as friend function.
//Member function of another class as friend function
#include <iostream>
using namespace std; class A;
class B { public:
void getData(A a);
};
class A { private:
int num1;
int num2;
public:
A(int a, int b) {
num1 = a;
num2 = b;
}
friend void B::getData(A a);
};
void B::getData(A a) {
cout << "num1: " << a.num1 << endl;
cout << "num2: " << a.num2 << endl; }
int main() {
A aa(3, 4);
B bb;
bb.getData(aa);
return 0; }
2. A friend class can access private and protected members of other classes in which it is declared as a friend. The friendship is not mutual- if class A is a friend of B, then B doesn't become a friend of A.
//Friend class example
#include <iostream> using namespace std;
class myApp1 { private:
int num1;
protected:
int num2;
public:
myApp1(){
num1 = 55;
num2 = 76;
}
friend class myApp2;
};
class myApp2{ public:
void display(){
myApp1 ap1;
cout<<"Private num1: "<<ap1.num1<<endl;;
cout<<"Protected num2: "<<ap1.num2<<endl;
} };
int main() {
myApp2 ap2;
ap2.display();
return 0; }
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
What is a friend function? Write a program to demonstrate its use.
Can a friend function access private members of two different classes?
Does a friend function have a 'this' pointer? Explain.
Function Overriding and Function Overloading
3 pages from PDFComplete content
🔥EXAM TOPIC: Functions, parameters, and recursion (8 Marks). Very high chance of a recursion question.
Tutorials- Function Overriding and Function Overloading
🔥EXAM TOPIC: Polymorphism (compile time / run time) (5 Marks).
1. Function Overriding
//Function Overriding
#include <iostream> using namespace std;
class P1{
public:
//inheritance
void myInherit(){
cout<<"I exists in parent class."<<endl;
cout<<"I am getting access from the child object."<<endl;
}
void myDisplay(){
cout<<"I am in parents class.";
} };
class C1: public P1{
public:
void myDisplay(){
cout<<"I am in child class.";
} };
int main() {
C1 cc1; //creating child class object
cc1.myInherit(); //no function to override; so, calling parent class function
cc1.myDisplay(); //overrides the parent class function with same name
return 0; }
2. Function Overloading based on numbers of parameters.
//Function Overloading based on number of parameters
#include <iostream> using namespace std;
class MyClass{
public:
int calc(int a, int b){
return a + b;
}
int calc(int a, int b, int c){
return a + b + c;
} };
int main() {
MyClass MC1;
cout<<"Function overloading with two arguments: "<<MC1.calc(2, 4)<<endl;
cout<<"Function overloading with three arguments: "<<MC1.calc(2, 4, 6)<<endl;
return 0; }
3. Function overloading based on order of parameters.
//Function Overloading based on order of parameters
#include <iostream> using namespace std;
class MyClass{
public:
void display(int age, string name){
cout<<"Age: "<<age<<endl;
cout<<"Name: "<<name<<endl;
}
void display(string name, int age){
cout<<"Name: "<<name<<endl;
cout<<"Age: "<<age<<endl;
} };
int main() {
MyClass MC1;
MC1.display("John Smith", 44);
//order of parameters - first with string and then integer
cout<<endl;
MC1.display(44, "Michael Jackson");
//order of parameter- first with integer and then string
return 0;
}
4. Function overloading based on types of parameters.
//Function Overloading based on types of parameters
#include <iostream> using namespace std;
class MyClass{
public:
void display(long n){
cout<<"Parameter is caught as a long type"<<endl;
}
void display(float n){
cout<<"Parameter is caught as a float type"<<endl;
}
void display(int n){
cout<<"Parameter is caught as a integer type"<<endl;
}
void display(string n){
cout<<"Parameter is caught as a string type"<<endl;
}
void display(double n){
cout<<"Parameter is caught as a double type"<<endl;
} };
int main() {
MyClass MC1;
MC1.display(44);
MC1.display("John Smith");
MC1.display(3.14F);
MC1.display(3.14);
MC1.display(98765432122);
return 0; }
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
What is function overloading? Give an example.
What is function overriding? How is it different from overloading?
Why is the 'virtual' keyword used in function overriding?
Inheritance
4 pages from PDFComplete content
🔥EXAM TOPIC: Functions, parameters, and recursion (8 Marks). Very high chance of a recursion question.
Tutorials- Function Inheritance
🔥EXAM TOPIC: Classes, objects, constructors, and inheritance (5 Marks).
1. Single Inheritance
//Single Inheritance
#include <iostream> using namespace std;
class P1{
public:
void display(){
cout<<"Thank you";
}
};
class C1: public P1{
};
int main() {
C1 cc;
cc.display();
return 0; }
2. Multi-level Inheritance
//Multi-level Inheritance
#include <iostream> using namespace std;
class P1{
public:
void display(){
cout<<"Thank you";
}
};
class C1: public P1{
};
class S1: public C1{
};
int main() {
S1 ss;
ss.display();
return 0; }
3. Multiple Inheritance
//Multiple Inheritance
#include <iostream> using namespace std;
class P1{
public:
void display(){
cout<<"Thank you"<<endl;
}
};
class P2{
public:
void greet(){
cout<<"Hello"<<endl;
} };
class C1: public P1, public P2{
};
int main() {
C1 cc;
cc.greet();
cc.display();
return 0; }
4. Hierarchical Inheritance
//Hierarchical Inheritance
#include <iostream> using namespace std;
class P1{
public:
void display(){
cout<<"Hello World"<<endl;
} };
class C1: public P1{
};
class C2: public P1{
};
int main() {
C1 cc1;
C2 cc2;
cc1.display();
cc2.display();
return 0; }
5. Hybrid Inheritance
//Hybrid Inheritance
#include <iostream> using namespace std;
class P1{
public:
void display(){
cout<<"Hello World"<<endl;
} };
class C1: public P1{
};
class C2: public P1{
};
class SC1: public C1{
};
int main() {
SC1 sc1;
C2 cc2;
sc1.display();
cc2.display();
return 0; }
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
What is inheritance? Explain single and multiple inheritance.
What is the ambiguity problem in multiple inheritance? How is it solved?
Write a program to demonstrate multilevel inheritance.
Operator Overloading
3 pages from PDFComplete content
🔥EXAM TOPIC: Polymorphism (compile time / run time) (5 Marks).
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
What is operator overloading? Can we overload all operators?
Write a program to overload the unary '++' operator.
How is operator overloading different from function overloading?
Pure Virtual Function
3 pages from PDFComplete content
🔥EXAM TOPIC: Functions, parameters, and recursion (8 Marks). Very high chance of a recursion question.
Tutorials- Pure Virtual Function
1. Print and scan
#include <iostream> using namespace std;
// Pure Abstract Class acting as an Interface class Printable { public:
virtual void print() = 0;
// pure virtual function
virtual void scan() = 0;
// pure virtual function
// Virtual destructor is a good practice for base classes
virtual ~Printable() {} };
// Derived class must implement all functions class Document : public Printable { public:
void print() override {
cout << "Printing document..." << endl;
}
void scan() override {
cout << "Scanning document..." << endl;
} };
class Photo : public Printable { public:
void print() override {
cout << "Printing photo..." << endl;
}
void scan() override {
cout << "Scanning photo..." << endl;
} };
int main() {
// Base class pointer pointing to derived objects
Printable* p1 = new Document();
Printable* p2 = new Photo();
// Call interface functions - runtime polymorphism
p1->print();
p1->scan();
p2->print();
p2->scan();
// Free memory
delete p1;
delete p2;
return 0; }
2. Shape
#include <iostream> using namespace std;
// Abstract Base Class class Shape { public:
// Pure virtual function makes Shape an abstract class
virtual void draw() const = 0;
// Virtual destructor ensures proper cleanup of derived objects
virtual ~Shape() {} };
// Derived Class 1: Circle class Circle : public Shape { private:
double radius;
public:
Circle(double r) : radius(r) {}
// Implementation of draw() for Circle
void draw() const override {
cout << "Drawing a Circle with radius: " << radius << endl;
} };
// Derived Class 2: Rectangle class Rectangle : public Shape { private:
double width;
double height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
// Implementation of draw() for Rectangle
void draw() const override {
cout << "Drawing a Rectangle with dimensions: " << width << " x " << height << endl;
} };
int main() {
// Working with shapes using base class pointers (Common Interface)
Shape* shape1 = new Circle(5.0);
Shape* shape2 = new Rectangle(4.0, 6.0);
// Dynamic polymorphism: calls the respective draw() implementation
shape1->draw();
shape2->draw();
// Clean up allocated memory
delete shape1;
delete shape2;
return 0; }
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
What is a pure virtual function? How is it declared?
What is an abstract class? Can we instantiate an abstract class?
Why do we need abstract classes in C++?
Linked Lists
15 pages from PDFComplete content
🔥EXAM TOPIC: Linked List - Insertion, deletion, traversal (10 Marks).
Tutorials- Linked Lists
1. Singly Linked List with a node
//Singly linked list with a data and address(next) values
#include <iostream> using namespace std;
class Node{
public:
int data;
Node* next;
Node(int data){
this -> data = data;
this -> next = NULL;
} };
int main() {
Node* node1 = new Node(10);
cout<< node1 -> data<< endl;
cout<< node1 -> next<< endl;
return 0; }
2. Singly Linked List, inserting node at head.
/*Singly linked list with a node and inserting two more nodes at head.*/
#include <iostream> using namespace std;
class Node{
public:
int data;
Node* next;
Node(int data){
this -> data = data;
this -> next = NULL;
} };
void insertAtHead(Node* &head, int d){
Node* temp = new Node(d);
temp -> next = head;
head = temp; }
void print(Node* &head){
Node* temp = head;
while(temp!=NULL){
cout<< temp -> data<<" ";
temp= temp->next;
}
cout<<endl; }
int main() {
Node* node1 = new Node(10);
Node* head = node1;
print(head);
insertAtHead(head, 12);
print(head);
insertAtHead(head, 15);
print(head);
return 0; }
3. Singly linked list, inserting node at tail.
/*Singly linked list with a node and inserting three more nodes at tail.*/
#include <iostream> using namespace std;
class Node{
public:
int data;
Node *next;
Node(int data){
this-> data = data;
this-> next = NULL;
}
};
void insertAtTail(Node* &tail, int n){
Node* temp = new Node(n);
tail -> next = temp;
tail = tail -> next; }
void print(Node* &head){
Node* temp = head;
while(temp!= NULL){
cout<< temp -> data <<" ";
temp = temp -> next;
}
cout<<endl; }
int main() {
Node* node1 = new Node(10);
Node* head = node1;
Node* tail = node1;
print(head);
insertAtTail(tail, 12);
print(head);
insertAtTail(tail, 15);
print(head);
insertAtTail(tail, 20);
print(head);
return 0; }
4. Singly linked list, inserting node at any position.
/*Singly linked list with a node, inserting one node at head and two nodes at tail; and also, inserting a node at the position 3.*/
#include <iostream> using namespace std;
class Node{
public:
int data;
Node *next;
Node(int data){
this-> data = data;
this-> next = NULL;
} };
void insertAtHead(Node* &head, int n){
Node* temp = new Node(n);
temp -> next = head;
head = temp; }
void insertAtTail(Node* &tail, int n){
Node* temp = new Node(n);
tail -> next = temp;
tail = tail -> next; }
void insertAtPosition(Node* &head, Node* &tail, int position, int n){
if(position == 1){
insertAtHead(head, n);
return;
}
Node* temp = head;
int cnt = 1;
while(cnt < position-1){
temp = temp -> next;
cnt++;
}
if(temp -> next == NULL){
insertAtTail(tail, n);
return;
}
Node* nodeToInsert = new Node(n);
nodeToInsert -> next = temp -> next;
temp -> next = nodeToInsert; }
void print(Node* &head){
Node* temp = head;
while(temp!= NULL){
cout<< temp -> data <<" ";
temp = temp -> next;
}
cout<<endl; }
int main() {
Node* node1 = new Node(10);
Node* head = node1;
Node* tail = node1;
print(head);
insertAtHead(head, 12);
print(head);
insertAtTail(tail, 15);
print(head);
insertAtTail(tail, 20);
print(head);
insertAtPosition(head, tail, 3, 22);
print(head);
cout<< "head "<<head ->data <<endl;
cout<< "tail "<<tail ->data <<endl;
return 0; }
5. Singly linked list, deleting node at any position.
#include <iostream> using namespace std;
class Node{
public:
int data;
Node *next;
Node(int data){
this-> data = data;
this-> next = NULL;
}
//Destructor for releasing the memory
~Node(){
int value = this -> data;
if(this -> next != NULL){
delete next;
this -> next = NULL;
}
cout<< "Memory is free for a data " <<value <<endl;
} };
void insertAtHead(Node* &head, int n){
Node* temp = new Node(n);
temp -> next = head;
head = temp; }
void insertAtTail(Node* &tail, int n){
Node* temp = new Node(n);
tail -> next = temp;
tail = tail -> next; }
void insertAtPosition(Node* &head, Node* &tail, int position, int n){
if(position == 1){
insertAtHead(head, n);
return;
}
Node* temp = head;
int cnt = 1;
while(cnt < position-1){
temp = temp -> next;
cnt++;
}
if(temp -> next == NULL){
insertAtTail(tail, n);
return;
}
Node* nodeToInsert = new Node(n);
nodeToInsert -> next = temp -> next;
temp -> next = nodeToInsert; }
void print(Node* &head){
Node* temp = head;
while(temp!= NULL){
cout<< temp -> data <<" ";
temp = temp -> next;
}
cout<<endl; }
void deleteNode(int position, Node* &head, Node* &tail){
if(position == 1){
Node* temp = head;
head = head -> next;
temp -> next = NULL;
delete temp;
}
else{
Node* curr = head;
Node* prev = NULL;
int cnt = 1;
while(cnt < position){
prev = curr;
curr = curr -> next;
cnt++;
}
prev -> next = curr -> next;
curr -> next = NULL;
tail = prev;
delete curr;
} }
int main() {
Node* node1 = new Node(10);
Node* head = node1;
Node* tail = node1;
print(head);
insertAtHead(head, 12);
print(head);
insertAtTail(tail, 15);
print(head);
insertAtTail(tail, 20);
print(head);
insertAtPosition(head, tail, 1, 22);
print(head);
cout<< "head "<<head ->data <<endl;
cout<< "tail "<<tail ->data <<endl;
deleteNode(5, head, tail);
print(head);
cout<< "head "<<head -> data <<endl;
cout<< "tail "<<tail -> data <<endl;
return 0; }
6. Doubly linked list with a node.
#include <iostream> using namespace std;
class Node{
public:
int data;
Node* next;
Node* prev;
Node(int d){
this -> data = d;
this -> next = NULL;
this -> prev = NULL;
} };
//Traversing a linked list void print(Node* head){
Node* temp = head;
while (temp != NULL){
cout<< temp -> data;
temp= temp -> next;
}
cout<<endl; }
//Getting length of linked list int getLength(Node* head){
int len = 0;
Node* temp = head;
while (temp != NULL){
len++;
temp= temp -> next;
}
return len; }
int main(){
Node* node1 = new Node(10);
Node* head = node1;
print(head);
cout<<getLength(head);
return 0; }
7. Doubly linked list with a node insertAtHead insertAtTail and insertAtPosition.
#include <iostream> using namespace std;
class Node{
public:
int data;
Node* next;
Node* prev;
Node(int d){
this -> data = d;
this -> next = NULL;
this -> prev = NULL;
} };
//Traversing a linked list void print(Node* head){
Node* temp = head;
while (temp != NULL){
cout<< temp -> data<<" ";
temp= temp -> next;
}
cout<<endl; }
//Getting length of linked list int getLength(Node* head){
int len = 0;
Node* temp = head;
while (temp != NULL){
len++;
temp= temp -> next;
}
return len; }
void insertAtHead(Node* &head, int d){
Node* temp = new Node(d);
temp -> next = head;
head -> prev = temp;
head = temp; }
void insertAtTail(Node* &tail, int d){
Node* temp = new Node(d);
tail -> next = temp;
temp -> prev = tail;
temp -> next = NULL;
tail = temp; }
void insertAtPosition(Node* &head, Node* &tail, int position, int d){
if(position == 1){
insertAtHead(head, d);
return;
}
Node* temp = head;
int cnt = 1;
while(cnt < position-1){
temp = temp -> next;
cnt++;
}
if(temp -> next == NULL){
insertAtTail(tail, d);
return;
}
Node* nodeToInsert = new Node(d);
nodeToInsert -> next = temp -> next;
nodeToInsert -> prev = temp;
temp -> next = nodeToInsert;
temp -> next -> prev = nodeToInsert; } int main() {
Node* node1 = new Node(10);
Node* head = node1;
Node* tail = node1;
print(head);
insertAtHead(head, 20);
print(head);
insertAtHead(head, 30);
print(head);
insertAtTail(tail, 50);
print(head);
insertAtPosition(head, tail, 5, 80);
print(head);
cout<<"Head: "<<head -> data<<endl;
cout<<"Tail: "<<tail -> data<<endl;
cout<<"The lenght of linked list is "<<getLength(head);
return 0; }
8. Doubly linked lists- deleting node from any position.
#include <iostream> using namespace std;
class Node{
public:
int data;
Node* next;
Node* prev;
Node(int d){
this -> data = d;
this -> next = NULL;
this -> prev = NULL;
}
~Node(){
int val = this -> data;
if(next != NULL){
delete next;
next = NULL;
}
cout<<"Memory free for node with data "<<val<<endl;
} };
//Traversing a linked list void print(Node* head){
Node* temp = head;
while (temp != NULL){
cout<< temp -> data<<" ";
temp= temp -> next;
}
cout<<endl; }
//Getting length of linked list int getLength(Node* head){
int len = 0;
Node* temp = head;
while (temp != NULL){
len++;
temp= temp -> next;
}
return len; }
void insertAtHead(Node* &head, int d){
Node* temp = new Node(d);
temp -> next = head;
head -> prev = temp;
head = temp; }
void insertAtTail(Node* &tail, int d){
Node* temp = new Node(d);
tail -> next = temp;
temp -> prev = tail;
temp -> next = NULL;
tail = temp; }
void insertAtPosition(Node* &head, Node* &tail, int position, int d){
if(position == 1){
insertAtHead(head, d);
return;
}
Node* temp = head;
int cnt = 1;
while(cnt < position-1){
temp = temp -> next;
cnt++;
}
if(temp -> next == NULL){
insertAtTail(tail, d);
return;
}
Node* nodeToInsert = new Node(d);
nodeToInsert -> next = temp -> next;
nodeToInsert -> prev = temp;
temp -> next = nodeToInsert;
temp -> next -> prev = nodeToInsert; }
void deleteNode(int position, Node* &head){
if(position == 1){
Node* temp = head;
temp -> next -> prev = NULL;
head = temp -> next;
temp -> next = NULL;
delete temp;
}
else{
Node* curr = head;
Node* prev = NULL;
int cnt = 1;
while(cnt < position){
prev = curr;
curr = curr -> next;
cnt++;
}
curr -> prev = NULL;
prev -> next = curr -> next;
curr -> next = NULL;
delete curr;
} }
int main() {
Node* node1 = new Node(10);
Node* head = node1;
Node* tail = node1;
print(head);
insertAtHead(head, 20);
print(head);
insertAtHead(head, 30);
print(head);
insertAtTail(tail, 50);
print(head);
insertAtPosition(head, tail, 3, 80);
print(head);
cout<<"Head: "<<head -> data<<endl;
cout<<"Tail: "<<tail -> data<<endl;
deleteNode(5, head);
print(head);
cout<<"Head: "<<head -> data<<endl;
cout<<"Tail: "<<tail -> data<<endl;
cout<<"The lenght of linked list is "<<getLength(head);
return 0; }
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
Write an algorithm to insert a node at the beginning of a singly linked list.
Write an algorithm to delete the last node of a linked list.
What are the advantages of linked lists over arrays?
Write a C++ program to traverse and display a linked list.
Queues
2 pages from PDFComplete content
🔥EXAM TOPIC: Templates, STL, Stack and Queues (10 Marks).
Tutorials- Queues
🔥EXAM TOPIC: Functions, parameters, and recursion (8 Marks). Very high chance of a recursion question.
1. Enqueue, dequeue and display using the custom function.
#include <iostream> #define MAX 5
// Maximum size of the queue
using namespace std;
class Queue { private:
int arr[MAX];
int front;
int rear;
public:
// Constructor to initialize the queue
Queue() {
front = -1;
rear = -1;
}
// Function to add an element to the queue
void enqueue(int value) {
if (rear == MAX - 1) {
cout << "Queue Overflow! Cannot enqueue " << value << endl;
return;
}
if (front == -1) {
front = 0;
// Initialize front index on first insertion
}
arr[++rear] = value;
cout << value << " successfully enqueued." << endl;
}
// Function to remove an element from the queue
void dequeue() {
if (front == -1 || front > rear) {
cout << "Queue Underflow! Nothing to dequeue." << endl;
return;
}
cout << arr[front] << " dequeued from the queue." << endl;
front++;
// Reset the queue pointers if it becomes empty
if (front > rear) {
front = -1;
rear = -1;
}
}
// Function to display the elements of the queue
void display() {
if (front == -1 || front > rear) {
cout << "Queue is empty!" << endl;
return;
}
cout << "Queue elements: ";
for (int i = front; i <= rear; i++) {
cout << arr[i] << " ";
}
cout << endl;
} };
int main() {
Queue q;
// Perform operations
q.enqueue(10);
q.enqueue(20);
q.enqueue(30);
q.display();
q.dequeue();
q.display();
return 0; }
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
What is a Queue? Explain the FIFO principle.
Write a program to implement enqueue and dequeue operations.
What is a circular queue? Why is it better than a linear queue?
Explain the conditions for Queue Overflow and Underflow.
Unit 11: Binary Trees
12 pages from PDFComplete content
🔥EXAM TOPIC: Binary Tree Traversal (5 Marks). Know recursive/iterative approaches.
Unit-11 Binary Trees
🔥EXAM TOPIC: Structure, arrays and vectors (6 Marks).
A binary tree is a hierarchical data structure in which each node has at most two children, commonly referred to as the left child and the right child.
🔥EXAM TOPIC: Sorting and Searching algorithms (10 Marks). Focus on Bubble/Insertion/Selection combined with Binary Search.
It is one of the most widely used tree data structures and forms the foundation of advanced structures such as Binary Search Trees, Heaps, and Expression Trees.
Each node in a binary tree can have zero, one, or two children. The topmost node is called the root, while nodes with no children are called leaf nodes. Binary trees are commonly used for hierarchical data representation, searching, and expression evaluation.
🔥EXAM TOPIC: Classes, objects, constructors, and inheritance (5 Marks).
A binary tree node is traditionally represented using a struct or a class. Each node contains a data variable and two pointers pointing to the memory locations of its child nodes. These pointers create links between nodes, allowing the tree to represent hierarchical relationships efficiently.
🔥EXAM TOPIC: Pointers and pass/call by reference (5 Marks).
A node contains a data field to store the value. A left pointer stores the address of the left child node. A right pointer stores the address of the right child node. Nodes are dynamically connected using pointers to form the tree structure.
#include <iostream>
// class definition for a node class Node {
public:
int data;
Node* left;
Node* right;
// Constructor to easily create and initialize new nodes
Node(int value) {
data = value;
left = nullptr;
right = nullptr;
} };
The following complete program demonstrates how to build a simple tree and navigate it using the three core Depth-First Search (DFS) traversal techniques.
Preorder Traversal (Root → Left → Right):
o It is a depth-first tree traversal method where each node is visited in a specific sequence: Root → Left Subtree → Right Subtree.
The term "pre" highlights that the root node is processed before moving to its child subtrees.
For every node encountered during the traversal, the algorithm performs three steps in order. 1. Visit the current node (e.g., print its value or save it). 2. Traverse the left subtree completely. 3. Traverse the right subtree completely.
Consider the following binary tree:
1
/ \
2 3
/ \
4 5
Following the Root → Left → Right sequence:
1. Start at the root: 1 2. Move left to node 2 3. Move left to node 4 (no children, backtrack to 2) 4. Move right to node 5 (no children, backtrack to 1) 5. Move right to node 3
Preorder Traversal Output: 1, 2, 4, 5, 3
Inorder (Left → Root → Right):
It is a depth-first tree traversal method where you visit nodes in a specific, strict sequence: Left subtree → Root Node → Right subtree.
For any given node in the tree, the process follows these three sequential steps. 1. Left: Recursively traverse the entire left subtree. 2. Root: Visit and process (e.g., print or save) the current node. 3. Right: Recursively traverse the entire right subtree.
Consider this simple binary tree:
1
/ \
2 3
/ \
4 5
Following the Left → Root → Right rule:
1. Start at root 1. Go left to 2. 2. At 2, go left to 4. 3. At 4, there are no left children. We visit 4, then check its right child (none). 4. Back up to 2. We already finished 2's left side, so we visit 2. Then go right to 5. 5. At 5, there are no children, so we visit 5. 6. Back up to 1. We finished 1's entire left subtree, so we visit 1. Then go right to 3. 7. At 3, there are no left children, so we visit 3.
Inorder Traversal Output: 4, 2, 5, 1, 3
Postorder (Left → Right → Root):
Postorder traversal is a depth-first tree traversal method that visits nodes in a Left → Right → Root order.
This means for any given node, the algorithm completely processes its entire left subtree first, then its entire right subtree, and finally visits the node itself.
Because a parent node is only visited after all of its descendants are processed, postorder traversal is widely used for deleting/freeing a tree (deleting children before the parent) and generating postfix expressions from mathematical expression trees.
🔥EXAM TOPIC: Functions, parameters, and recursion (8 Marks). Very high chance of a recursion question.
Every recursive call follows three strict operational steps: 1. Left: Run the postorder function on the left child node. 2. Right: Run the postorder function on the right child node. 3. Root: Process (e.g., print or store) the current node's value.
Consider this simple binary tree:
1
/ \
2 3
/ \
4 5
Following the postorder rules, here is how the algorithm executes step-by-step:
1. Start at the root node (1). Go to its left subtree (headed by 2). 2. From node 2, go to its left child, which is node 4. 3. Node 4 has no children (it is a leaf node). Left is empty → Right is empty → Visit node 4.
4. Go back to parent node 2. Before visiting 2, you must check its right child, which is node 5. 5. Node 5 has no children.
Postorder Traversal Output: 4, 5, 2, 3, 1
//Binary Tree Recursive Traversal
#include <iostream> using namespace std;
class Node {
public:
int data;
Node* left;
Node* right;
Node(int value){
data = value;
left = NULL;
right = NULL;
} };
// 1. Preorder Traversal void printPreOrder(Node* root) {
if (root == NULL){
return;
}
cout << root -> data << " ";
printPreOrder(root -> left);
printPreOrder(root -> right); }
// 2. Inorder Traversal void printInOrder(Node* root) {
if (root == NULL){
return;
}
printInOrder(root->left);
cout << root->data << " ";
printInOrder(root->right); }
// 3. Postorder Traversal void printPostOrder(Node* root) {
if (root == NULL){
return;
}
printPostOrder(root->left);
printPostOrder(root->right);
cout << root->data << " "; }
int main() {
/* Hardcoding a sample tree structure:
1
/ \
2
3
/\
/\
4
5 6
7
*/
Node* root = new Node(1);
root->left = new Node(2);
root->right = new Node(3);
root->left->left = new Node(4);
root->left->right = new Node(5);
root->right->left = new Node(6);
root->right->right = new Node(7);
cout << "Preorder Traversal: ";
printPreOrder(root);
cout << "\nInorder Traversal: ";
printInOrder(root);
cout << "\nPostorder Traversal: ";
printPostOrder(root);
cout << endl;
// Clean up dynamic memory (Optional but good practice)
delete root->right->right;
delete root->right->left;
delete root->left->right;
delete root->left->left;
delete root->left;
delete root->right;
delete root;
return 0; }
//Binary Tree Iterative Traversal using Stack
#include <iostream>
#include <stack> using namespace std;
class Node { private:
int data;
Node* left;
Node* right;
public:
Node(int value) {
data = value;
left = NULL;
right = NULL;
}
int getData() const {
return data;
}
Node* getLeft() const {
return left;
}
Node* getRight() const {
return right;
}
void setLeft(Node* node) {
left = node;
}
void setRight(Node* node) {
right = node;
} };
// 1. Iterative Preorder Traversal (Root -> Left -> Right) /* Uses a single stack. Push the root, then in a loop: pop a node,
print it, and push its right child before its left child
(so the left gets popped/processed first).*/
void printPreOrderIterative(Node* root) {
if (root == NULL){
return;
}
stack<Node*> s;
s.push(root);
while (!s.empty()) {
Node* curr = s.top();
s.pop();
cout << curr->getData() << " ";
// Push right first so left is processed first (LIFO)
if (curr->getRight() != NULL) s.push(curr->getRight());
if (curr->getLeft() != NULL) s.push(curr->getLeft());
} }
// 2. Iterative Inorder Traversal (Left -> Root -> Right) /* Uses a single stack with a curr pointer. Keep pushing left
children onto the stack until you hit NULL, then pop, print,
and move to the right subtree.*/ void printInOrderIterative(Node* root) {
stack<Node*> s;
Node* curr = root;
while (curr != NULL || !s.empty()) {
// Go as far left as possible, pushing nodes onto the stack
while (curr != NULL) {
s.push(curr);
curr = curr->getLeft();
}
curr = s.top();
s.pop();
cout << curr->getData() << " ";
curr = curr->getRight();
} }
// 3. Iterative Postorder Traversal (Left -> Right -> Root)
// Using two stacks for simplicity /* Uses two stacks. The first stack does a modified preorder
(Root → Right → Left) and pushes each visited node onto
the second stack. Popping everything off the second stack
gives the correct postorder (Left → Right → Root).*/
void printPostOrderIterative(Node* root) {
if (root == NULL) {
return;
}
stack<Node*> s1, s2;
s1.push(root);
while (!s1.empty()) {
Node* curr = s1.top();
s1.pop();
s2.push(curr);
if (curr->getLeft() != NULL){
s1.push(curr->getLeft());
}
if (curr->getRight() != NULL){
s1.push(curr->getRight());
}
}
// s2 now has nodes in reverse postorder; pop them all to get correct order
while (!s2.empty()) {
cout << s2.top()->getData() << " ";
s2.pop();
} }
int main() {
/* Hardcoding a sample tree structure:
1
/ \
2
3
/\
/\
4
5 6
7
*/
Node* root = new Node(1);
root->setLeft(new Node(2));
root->setRight(new Node(3));
root->getLeft()->setLeft(new Node(4));
root->getLeft()->setRight(new Node(5));
root->getRight()->setLeft(new Node(6));
root->getRight()->setRight(new Node(7));
cout << "Preorder Traversal: ";
printPreOrderIterative(root);
cout << "\nInorder Traversal: ";
printInOrderIterative(root);
cout << "\nPostorder Traversal: ";
printPostOrderIterative(root);
cout << endl;
// Clean up dynamic memory
delete root->getLeft()->getLeft();
delete root->getLeft()->getRight();
delete root->getRight()->getLeft();
delete root->getRight()->getRight();
delete root->getLeft();
delete root->getRight();
delete root;
return 0; }
/*Binary Tree level-order traversal (breadth-first / BFS) using Queue*/
#include <iostream>
#include <queue> using namespace std;
class Node { private:
int data;
Node* left;
Node* right;
public:
Node(int value) {
data = value;
left = NULL;
right = NULL;
}
int getData() const {
return data;
}
Node* getLeft() const {
return left;
}
Node* getRight() const {
return right;
}
void setLeft(Node* node) {
left = node;
}
void setRight(Node* node) {
right = node;
} };
// Level Order Traversal (BFS) using a queue void printLevelOrder(Node* root) {
if (root == NULL) {
return;
}
queue<Node*> q;
q.push(root);
while (!q.empty()) {
Node* curr = q.front();
q.pop();
cout << curr->getData() << " ";
if (curr->getLeft() != NULL) {
q.push(curr->getLeft());
}
if (curr->getRight() != NULL) {
q.push(curr->getRight());
}
} }
/* Level Order Traversal printed level-by-level (on separate lines)*/ void printLevelOrderByLevel(Node* root) {
if (root == NULL) {
return;
}
queue<Node*> q;
q.push(root);
while (!q.empty()) {
int levelSize = q.size();
// number of nodes at this level
for (int i = 0; i < levelSize; i++) {
Node* curr = q.front();
q.pop();
cout << curr->getData() << " ";
if (curr->getLeft() != nullptr) {
q.push(curr->getLeft());
}
if (curr->getRight() != nullptr) {
q.push(curr->getRight());
}
}
cout << endl;
// move to next line after each level
} }
int main() {
/* Hardcoding a sample tree structure:
1
/ \
2
3
/\
/\
4
5 6
7
*/
Node* root = new Node(1);
root->setLeft(new Node(2));
root->setRight(new Node(3));
root->getLeft()->setLeft(new Node(4));
root->getLeft()->setRight(new Node(5));
root->getRight()->setLeft(new Node(6));
root->getRight()->setRight(new Node(7));
cout << "Level Order Traversal: ";
printLevelOrder(root);
cout << endl;
cout << "Level Order Traversal (by level):\n";
printLevelOrderByLevel(root);
// Clean up dynamic memory
delete root->getLeft()->getLeft();
delete root->getLeft()->getRight();
delete root->getRight()->getLeft();
delete root->getRight()->getRight();
delete root->getLeft();
delete root->getRight();
delete root;
return 0;
}
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
What is a Binary Tree? Explain root, leaf node, height, and depth.
Write an algorithm for In-order traversal of a binary tree.
Trace the Pre-order, In-order, and Post-order traversal for a given binary tree.
What is a Binary Search Tree (BST)? What is its main property?
Unit 12: Graphs
7 pages from PDFComplete content
🔥EXAM TOPIC: Graph - Adding vertices and traversal (10 Marks).
Unit-12 Graphs
🔥EXAM TOPIC: Structure, arrays and vectors (6 Marks).
Graphs are non-linear data structures that are used to represent the relationships between various objects. A graph is defined as a collection of vertices and edges.
Some of the types of Graphs are:
1. Directed and Undirected Graphs:- When in a graph all edges point from one node to another node. The graph created is known as the Directed Graph. On the other hand, when there’s a simple line between two nodes as an edge the graph constructed is known as an Undirected graph.
2. Weighted Graph:- A graph in which every edge has a weight (a value of the connection between two nodes connected by that edge).
3. Cyclic and Acyclic Graphs:- In the case of Directed Graphs if there’s a possibility of creating a path such that while traveling you end up on an already traveled node, the graph is Cyclic. And the opposite goes for the Acyclic graph.
Some terms related to Graphs:
Degree (in case of Undirected Graph): Degree means how many edges are connected to a node in case of Undirected graph.
InDegree and OutDegree(Directed Graph): Indegree means how many incoming edges are on a node in the case of a Directed graph. On the other hand, Outdegree means how many outgoing edges are from a Directed Graph node.
There are two primary ways to implement or represent graph data structures:
1. Adjacency Matrix Representation of Graph
An adjacency matrix is a square matrix (2D vector) used to represent a finite graph. It provides a straightforward way to describe the relationships between nodes (vertices) in a graph.
Create an n x n 2d vector named matrix, where n is the number of vertices, with all entries initialized to 0.
For an undirected graph, set both matrix[i][j] and matrix[j][i] to 1 if there is an edge between vertices i and j.
For a directed graph, set matrix[i][j] to 1 if there is an edge from vertex i to vertex j.
For a weighted graph, set matrix[i][j] to the weight of the edge between vertices i and j.
🔥EXAM TOPIC: Loops and break/continue statements (4 Marks). Focus on nested loops.
If there is a self-loop on vertex i, set matrix[i][i] to 1 (or the weight if weighted).
// A Graph Using Adjacency Matrix
#include <iostream>
#include <vector> using namespace std;
class Graph {
// Adjacency matrix to store graph edges
vector<vector<int> > adj_matrix;
public:
// Constructor to initialize the graph with 'n' vertices
Graph(int n){
adj_matrix = vector<vector<int> >(n, vector<int>(n, 0));
}
// Add an edge between vertices 'u' and 'v' of the graph
void add_edge(int u, int v){
// Set edge from u to v
adj_matrix[u][v] = 1;
// Set edge from v to u (for undirected graph)
adj_matrix[v][u] = 1;
}
// Print the adjacency matrix representation of the graph
void print(){
// Get the number of vertices
cout << "Adjacency Matrix for the Graph: " << endl;
int n = adj_matrix.size();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cout << adj_matrix[i][j] << " ";
}
cout << endl;
}
} };
int main(){
// Number of vertices
int n = 4;
// Create a graph with 4 vertices
Graph g(n);
// Adding the specified edges in the graph
g.add_edge(0, 1);
g.add_edge(0, 2);
g.add_edge(1, 3);
g.add_edge(2, 3);
// Print the adjacency matrix representation of the graph
g.print();
return 0; }
2. Adjacency List Representation of Graph
An Adjacency List is a common way of representing a graph as a map from vertices to lists of edges. The adjacency list representation of a graph is linked to the degree of the vertices, and hence is quite space efficient.
It only uses space proportional to the number of edges, which can be much less than the square of the number of vertices (which is the space complexity of the adjacency matrix representation).
Create a list of n elements, where n is the number of vertices.
For an undirected graph, add vertex j to the list of vertex i and add vertex i to the list of vertex j if there is an edge between i and j.
For a directed graph, add vertex j to the list of vertex i if there is an edge from vertex i to vertex j.
For a weighted graph, add a tuple (j, weight) to the list of vertex i to represent an edge from i to j with the given weight.
If there is a self-loop on vertex i, add i to the list of vertex i (or (i, weight) if weighted).
// A Graph Using Adjacency List
#include <iostream> using namespace std;
class Graph {
int numVertices;
// Total number of vertices
int matrix[10][10];
// matrix[u][v] = 1 if there is an edge between u and v
public:
// Constructor: set every cell of the matrix to 0 (no edges yet)
Graph(int n) {
numVertices = n;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
matrix[i][j] = 0;
}
}
}
// Add an edge between vertices u and v of the graph
void add_edge(int u, int v) {
matrix[u][v] = 1;
matrix[v][u] = 1;
// undirected graph, so mark both directions
}
// Print the adjacency list by scanning each row of the matrix
void print() {
cout << "Adjacency list for the Graph: " << endl;
for (int i = 0; i < numVertices; i++) {
cout << i << " -> ";
for (int j = 0; j < numVertices; j++) {
if (matrix[i][j] == 1) {
cout << j << " ";
}
}
cout << endl;
}
} };
int main() {
Graph g(3);
g.add_edge(1, 0);
g.add_edge(2, 0);
g.add_edge(1, 2);
g.print();
return 0; }
// Directional Weighted Graph Using Adjacency List
#include <iostream> using namespace std;
class Graph {
int numVertices;
// Total number of vertices
int matrix[10][10];
// matrix[u][v] = weight of edge u -> v (0 means no edge)
public:
// Constructor: set every cell of the matrix to 0 (no edges yet)
Graph(int n) {
numVertices = n;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
matrix[i][j] = 0;
}
}
}
// Add a directed edge from u to v with the given weight
void add_edge(int u, int v, int weight) {
matrix[u][v] = weight;
// only one direction, so the graph is directed
}
// Print each vertex with its outgoing edges as (destination, weight)
void print() {
cout << "Adjacency list for the Directed Weighted Graph: " << endl;
for (int i = 0; i < numVertices; i++) {
cout << i << " -> ";
for (int j = 0; j < numVertices; j++) {
if (matrix[i][j] != 0) {
cout << "(" << j << ", w=" << matrix[i][j] << ") ";
}
}
cout << endl;
}
} };
int main() {
// Create a graph object with 4 vertices (0, 1, 2, 3)
Graph g(5);
// Add directed weighted edges: from, to, weight
g.add_edge(0, 1, 5);
g.add_edge(0, 2, 3);
g.add_edge(1, 3, 2);
g.add_edge(2, 1, 1);
g.add_edge(2, 3, 7);
g.add_edge(3, 0, 8);
g.add_edge(3, 4, 2);
g.print();
return 0; }
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
What is a Graph? Differentiate between directed and undirected graphs.
Write an algorithm to represent a graph using an Adjacency List.
Explain Breadth-First Search (BFS) algorithm for graph traversal.
Explain Depth-First Search (DFS) algorithm for graph traversal.
An expression is evaluated based on the operator precedence and associativity.
Highest to Lowest Precedence Associativity () {} []
^ Right to Left * / Left to Right + - Left to Right
When there are multiple operators in an expression, they are evaluated according to their precedence and associativity. The operator with higher precedence is evaluated first and the operator with the least precedence is evaluated last.
In the infix expression as 10 + 4 * 3 / 2 (based on <operand> <operator> <operand>), there are three operators +, * and /. Among these three operators, both multiplication and division have the same higher precedence and addition has lower precedence. So, according to the operator precedence both multiplication and division are evaluated first and then the addition is evaluated. As multiplication and division have the same precedence they are evaluated based on the associativity.
Here, the associativity of multiplication and division is left to right. So, multiplication is performed first, then division and finally addition. So, the above expression is evaluated in the order of * / and +. It is evaluated as follows.
10 + 4 * 3 / 2 10 + 12 / 2
(i.e. 4 * 3 = 12) 10 + 6
(i.e. 12 / 2 = 6) 16
(i.e. 10 + 6 = 16)
The expression is evaluated to 16.
Another Example:
2 ^ 2 ^ 3 2 ^ 8
(i.e. 2 ^ 3 = 8) 256
(i.e. 2 ^ 8 = 256)
The expression is evaluated to 256.
Now, let us convert the infix expression into the prefix expression (polish notation- <operator> <operand> <operand>). For example, if we have expression as below.
// Example 1
1. Infix: (A + B) * (C * D)
Step 1: (+ A B) * (C * D)
Step 2: (+ A B) * (* C D)
Prefix: * + A B * C D
// Example 2
2. Infix: ((a + b) * c) - d
Step 1: ((+ a b) * c) - d
Step 2: (* + a b c) - d
Prefix: - * + a b c d
Now, let us convert the infix expression into the postfix expression (reverse polish notation- <operand> <operand> <operator>). For example, if we have expression as below.
// Example 1
1. Infix: (A + B) * (C * D)
Step 1: (A + B) * (C D *)
Step 2: (A B +) * (C D *)
Postfix: A B + C D * *
// Example 2
2. Infix: (p + q) * r
Step 1: (p q +) * r
Postfix: p q + r *
// Example 3
3. Infix: a / b + c / d
Step 1: a b / + c / d
Step 2: a b / + c d /
Postfix: a b / c d / +
// Example 4
4. Infix: (A + B * D) * (B - C)
Step 1: (A + B D *) * (B - C)
Step 2: (A B D * +) * (B - C)
Step 3: (A B D * +) * (B C -)
Postfix: A B D * + B C - *
Now, let us convert the postfix expression into the infix expression (<operand> <operator> <operand>). For example, if we have expression as below.
// Example 1
1. Postfix: A B D * + B C - *
Step 1: A (B * D) + B C - *
Step 2: (A + (B * D)) B C - *
Step 3: (A + (B * D)) (B - C) *
Infix: (A + (B * D)) * (B - C)
2. a b + c d + * (a + b) c d + * (a + b) (c + d) * (a + b) * (c + d)
Now, let us convert the prefix expression into the infix expression (<operand> <operator> <operand>). For example, if we have expression as below.
// Example 1
1. Prefix: / + A * B D - B C
Step 1: / + A * B D (B - C)
Step 2: / + A (B * D) (B - C)
Step 3: / (A + (B * D)) (B - C)
Infix: (A + (B * D)) / (B - C)
2. + / a b / c d + / a b (c / d) + (a / b) (c / d)
(a / b) + (c / d)
🎯 Expected Exam Questions (TCA-2)
Based on the TCA-2 syllabus and mock exam, make sure you can answer these specific questions before the exam:
Convert the infix expression A+B*C to postfix using a stack.
Convert the infix expression (A+B)*(C-D) to prefix.
What are infix, prefix, and postfix expressions?
Write an algorithm to evaluate a postfix expression.
TCA-2 Mock Exam & Solutions
Exam Preparation100 Marks Total
This section contains the TCA-2 Mock Exam questions along with complete, highly-detailed C++ code solutions and explanations.
Q1. Operator Overloading (12 Marks)
Question: Write a program to demonstrate the "-" operator overloading using a member function.
Answer: We can demonstrate this by creating a Point class that overrides the - operator to subtract coordinates.
#include <iostream>
using namespace std;
class Point {
private:
int x, y;
public:
Point(int r = 0, int i = 0) {
x = r;
y = i;
}
// Overloading the '-' operator
Point operator-(const Point& obj) {
Point res;
res.x = x - obj.x;
res.y = y - obj.y;
return res;
}
void display() {
cout << "Point(" << x << ", " << y << ")" << endl;
}
};
int main() {
Point p1(10, 5), p2(2, 4);
// Subtraction using overloaded operator
Point p3 = p1 - p2;
cout << "P1: "; p1.display();
cout << "P2: "; p2.display();
cout << "Result (P1 - P2): "; p3.display();
return 0;
}
Q2. Access Specifiers (12 Marks)
Question: What is the difference between private and protected access specifiers? Explain with a supporting code block.
Answer: • Private: Members declared as private can only be accessed by functions inside the same class. They are NOT accessible in derived classes.
• Protected: Members declared as protected can be accessed inside the same class AND in any derived (child) classes.
#include <iostream>
using namespace std;
class Base {
private:
int privateVar = 10;
protected:
int protectedVar = 20;
};
class Derived : public Base {
public:
void show() {
// cout << privateVar; // ERROR: Not accessible
cout << "Protected Variable accessible in derived class: " << protectedVar << endl;
}
};
int main() {
Derived obj;
obj.show();
// cout << obj.protectedVar; // ERROR: Not accessible outside the class hierarchy
return 0;
}
Q3. Function Overloading (12 Marks)
Question: Write a program to demonstrate function overloading.
#include <iostream>
using namespace std;
class MathOperations {
public:
// Function with two integer parameters
int add(int a, int b) {
return a + b;
}
// Function with three integer parameters
int add(int a, int b, int c) {
return a + b + c;
}
// Function with double parameters
double add(double a, double b) {
return a + b;
}
};
int main() {
MathOperations math;
cout << "Add 2 ints: " << math.add(10, 20) << endl;
cout << "Add 3 ints: " << math.add(10, 20, 30) << endl;
cout << "Add 2 doubles: " << math.add(5.5, 4.2) << endl;
return 0;
}
Q4. Template Function (12 Marks)
Question: Write a template function for int sum(int a, int b). Also write a main function to demonstrate how a template function works with int, double, and character-type input parameters.
#include <iostream>
using namespace std;
// Template function
template <typename T>
T sum(T a, T b) {
return a + b;
}
int main() {
// With int
cout << "Sum of integers (5, 10): " << sum<int>(5, 10) << endl;
// With double
cout << "Sum of doubles (3.5, 2.1): " << sum<double>(3.5, 2.1) << endl;
// With character (Adds ASCII values)
cout << "Sum of characters ('A', 1): " << sum<char>('A', 1) << endl;
// 'A' is 65, + 1 = 66 ('B')
return 0;
}
Q5. Custom Queue (12 Marks)
Question: Write a custom queue program that contains enqueue, dequeue, and display functions. [4+4+4]
#include <iostream>
#define SIZE 5
using namespace std;
class Queue {
private:
int items[SIZE], front, rear;
public:
Queue() {
front = -1;
rear = -1;
}
// Enqueue function
void enqueue(int element) {
if (rear == SIZE - 1) {
cout << "Queue is full" << endl;
} else {
if (front == -1) front = 0;
rear++;
items[rear] = element;
cout << "Inserted " << element << endl;
}
}
// Dequeue function
void dequeue() {
if (front == -1 || front > rear) {
cout << "Queue is empty" << endl;
} else {
cout << "Deleted " << items[front] << endl;
front++;
}
}
// Display function
void display() {
if (front == -1 || front > rear) {
cout << "Queue is empty" << endl;
} else {
cout << "Queue elements: ";
for (int i = front; i <= rear; i++) {
cout << items[i] << " ";
}
cout << endl;
}
}
};
int main() {
Queue q;
q.enqueue(10);
q.enqueue(20);
q.display();
q.dequeue();
q.display();
return 0;
}
Q6. Infix to Postfix Algorithm & Conversion (12 Marks)
Question: Write an algorithm to convert an infix expression to a postfix expression. Convert A+((B+C)*(E-F)-G)/(H-I) infix expression to a postfix expression.
Algorithm: 1. Scan the infix expression from left to right.
2. If the scanned character is an operand, output it.
3. If it is an operator, pop operators from the stack to output until the stack is empty or a lower precedence operator is found, then push the scanned operator.
4. If it is a '(', push it to the stack.
5. If it is a ')', pop and output from the stack until '(' is encountered.
6. Repeat until all characters are scanned, then pop and output all remaining operators.
Conversion of A+((B+C)*(E-F)-G)/(H-I): 1. A is output. Stack: empty. Output: A 2. + is pushed. Stack: + 3. ( is pushed. Stack: + ( 4. ( is pushed. Stack: + ( ( 5. B is output. Output: A B 6. + is pushed. Stack: + ( ( + 7. C is output. Output: A B C 8. ) pops until (. Output: A B C +. Stack: + ( 9. * is pushed. Stack: + ( * 10. ( is pushed. Stack: + ( * ( 11. E is output. Output: A B C + E 12. - is pushed. Stack: + ( * ( - 13. F is output. Output: A B C + E F 14. ) pops until (. Output: A B C + E F -. Stack: + ( * 15. - is pushed (pops * because precedence). Output: A B C + E F - *. Stack: + ( - 16. G is output. Output: A B C + E F - * G 17. ) pops until (. Output: A B C + E F - * G -. Stack: + 18. / is pushed. Stack: + / 19. ( is pushed. Stack: + / ( 20. H is output. Output: A B C + E F - * G - H 21. - is pushed. Stack: + / ( - 22. I is output. Output: A B C + E F - * G - H I 23. ) pops until (. Output: A B C + E F - * G - H I -. Stack: + / 24. End of string, pop remaining. Output: A B C + E F - * G - H I - / +
Q7. Binary Tree Traversal (12 Marks)
Question: Complete the functions displayInOrder, displayPreOrder, and displayPostOrder for MyBinaryTree.
void displayInOrder() {
if (leftNode != nullptr) leftNode->displayInOrder();
cout << data << " ";
if (rightNode != nullptr) rightNode->displayInOrder();
}
void displayPreOrder() {
cout << data << " ";
if (leftNode != nullptr) leftNode->displayPreOrder();
if (rightNode != nullptr) rightNode->displayPreOrder();
}
void displayPostOrder() {
if (leftNode != nullptr) leftNode->displayPostOrder();
if (rightNode != nullptr) rightNode->displayPostOrder();
cout << data << " ";
}
Q8. Graph Adjacency List (12 Marks)
Question: Write an algorithm to represent a graph as an adjacency list and make the adjacency list of this graph.
Algorithm: 1. Create an array (or vector) of linked lists (or vectors) of size V, where V is the number of vertices.
2. For every directed edge from vertex U to vertex V, add V to the list representing U's neighbors.
3. To traverse or display, loop through each vertex from 0 to V-1 and print all elements in its linked list.
Adjacency List for the given graph: Based on the nodes (A, B, C, D, E, F, G) and visible arrows:
• A → [C]
• B → [C, F]
• C → [F, G]
• D → [A]
• E → [A, C]
• F → []
• G → [B]
Q9. Tree Properties (4 Marks)
Question: What are the node size, leaf node size, height, and width of the following tree?
Answer: • Node Size: 8 (Total number of nodes: R, A, B, C, D, E, F, G)
• Leaf Node Size: 4 (Nodes with no children: C, D, E, G)
• Height: 4 (Longest path from root to leaf: R -> B -> F -> G. Counted by levels: 1, 2, 3, 4)
• Width: 4 (The maximum number of nodes at any level. Level 3 has 4 nodes: C, D, E, F)