0% found this document useful (0 votes)
6 views

C++ From Beginner to Professional by Example (1)

The document is a comprehensive guide to C++, covering fundamental concepts such as object-oriented programming, variables, data types, and functions. It explains key programming principles like data abstraction, encapsulation, inheritance, and polymorphism, alongside practical details on compiling programs and using header files. Additionally, it details variable declaration, initialization, data types, and the use of functions in C++.

Uploaded by

cragnolinitomas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views

C++ From Beginner to Professional by Example (1)

The document is a comprehensive guide to C++, covering fundamental concepts such as object-oriented programming, variables, data types, and functions. It explains key programming principles like data abstraction, encapsulation, inheritance, and polymorphism, alongside practical details on compiling programs and using header files. Additionally, it details variable declaration, initialization, data types, and the use of functions in C++.

Uploaded by

cragnolinitomas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 410

C++ complete guide

TOMÁS CRAGNOLINI
INTRODUCTION

C++

C++ supports the concepts of object-oriented programming (or OOP for short),
which are:

1. Data abstraction, that is, the creation of classes to describe objects

2. Data encapsulation for controlled access to object data

3. Inheritance by creating derived classes (including multiple derived classes)

4. Polymorphism , that is, the implementation of instructions


that can have varying effects during program execution.
Compiling a Program
1. Using Header Files

Header files are text files containing declarations and macros. By using an #include
directive these declarations and macros can be made available to any other source file, even
in other header files.
Pay attention to the following points when using header files:

● Header files should generally be included at the start of a program before any
other declarations.

● You can only name one header file per #include directive.

● The file name must be enclosed in angled brackets < ... > or double quotes.

" ... ".


1.2 Searching for Header Files

The header files that accompany your compiler will tend to be stored in a folder of their own
—normally called include. If the name of the header file is enclosed by angled brackets < ...
>, it is common to search for header files in the include folder only.

The current directory is not searched to increase the speed when searching for header files.

C++ programmers commonly write their own header files and store them in the current
project folder. To enable the compiler to find these header files, the #include
directive must state the name of the header files in double quotes.

Example: #include "project.h"

The compiler will then also search the current folder. The file suffix .h is normally used for
user-defined header files.

1.3 Standard Class Definitions

In addition to standard function prototypes, the header files also contain standard class
definitions. When a header file is included, the classes defined and any objects declared in
the file are available to the program.

Example:

#include <iostream> // is composed from the ostream and istream


standard classes

using namespace std;

The classes istream and ostream can be used with the cin and cout streams. cin is an object
of the istream class and cout an object of the ostream class.
C++ also includes the C programming language header files

CHAPTER 1

1. Variables

A variable is a data structure that allows storing different kinds of values . Variables are the
names you give to a specific computer's memory location where values are stored. You get
access to the value stored by using variable’s names.
Picture this like a little box where you want to store something and you label that box in order
to know what it contains.
As for a real world example, imagine you have a box where you want to store books of their
genre and you also want to know how many are .
You’d possibly follow this approach: you'd get a box and stick a label on it with the following
writing .

books of fiction
20

When you take that box for whatever purpose you know it contains 20 books according to
the genre “ fiction “ .

Let’s take that example and try to make it computer’s related :

// this is a variable called BooksOfFictionBox (represents the box labeled books of fiction)
which stores( represented by the equal sign “=” ) 20 fictional books (quantity of book items)

BooksOfFictionBoxNumber= 20;

Let’s go through this example and make it work following a C syntax program :

A variable declaration and definition in C assumes the following syntax:

1) Type : The type relates to the kind of value you want to store:In the example
20 is used to indicate the number of books. This means numbers are
considered a value type .

2) Variable name: It’s just a way to give a name that best describes what the
value what’s stored . In the books variable you use the name
BooksOfFictionBoxNumber to denote that you’re going to store the number of
books of that genre.

3) Value: The value itself is followed by a semicolon . In the example 20 is


assigned .

2. Basic Data Types


Each basic data type is explained in the table below.

I. Data Types Examples :

In the example each data type is followed by a variable’s name and the value stored.

char data type : char g = ‘g’;


int data type: int intNum = 1;
float data type: float floatNum= 12.236;
double data type : double doubleNum=17,723;

II. More Specific Data Types

In the table below you’ll see included some of the basic types with some modifications , the
value range (numbers that can be stored ) and the printf format specifier. I’ll just give an
explanation of the types not discussed beforehand.
III. Data types Modifiers

If you pay attention there are some new keywords before the basic types:

a) short
b) long
c) unsigned/signed

These are called modifiers just like the const keyword but they affect the value range of the
basic types when used in combination.

a) Long and Short

1) Short reduces the range capacity in a half. So when you declare a variable
using the short modifier it takes half the storage capacity that a normal int:

● A normal int takes 4 bytes of memory storage capacity.A short int takes 2 bytes of
memory storage capacity.

b) Long doubles the int memory storage capacity : An int data type takes 4 bytes of
memory storage.A long int takes 8.

c) Signed and Unsigned

1) signed means that the value range can include negative and positive numbers
including 0.
2) unsigned means that the value range can only include positive values including 0.
types by default are signed .They do not affect the memory storage capacity but it changes
the value range these variables can contain.

IV. Format Specifiers

Format specifiers are placeholders that are replaced by the argument values supplied in the
printf() function between others.
In the format string section you add the format specifiers and in the arguments section you
add the values you want to printf(). This function displays the arguments provided on the
screen.
Each placeholder points to the corresponding argument in the exact same order they’re
included.

V. Format Specifier List

The format specifiers are detailed below:

%d : it is used for displaying integers and short integers


%f : it’s used to display float and double types
%c: it displays a single character
%u: it displays unsigned characters, unsigned int and unsigned short int.
%lu: it displays unsigned long int types
%ld: it is used for displaying long int types

Example :

#include <stdio.h>

int main () {

long int lampsQuantity =14;


// the function takes the lampsQuantity variable , reads its value
and displays it using the %ld placeholder

printf(“%ld”, lampsQuantity);

return 0;

VI. The sizeof Operator

The amount of memory needed to store an object of a certain type can be ascertained
using the sizeof operator:

sizeof(name)

It yields the size of an object in bytes, and the parameter name indicates the object type or
the object itself. This means if you store a value of type int the computer will allocate 2 or
4 bytes according to the operating system architecture (64 or 32 bytes)

For example, sizeof (int) represents a value of 2 or 4 depending on


the machine.

The following graphic describes the size in bytes for each data type. Notice that the graphics
do not always include each of them.

VII. Understanding Each Type of Data


Each data type can be categorized as an integral or floating point type :

1. Integral types refer to integer types.

2. Floating-point types refer to decimal types.

CHAPTER 2

DATA STRUCTURES

1. More about Variables

I. Declaration :

The first step to create a variable is referred to as variable declaration.


When declaring a variable no value gets stored you’re only telling the compiler to save some
memory space that you are going to use later in your program.
Syntax:

Data type + Given name ;

Examples:

// declaration there’s the data type (int) followed by the


variable name(clients). no value stored.

int clients;

II. ASSIGNMENT/INITIALIZATION

It’s how you get a value stored in an existing declared variable.

The following graphic details the different ways you can use to proceed with value
initialization.
a) Equal Operator Initialization

data type + variable name = given value;

Example: int number = 1;

b) Constructor Initialization:

type variable_name (value);

Example: int number(1);

c) Brace Initialization :

type variable_name {value};

Example: int number{1}

Despite the fact that there are many ways to initialize a variable not every syntax
works for the different operations you might need to carry out.
it is highly recommended to stick to the brace and equal initialization methods.

2. VARIABLES AND DATA TYPES

I. Integer Types

There are four integer types you can use depending on the range value you want to store:

Modifiers:

You can supply suffixes to an integer literal to specify its type (suffixes are case insensitive,
so you can choose the style you like best):

• The unsigned suffix u or U


• The long suffix l or L
• The long long suffix ll or LL

By literals, it is meant data that have not been assigned to any variable .
For Example :

This provides major security when it comes to types.

a) 3L (long integer literal )


b) Long int = 8L ;

You can combine the unsigned suffix with either the long or the long
long suffix to specify signed-ness and size. The Table shows the possible types
that a suffix combination can take. Allowed types are shown with a check
mark (). For binary, octal, and hexadecimal literals, you can omit the u
or U suffix. These are depicted with an asterisk (*).

If you want to enforce guaranteed integer sizes, you can use integer types in the
<cstdint> library.

The Microsoft C++ compiler features five sized integer types. These types start with
int followed by the number of bits you want the integer to hold – either 8, 16, 32 or 64 bits.

__int8 myInt8 = 0; // 8 bits


__int16 myInt16 = 0; // 16 bits
__int32 myInt32 = 0; // 32 bits
__int64 myInt64 = 0; // 64 bits

II. Floating-point Types

The floating-point types can store real numbers with different levels of precision.
This includes float and double types.
float myFloat = 3.14; // 3.4E +/- 38 (7 digits)
double myDouble = 3.14; // 1.7E +/- 308 (15 digits)long double
long double num= 3.14; // same as double

As with all types, floating-point types take up a finite amount of memory,which is called the
type’s precision. The more precision a floating-point type has, the more accurate it will be at
approximating a real number. C++ offers three levels of precision for approximations:

1. float : single precision


2. double :double precision
3. long double :extended precision

Floating-point literals are double precision by default. If you need single precision,
use an f or F suffix; for extended precision, use l or L, as shown here:

float a = 0.1F; // single precision


double b = 0.2; //double precision by default
long double c = 0.3L; // extended precision

III. Char Types

The char type is commonly used to represent ASCII characters.


Character types store human language data. The six character types are:

1. char is the default type, and takes 1 byte of memory . May or


may not be signed.

2. char16_t : Used for 2-byte character sets.

3. char32_t : Used for 4-byte character sets.

4. signed char: Same as char but guaranteed to be signed.

5. unsigned char: Same as char but guaranteed to be unsigned.

6. wchar_t: Largest character of the implementation’s locale.

The conversion between the number stored in the char and the character shown
when the char is printed occurs automatically.

char c = 'x'; // assigns 120 (ASCII for 'x')


std::cout << c; // prints 'x'

IV. Boolean Types

The bool type can store a Boolean value, which is a value that can only be either true or
false. These values are specified with the true and false keywords.You need to include
_Bool if you want to use those values. Otherwise you have to use 0 to indicate false and 1 to
Indicate true .

bool b = false; // true or false value


int b = 1 // this evaluates to false

V. Explicit Type Conversion

It is possible to convert the type of an expression explicitly using the cast operator
(type).

Syntax: (type) expression

This converts the value of an expression to the given type. Explicit type conversion is
also known as casting.

The cast operator (type) is a unary operator and thus has a higher precedence than
the arithmetic operators.

Example:

int a = 1, b = 4;
double x;
x = (double)a/b;

VI. The size_t Type

You use the size_t type, also available in the <cstddef> header, to encode size
of objects. The size_t objects guarantee that their maximum values are sufficient
to represent the maximum size in bytes of all objects.
Technically, this means a size_t could take 2 bytes or 200 bytes depending on the
implementation.
In practice, it’s usually identical to an unsigned long long on 64-bit
architectures.
NOTE

The type size_t is a C type in the <cstddef>.

VII. Const Variables

You can add the const prefix before any data type declaration.Although you must define the
variable straight away , otherwise the compiler will throw an error.

const bool isOn= false;

What’s achieved by the const keyword is that you are not allowed to modify the value
assigned later in the program. the next example will throw an error 👍

Example :

const bool isOn= false;

isOn=true

This reassignment won’t produce effects and the compiler will warn you that you're trying to
modify a const variable.

VIII. Auto Type Deduction

In almost all situations, the compiler can determine the correct type of an
object using the initialization value. This assignment contains redundant
information:

int answer = 42;

The compiler knows answer is an int because 42 is an int.

You can use auto instead:

auto the_answer { 42 }; // int


auto foot { 12L }; // long
auto rootbeer { 5.0F }; // float
auto cheeseburger { 10.0 }; // double
auto politifact_claims { false }; // bool
auto cheese { "string" }; // char[7]
CHAPTER 3

1. Introduction To Loops , Functions and Arrays

1. Functions
A function is a sequence of program instructions that performs a specific task, packaged as
a unit.
Just like variables you need to declare and define them but there’s also a further step which
is referred to as calling.

I. Declaration

In a function declaration, we must provide the function name, its return type, the number and
the parameters type . A function declaration tells the compiler that there is a function
declared somewhere in the program.

return_type + name_of_the_function (parameter_1, parameter_2)


{... statements } ;

The parameter name is not mandatory while declaring functions. We can also declare the
function without using any of them. Parameters are variables that can only be accessed
inside the function and expect the user to provide the specified value types called
arguments.
II. Function Definition

The function definition consists of actual statements which are executed when the function is
called (i.e. when the program control comes to the function).
A C/C ++ function is generally defined and declared in a single step because it always starts
with the function declaration so we do not need to declare it explicitly. The below example
serves as both a function definition and a declaration.

III. Function Call

A function call is a statement that instructs the compiler to execute the function. We use the
function name and parameters in the function call.
In the below example, the first sum function is called and 10,30 are passed to the sum
function. After the function call sum of a and b is returned and control is also returned back
to the main function of the program.

Function call is necessary to bring the program control to the function definition. If not
called, the function statements will not be executed.
// Calling sum function and storing its value in add variable

int main {
int add = sum(10, 30);
printf("Sum is: %d", add);
return 0;
}

Output
Sum is: 40
As we noticed, we have not used explicit function declaration. We simply defined and called
the function.

IV. Function with Arguments

Function Arguments are the data that is passed to a function that is required by the
parameter types. This is shown in the previous example. The sum function is passed two int
arguments (values) to perform the calculation.
Example:
int function_name(int var1, int var2); //these are the parameters
sum (10, 30); // these are the arguments
V. Function Return Type

Function return type tells what type of value is returned after all the function´s statements
have finished being executed. When we don’t want to return a value, we can use the void
data type.
Example:
int func (parameter_1,parameter_2);
The above function will return an integer value after running statements inside the function.
Note: Only one value can be returned from a C function. To return multiple values, we have
to use pointers or structures.

Conditions of Return Types and Arguments

In C programming language, functions can be called either with or without arguments and
might return values. They may or might not return values to the calling functions.

1. Function with no arguments and no return value

2. Function with no arguments and with return value

3. Function with argument and with no return value

4. Function with arguments and with return value

How Does C Function Work?

Working of the C function can be broken into the following steps as mentioned below:

1. Declaring a function: Declaring a function is a step where we declare a function.


Here we define the return types and parameters of the function.

2. Defining a function: declaring its statements. the code that’s going to be


executed when it is called .

3. Calling the function: Calling the function is a step where we call the function by
passing the arguments in the function.

4. Executing the function: Executing the function is a step where we can run all
the statements inside the function to get the final result.

5. Returning a value: Returning a value is the step where the calculated value after
the execution of the function is returned. Exiting the function is the final step
where all the allocated memory to the variables, functions, etc is destroyed
before giving full control to the main function.

Example 1 :

// C program to show function


// call and definition

#include <stdio.h>

// Function that takes two parameters


// a and b as inputs and returns their sum

int sum(int a, int b){return a + b;}

// Driver code

int main()
{

// Calling sum function and


// storing its value in add variable

int add = sum(10, 30);

printf("Sum is: %d", add);

return 0; }

Output

Sum is: 40

As we noticed, we have not used explicit function declaration. We


simply defined and called the function.

VI. Types of Functions

There are two types of functions in C:

a) Library Functions
b) User Defined Functions

a) Library Function

A library function is also referred to as a “built-in function”. Built-in functions have the
advantage of being directly usable without being defined, whereas user-defined functions
must be declared and defined before being used.

#include <math.h> // this is a header file . it has the predefined


math functions that will be used

#include <stdio.h>// this is another header file. The printf()


function is defined in this file.

// Driver code

int main() {
double Number;
Number =100;

// Computing the square root withthe help of predefined C


// library function

double squareRoot = sqrt(Number);


printf("The Square root of %.2lf = %.2lf", Number, squareRoot);
return 0;
}

For Example:

pow(), sqrt(), strcmp(), strcpy() etc.


b) User Defined Function

Functions that the programmer creates are known as User-Defined functions or “tailor-
made functions”. User-defined functions can be improved and modified according to the
need of the programmer.

Example:

// C program to show user-defined functions

#include <stdio.h>

int sum(int a, int b){ return a + b; }

// Driver code

int main(){

int a = 50, b = 50;

// function call

int res = sum(a, b);

printf("Sum is: %d", res);

return 0; }

2. LOOPS

Loops in programming are used to repeat a block of code until the specified condition is met.
A loop statement allows programmers to execute a statement or group of statements
multiple times without repetition of code.

for(int i = 0; i < n; ++i){


printf("will print if loop still running /n");};

a) Initialization Expression: In this expression, we assign a loop variable or


loop counter to some value. for example: int i=0;
b) Test Expression: In this expression, test conditions are performed. If the
condition evaluates to true then the loop body will be executed and then an
update of the loop variable is done. If the test expression becomes false then
the control will exit from the loop. for example, i<=9;

c) Update Expression: After execution of the loop body the loop variable is
updated by some value; it could be incremented, decremented, multiplied, or
divided by any value.

C program to illustrate the need of loops.

The first program shows how you must proceed to print text several times. You’d need to
write as many printf() functions as you need to have the text printed the times you
want.

#include <stdio.h>

int main(){

printf( "Hello World\n");


printf( "Hello World\n");
printf( "Hello World\n");
printf( "Hello World\n");
printf( "Hello World\n");
return 0;
}

Loops simplify this process, saving you all that typing.

// C program to illustrate for loop

#include <stdio.h>

int main(){

int i = 0;

for (i = 1; i <= 10; i++){ printf( "Hello World\n");};

return 0; };
3. Introducing Arrays

An array in C is a fixed-size collection of similar data items stored in contiguous memory


locations. It can be used to store the collection of primitive data types such as int, char, float,
etc., and also derived and user-defined data types such as pointers, structures, etc.

I. C Array Declaration

In C, we have to declare the array like any other variable before using it. We can declare an
array by specifying its name, the type of its elements, and the size of its dimensions. When
we declare an array in C, the compiler allocates the memory block of the specified size to
the array name.

Syntax of Array Declaration

data_type array_name [size];

or

data_type array_name [size1] [size2]...[sizeN];

where N is the number of dimensions.


Example :

// C Program to illustrate the array declaration

#include <stdio.h>

int main() {

// declaring array of integers

int arr_int[3];

// declaring array of characters

char arr_char[95];

return 0;
}

II. C Array Initialization

Initialization in C is the process to assign some initial value to the variable. When the
array is declared or allocated memory, the elements of the array contain some
garbage value. So, we need to initialize the array to some meaningful value. There
are multiple ways in which we can initialize an array in C..

a) Array Initialization with Declaration

In this method, we initialize the array along with its declaration. We use an initializer
list to initialize multiple elements of the array. An initializer list is the list of values
enclosed within braces { } separated by comma.

data_type array_name [size] = {value1, value2, ...


valueN};
b) Array Initialization without Size Declaration

If we initialize an array using an initializer list, we can skip declaring the size of the
array as the compiler can automatically deduce the size of the array in these cases.
The size of the array is equal to the number of elements present in the initializer list
as the compiler can automatically deduce the size of the array.

//The size of the above arrays is 5 which is automatically


deduced by the compiler.

data_type array_name[] = {1,2,3,4,5};

c) Array Initialization after Declaration (Using Loops)

We initialize the array after the declaration by assigning the initial value to each
element individually. We can use for loop, while loop, or do-while loop to assign the
value to each element of the array.

for (int i = 0; i < N; i++) {

array_name[i] = value i;
}

Example :

// C Program to demonstrate array initialization

#include <stdio.h>
int main(){

// array initialization using initializer list

int arr[5] = { 10, 20, 30, 40, 50 };

// array initialization using initializer list without


// specifying size

int arr1[] = { 1, 2, 3, 4, 5 };

// array initialization using for loop

float arr2[5];

for (int i = 0; i < 5; i++) {

arr2[i] = (float)i * 2.1; }

return 0;
}

III. Access Array Elements

We can access any element of an array in C using the array subscript operator [ ]
and the index value i of the element.

array_name [index];

One thing to note is that the indexing in the array always starts with 0, i.e., the first
element is at index 0 and the last element is at N – 1 where N is the number of
elements in the array.
Program Description :

// C Program to illustrate element access using array

#include <stdio.h>

int main()
{

// array declaration and initialization

int arr[5] = { 15, 25, 35, 45, 55 };

// accessing element at index 2 i.e 3rd element

printf("Element at arr[2]: %d\n", arr[2]);

// accessing element at index 4 i.e last element

printf("Element at arr[4]: %d\n", arr[4]);

// accessing element at index 0 i.e first element

printf("Element at arr[0]: %d", arr[0]);

return 0;
}

Output

Element at arr[2]: 35
Element at arr[4]: 55
Element at arr[0]: 15

IV. Update Array Element

We can update the value of an element at the given index i in a similar way to accessing
an element by using the array subscript operator[] and assignment operator =.

array_name[i] = new_value;
V. Length of Array in C

it can be determined by using the methods mentioned below:

a) Using sizeof() Operator

It returns an integer value that represents the size of the expression or a variable in bytes.
The sizeof operator is primarily used for dynamic memory allocation but it can also be used
to find the length of an array.

Syntax:

data_type size = sizeof(Array_name) / sizeof(Array_name[index]);

In the above syntax,

● data_type: It is the type of variable in which we want to store the length of the
array.(like int, size_t, etc.).

● Array_name: It is the name of the array you want to find the size of.

● sizeof(Array_name): It is an operator that returns the size of the entire array in


bytes.

● sizeof(Array_name[index]): It returns the size of a single element in the array in


bytes.

● index: It is the index of any element in the array.

Example:

// C Program to calculate size of an array using sizeof()


// operator

#include <stdio.h>

int main(){

int Arr[] = { 1, 2, 3, 4, 5 };

// variable to store size of Arr


int length = sizeof(Arr) / sizeof(Arr[0]);

printf("The length of the array is: %d\n", length);

return 0; }

Output

The length of the array is: 5

b) Using Loop

The loop method is used to calculate the length of an array in C. It iterates through all the
elements of an array and increments the count.

Example:

// C Program to calculate size of an array using loop

#include <stdio.h>

int arr_length(int arr[]) {

int i;
int count = 0;

for(i=0; arr[i]!='\0'; i++){

count++;
}

return count; }

int main() {

int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 15};


int n;

n = arr_length(arr);

printf("Length of Array is: %d", n);

return 0;
}

Output

Length of Array is: 10

Note: Please noteThe iterative methods of finding the length of the strings (array of
characters) also cannot be applied to the array of other types as there is no end indicator in
these array types in contrast to the ‘\0’ NULL character marking the end of the string. that
these methods only works when the array is declared in the same scope. These methods
will fail if we try them on an array which is passed as a pointer. This happens due to Array
Decay

VI. C ++ Array Traversal

Traversal is the process in which we visit every element of the data structure. For C array
traversal, we use loops to iterate through each element of the array.

a) Array Traversal using for Loop

for (int i = 0; i < N; i++) {

array_name[i];

Program Description:
// C Program to demonstrate the use of array

#include <stdio.h>

int main(){

// array declaration and initialization

int arr[5] = { 10, 20, 30, 40, 50 };

// modifying element at index 2

arr[2] = 100;

// traversing array using for loop

printf("Elements in Array: ");

for (int i = 0; i < 5; i++) {

printf("%d ", arr[i]);


}

return 0;
}

Output

Elements in Array: 10 20 100 40 50

VII. Types of Array in C

There are two types of arrays based on the number of dimensions it has. They are as
follows:

a) One Dimensional Array in C

The One-dimensional arrays, also known as 1-D arrays in C are those arrays that
have only one dimension.

Syntax of 1D Array in C:

array_name [size];
Program Description:

// C Program to illustrate the use of 1D array


#include <stdio.h>

int main()
{

// 1d array declaration
int arr[5];

// 1d array initialization using for loop


for (int i = 0; i < 5; i++) {
arr[i] = i * i - 2 * i + 1;
}

printf("Elements of Array: ");


// printing 1d array by traversing using for loop
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}

return 0;
}

Output:

Elements of Array: 1 0 1 4 9

b) Multidimensional Array in C
Multi-dimensional Arrays in C are those arrays that have more than one dimension.
Some of the popular multidimensional arrays are 2D arrays and 3D arrays. We can
declare arrays with more dimensions than 3d arrays but they are avoided as they get
very complex and occupy a large amount of space.

● Two-Dimensional Array in C

A Two-Dimensional array or 2D array in C is an array that has exactly two


dimensions. They can be visualized in the form of rows and columns organized in a
two-dimensional plane.

Syntax of 2D Array in C
array_name[size1] [size2];

Here,

size1: Size of the first dimension.


size2: Size of the second dimension.

Program description:

// C Program to illustrate 2d array


#include <stdio.h>

int main() {

// declaring and initializing 2d array

int arr[2][3] = { 10, 20, 30, 40, 50, 60 };

printf("2D Array:\n");
// printing 2d array

for (int i = 0; i < 2; i++) {

for (int j = 0; j < 3; j++) {

printf("%d ",arr[i][j]); }

printf("\n"); }

return 0; }

Output

2D Array:

10 20 30
40 50 60

● Three-Dimensional Array in C

Another popular form of a multi-dimensional array is Three Dimensional Array or 3D


Array. A 3D array has exactly three dimensions. It can be visualized as a collection of
2D arrays stacked on top of each other to create the third dimension.

Syntax of 3D Array in C:

array_name [size1] [size2] [size3];


Program Description :

// C Program to illustrate the 3d array


#include <stdio.h>

int main() {

// 3D array declaration

int arr[2][2][2] = { 10, 20, 30, 40, 50, 60 };

// printing elements

for (int i = 0; i < 2; i++) {

for (int j = 0; j < 2; j++) {

for (int k = 0; k < 2; k++) {

printf("%d ", arr[i][j][k]);


}

printf("\n");
}

printf("\n \n");
}

return 0; } ;

Output

10 20
30 40
50 60
0 0

CHAPTER 4

1. C-STYLE STRINGS

Strings are contiguous blocks of characters. A C-style string or null-terminated


string has a zero-byte appended to its end (a null) to indicate the end of
the string. Because array elements are contiguous, you can store strings in
arrays of character types.

char english[] = "I love movies";

this is the same as :

char english[] ={I ,l ,o, v, e, m, o, v, i, e, s,/0}

A String in C programming is a sequence of characters terminated with a null character ‘\0’.


The C String is stored as an array of characters. The difference between a character array
and a C string is that the string is terminated with a unique character ‘\0’.

I. C String Declaration Syntax

Declaring a string in C is as simple as declaring a one-dimensional array. Below is the basic


syntax for declaring a string.
char string_name[size];
In the above syntax str_name is any name given to the string variable and size is used to
define the length of the string, i.e the number of characters strings will store.
There is an extra terminating character which is the Null character (‘\0’) used to indicate the
termination of a string that differs strings from normal character arrays.

II. Ways to Initialize a String in C

We can initialize a C string in 4 different ways which are as follows:

● Assigning a string literal without size

String literals can be assigned without size. Here, the name of the string str acts as a pointer
because it is an array.
char str[] = "learning";

● Assigning a string literal with a predefined size


String literals can be assigned with a predefined size. But we should always account for one
extra space which will be assigned to the null character. If we want to store a string of size n
then we should always declare a string with a size equal to or greater than n+1.

char str[50] = "learning ";

● Assigning character by character with size

We can also assign a string character by character. But we should remember to set the end
character as ‘\0’ which is a null character.

char str[9] = { 'L','e','a','r','n','i','n','g','\0'};

● Assigning character by character without size

We can assign character by character without size with the NULL character at the end. The
size of the string is determined by the compiler automatically.

char str[] = { 'L','e','a','r','n','i','n','g','\0'};

Note: When a Sequence of characters enclosed in the double quotation marks is


encountered by the compiler, a null character ‘\0’ is appended at the end of the string by
default.

Note: After declaration, if we want to assign some other text to the string, we have to
assign it one by one or use built-in strcpy() function because the direct assignment of
string literal to character array is only possible in declaration.
Example :

// C program to illustrate strings

#include <stdio.h>
#include <string.h>

int main()
{
// declare and initialize string

char str[] = "learning";

// print string

printf("%s\n", str);

int length = 0;

length = strlen(str);

// displaying the length of string

printf("Length of string str is %d", length);

return 0;
}

Output

learning
Length of string str is 5

We can see in the above program that strings can be printed using normal printf statements
just like we print any other variable. Unlike arrays, we do not need to print a string, character
by character.
Note: The C language does not provide an inbuilt data type for strings but it has an access
specifier “%s” which can be used to print and read strings directly.

III. USER INPUT


The following example demonstrates how to take string input using scanf() function in C

// C program to read string from user

#include <stdio.h>

int main()
{
// declaring string

char str[50];

// reading string

scanf("%s",str);

// print string

printf("%s",str);

return 0;
}

Input
learning
Output
learning

You can see in the above program that the string can also be read using a single scanf
statement. Also, you might be wondering why we have not used the ‘&’ sign with the string
name ‘str’ in the scanf statement! To understand this you will have to recall your
knowledge of scanf.
We know that the ‘&’ sign is used to provide the address of the variable to the scanf()
function to store the value read in memory. As str[] is a character array so using str
without braces ‘[‘ and ‘]’ will give the base address of this string. That’s why we have
not used ‘&’ in this case as we are already providing the base address of the string to
scanf.

Now consider one more example:

// C Program to take input string which is separated by


// whitespaces
#include <stdio.h>

// driver code

int main(){

char str[20];

// taking input string

scanf("%s", str);

// printing the read string

printf("%s", str);

return 0;
}

Input
Learning takes time and effort and much more than just going for a job.
Output
Learning takes time and effort and much more than just going for a job.

IV. How to Read a String Separated by Whitespaces in C?

We can use multiple methods to read a string separated by spaces in C. The two of the
common ones are:

1. We can use the fgets() function to read a line of string and gets() to read
characters from the standard input (stdin) and store them as a C string until a
newline character or the End-of-file (EOF) is reached.

2. We can also scanset characters inside the scanf() function.

Example 1:

// C program to illustrate fgets()

#include <stdio.h>
#define MAX 50

int main(){

char str[MAX];

// MAX Size if 50 defined

fgets(str, MAX, stdin);

printf("String is: \n");

// Displaying Strings using Puts

puts(str);

return 0;
}

Input
just type what you want

Output
String is:
just type what you want

Example 2 :

// c program
// scanset characters

#include <stdio.h>

// driver code

int main(){

char str[20];

// using scanset in scanf


scanf("%[^\n]s", str);

// printing read string

printf("%s", str);

return 0; }

Input
doing some more printing

Output
doing some more printing

Example 3 :

// C Program to illustrate strings

#include <stdio.h>

int main(){

// creating array of character

char arr[6] = { 'h', 'e', 'y', '\0' };

// printing string

int i = 0;

while (arr[i]) {

printf("%c", arr[i++]);

return 0;
}

Output : hey

STRINGS IN C++
to avoid using char arrays to represent strings C++ included the <string> header file in order
facilitate operation with a chain of characters

if you need to use a string variable you just include the header file and use the string type in
front of a variable.

string name = ”Tomás” ;

CHAPTER 5

1. POINTERS

A pointer is a data type that holds the address location in memory of another variable, and it
can also have access to its value . A pointer always points to an object of a specific type.

I. The Declaration Syntax is as follows :

type * pointer_name ;

Example: int *ptr ;


II. Pointer Variable Definition:

First you need a variable of the type the pointer variable will point to.

int pencils= 3; // int data type

Once the pointer variable has been initialized it holds the value of the memory location of the
pointed variable.
In the example the location in memory of the variable pencils is used .

int pencils= 3; // int data type

int *pointerToPencils =&pencils;

Defining a pointer variable can be optionally divided in the following syntax.

In both cases keep in mind that the value of a pointer data type is the memory location of the
given variable (address in memory) and not its value, even though you can access that its
value by dereferencing (see the next title).

Pointer definition Syntax :

type * pointer_name1 = &variable_name;

a) one step assignment

Example:

//pointerToPencils points to the variable pencils’ memory location.

int *pointerToPencils =&pencils;

b) Two steps assignment:

Example:

//pointerToPencils points to the variable pencils’ memory location.


int *pointerToPencils ;

pointerToPencils = &pencils;

III. Dereferencing (Accessing The Value of The Pointed Variable )

Dereferencing is used to access the value of a pointed variable.


To access the value stored in the variable Pencils by the pointerToPencils variable
you need to dereference it.

Example:

*pointerToPencils // it is pointing to “int pencils =3;”

std::cout<< *pointerToPencils<< std::endl // prints 3

IV. Const Pointers

Pointers can also have the modifiers or keywords like any other data type.

a) const type *pointer_name = &variable_name;

Example: const int constPointer = &pencils;

When you work with const pointers you need to distinguish between two different
operations:
1. changing the value of the pointed variable (dereferencing):

A. const int *constPointer = & pencils // pencils equals 3


B. *constPointer// dereferencing, gets the value of pencils
C. *constPointer = 15; // changing the value of pencils yields an
error .

2.changing the pointer’s value (the variable memory address it is


pointing to):

A. const int *constPointer = & pencils // pencils equals 3


B. constPointer = &anotherVariable // this works

See an example :

// int variables

int books=3;

// int variables

int chapter=2;

// pointer variable

const int *bookP= &books;

// error you cant modify the value but you can assign the pointer
to point to a different variable.

*bookP= 15;

// in this case you’re changing the address of bookP to point to


the variable chapter’s address.

bookP=&chapter;

V. Pointers To Const

Pointers to const follow the syntax below :

a) type *const pointer_name = &variable_name;

Example :
int numExample= 1;

int *const numericalPointerToConst = numExample;

Example Program:

int main () {

// int variables

int books= 3;

// int variables

int chapter=2;

int *const bookP= &books;

// this will throw an error since you cannot modify the variable
booksP points to.

bookP=&chapter;

// you cannot modify the value of books through bookP

bookP=22;

return 0 ;}

Const Pointers Table


VI. Auto and Pointer Types

Here there are some examples using the auto keyword with pointers:

auto year { 2019 }; // int

auto* year_ptr = &year; // int*

const auto* year_cptr = &year; // const int*

chapter 6

1. DYNAMIC MEMORY ALLOCATION WITH POINTERS

Using the new operator to allocate memory in the heap.


The heap is a part of the computer’s memory. You’ll learn more about this in a different
chapter. you allocate objects ( variables are also objects ) when you don't want to overload
the stack memory of the computer. Dynamic memory allocation means that you’re not saving
objects in the stack but in the heap. You have no need to understand these concepts but the
main objective is to reduce the load used by the stack.
I. Allocation Steps

To allocate memory in the heap, you need to do two things:

a) First, declare a pointer variable.

b) Second, call a function named new.

The new function is a little different from other functions in that you don’t put parentheses
around its parameter. For this reason, it’s actually an operator.

Syntax :

type *name = new type;

Example:

int *ptr = new int;

*ptr=10;

After the computer creates the new integer variable, it stores the address of the integer
variable ptr and right after it you assign a value to that pointer:

Program Example :
#include <iostream>
using namespace std;

int main (){

int *ptr=new int;

*ptr=10;

std::cout<<*ptr<<std::endl;

std::cout<<ptr<<std::endl;

return 0;}

Output:

10
0x73af10

Note

Achieving The Same Dynamic Allocation In a Single Step.

The following code calls new, but also provides a number in parentheses.
That number is put in the memory initially, instead of being assigned to it later.

int *ptr = new int(14);

II. USING DELETE

Once you finish using the pointer variable you must delete it to free space in memory :

Syntax: delete ptr;

See delete in action:

#include <iostream>

using namespace std;

int main() {

string *phrase = new string("All presidents are cool!!!");


cout << *phrase << endl;

cout<<(*phrase)[20] << endl;

cout << *phrase << endl;

delete phrase;

return 0; }

OUTPUT:

All presidents are cool!!!

Note

Don’t try to use the pointer after freeing the memory it points to until you set the pointer to
point to something else through a call to new or by setting it to another variable.

Whenever you free a pointer, a good habit is to set the pointer to the value 0 or
nullptr.

III. SMART POINTERS

This section discusses two smart pointer classes from an overview perspective:

1. A unique_ptr is the only pointer that can point to a resource.

2. A shared_ptr is used when you actually do need to copy pointers. You can copy a
shared_ptr to another pointer.

1. The example shows three ways to create a unique_ptr:

a) Use the new operator.


unique_ptr<int> ptr1(new int());

*ptr1 = 100;

std::cout << "ptr1 value: " << *ptr1 << std::endl;

b) Create a variable and point to it.

int myValue = 42;

unique_ptr<int> ptr2(&myValue);

std::cout << "ptr2 value: " << *ptr2 << std::endl;

c) Employ the make_unique() function.

unique_ptr<int> ptr3 = make_unique<int>(99);

std:: cout << "ptr3 value: " << *ptr3 << std::endl;

std:: cout << "ptr3 address: " << ptr3.get() << std::endl;

Notice

You must specify the pointer type using <int>.

1. Unlike most pointers, you can’t simply specify the pointer name and obtain its
address:

● You must use the get() function instead.

2. As previously mentioned, you can’t make one unique_ptr equal to another


unique_ptr :

● You can use the move() function to move the address of one
unique_ptr to another unique_ptr.

● The swap() function simply swaps addresses between two pointers.

Program Example:

#include <iostream>
#include <memory>
using namespace std;

int main() {

// using the new operator

unique_ptr<int> ptr1(new int());


*ptr1 = 100;
std::cout << "ptr1 value: " << *ptr1 <<std:: endl;

//Create a variable and point to it.

int myValue = 42;


unique_ptr<int> ptr2(&myValue);
std::cout << "ptr2 value: " << *ptr2 << std::endl;

//Employ the make_unique() function.

unique_ptr<int> ptr3 = make_unique<int>(99);


std::cout << "ptr3 value: " << *ptr3 << std::endl;
std::cout << "ptr3 address: " << ptr3.get() <<std::endl; // using
get

// using move

unique_ptr<int> ptr4;

ptr4 = move(ptr3);

if (ptr3 == nullptr) {

std::cout << "ptr3 is nullptr." << std::endl;}

std::cout << "ptr4 value: " << *ptr4 <<std:: endl;

//using get
std::cout << "ptr4 address: " << ptr4.get() <<std:: endl;

return 0;}

Output:

ptr1 value: 100


ptr2 value: 42
ptr3 value: 99
ptr3 address: 0x5daf28
ptr3 is nullptr.
ptr4 value: 99
ptr4 address: 0x5daf28

IV. Using a shared_ptr for Copying

When working with a shared_ptr, you can make one pointer equal to another
pointer. The code demonstrates that both ptr1 and ptr2 point to the same memory location
and have the same value.

Consequently, the resource (not the pointers) is shared between the two pointers.

To make it easier to determine how many references a resource has, you use the
use_count() function.

Of course, now you need some way to remove references when they’re no longer
needed. To perform this task, you use reset().
The code uses ptr2.reset() to remove the second reference to myValue. As shown in the
following output, the use count decreases each time you reset() a pointer.

On the next page I’ve detailed the example in order to make it more readable.

Program Example:

#include <iostream>
#include <memory>
using namespace std;
int main() {

int myValue = 42;

shared_ptr<int> ptr1(new int(myValue));


std::cout << "ptr1 value: " << *ptr1 << std::endl;
std::cout << "ptr1 use count: " << ptr1.use_count() <<std::endl;
shared_ptr<int> ptr2 = ptr1;

std::cout << "ptr2 value: " << *ptr2 << std::endl;


std::cout << "ptr1 address: " << ptr1 << std::endl;
std::cout << " ptr2 address: " << ptr2 <<std:: endl;
std::cout << "ptr1 use count: " << ptr1.use_count() <<std:: endl;

ptr2.reset();

std::cout << "ptr1 use count: " << ptr1.use_count() <<std:: endl;

ptr1.reset();

std::cout << "ptr1 use count: " << ptr1.use_count() << std::endl;

return 0;}

Output:

ptr2 address: 0x6caf08


ptr1 use count: 2
ptr1 use count: 1
ptr1 use count: 0

V. Defining Nullable Values Using std::optional and std::nullopt

In many respects,optional appears as a pointer because it supports many of the same


features as unique_ptr and shared_ptr do.
The following listing shows you how to create a function that could receive a string , but then
again, perhaps not.

You must change the #include <optional> to read #include <experimental/optional>


because the support is experimental.
Example:

#include <iostream>
#include <optional>
using namespace std;

void myFunction(optional<string> name = nullopt) {

if (name == nullopt {

std::cout << "I wish I knew your name!" << std::endl;

} else {

std::cout << "Hello " << name.value() << "!" <<std::endl;}}

int main() {

myFunction();

myFunction("lilly");

return 0;}

When you run this application, you see the following output:

Before the function:


30
Inside the function:
40
After the function:
30

Before the function call, mynumber is 30. And after the function call, it’s still 30.
But the function added 10 to its parameter. This means that when the function
modified its parameter, the original variable remains untouched. The two are separate
entities. Only the value 30 went into the function. The actual variable did
not. It stayed in main(). But what if you write a function that you want to modify
the original variable?

A pointer contains a number, which represents the address of a variable. If you


pass this address into a function and the function stores that address into one
of its own variables, its own variable also points to the same variable that the
the original pointer did. The pointer variable in main() and the pointer variable in
the function both point to the same variable because both pointers hold the same
address.

VI. Using Pointers to Modify a Variable Passed into a Function

A function called ChangesAreGood() that modifies the parameter it receives. (It adds 10 to
its parameter called myparam.) It then print the new value of the parameter.

void ChangesAreGood(int *myparam) {


*myparam += 10;
std::cout << "Inside the function:" <<std::endl;
std::cout << *myparam << std::endl; }

The main() function initializes an integer variable, mynumber, to 30 and prints its
value. It then calls the ChangesAreGood() function, which changes its parameter
After coming back from the ChangesAreGood() function, main() prints the value
again.

Example :

int main() {

int mynumber = 30;


std::cout << "Before the function:" <<std::endl;
std::cout << mynumber << std::endl;
ChangesAreGood(&mynumber);
std::cout << "After the function:" << std::endl;
std::cout << mynumber << std::endl;
return 0;

When you run this application, you see the following output:

Before the function: 30


Inside the function:40
After the function: 30

CHAPTER 6

1. REFERENCING

A Reference is just an alias for an object, which is essentially a way to


give another name to an existing variable.Unlike pointers you must directly declare and
initialize it. This concept is different in the C language.

I. Checking the syntax :

type &reference_name = variable_name;

Example:

int pencils = 3 ;
int &refToPencils = pencils; // its value is now 3

Note

1. Once initialized, a reference remains bound to its initial object. So, it is not
possible to reassign a reference to another object.

2. Since there is not the possibility to rebind a reference, it is necessary to initialize


it.
3. It is not possible to have a reference to another reference since they’re not an object
(you can do this with pointers).

Example 1 :

#include <iostream>
#include <cstdio>
using namespace std;

int main() {

// This program initializes an int called original to 100.

int original = 100;

// Then it declares a reference to original called original_ref.


From this point on, original_ref will always refer to original.

int& original_ref = original;

// This is illustrated by printing the value of original


and the value referred to by original_ref . They’re the same.

printf("Original: %d\n", original);


printf("Reference: %d\n", original_ref);

//Next, you initialize another int called new_value to 200 and


assign original to it . Read that carefully: this assignment doesn’t
reseat original_ref so that it points to new_value. Rather, it
assigns the value of new_value to the object.it points to
(original).

int new_value = 200;


original_ref = new_value;

// The upshot is that all of these variables—original, original_ref,


and new_value—evaluate to 200

printf("Original: %d\n", original);


printf("New Value: %d\n", new_value);
printf("Reference: %d\n", original_ref);

}
Output:

Original: 100
Reference: 100
Original: 200
New Value: 200
Reference: 200

II. CONST REFERENCES:

References to const cannot be used to change the object they refer to.
they’re read-only references.

a)const type variable_name;

b)const type &reference_name = variable_name;

Example :

//Trying to modify the variable countries through refCountries


throws a compiler error.

const int countries =250;

const int &refToCountries= countries;

refToCountries++ // increment of a read-only references plus


the fact that the var countries is also const so there’s no
way to make any changes.

a) CONST REFERENCE TO A NON-CONSTANT VARIABLE :

You can modify the value of var countries but not from the refToCountries variable.
If you change the value of countries the changes will be visible in refToCountries ;

Syntax:
a) type variable_name;

b) const type &reference_name = variable_name;

Snippet :
int countries =250;

const &refToCountries= countries;

refToCountries++ ;

Output: error increment of read-only variable reference

b) A NON-CONSTANT REFERENCE TO A CONSTANT VARIABLE :

This is just not allowed . I mention it as a matter of completeness.

Syntax: a) const type variable_name = value;


b) type &reference_name = variable_name;

Snippet: const int countries =250;


int &refToCountries= countries;

Output: error: binding reference of type 'int&' to 'const int';

c) Auto and Reference Types

The compiler can deduce the type at compile time.

1. auto year { 2019 }; // int


2. auto& year_ref = year; // int&
3. const auto& year_cref = year; // const int&
CHAPTER 7

1. ARRAYS

An array is a data structure containing a series of elements of the same type that have been
placed in contiguous memory locations and can be accessed by their position.

I. TYPES OF ARRAYS

1) SIMPLE ARRAYS

a) Simple Array Declaration

Like any other variable, arrays need to be declared before they can be used. An array
declaration has the following form:

Syntax: type name [elements];


Example: int cities [5];

Here, type is the type of the contained elements, name is the identifier of the array
variable, and elements is the length of the array, so it signifies the number of elements
contained within.

It is important not to exceed the declared length of elements since it can produce runtime
errors. Don’t forget to always use elements that match the array’s type.

b) Array Initialization

There’s two different ways to achieve initialization:

1) Elements can be specifically initialized at declaration time:

int foo [5] = { 1, 2, 11, 15, 1989 }; // 5 elements

2) Its length can also be omitted so the following declaration takes the inserted
elements to determine the array’s length.

int foo [] = { 1, 2, 11, 15, 1989 }; // 5 elements

3) If the number of elements is provided, but the array is initialized with fewer elements,
then the remaining value will be zero-initialized, for example:

int foo [5] = { 1, 2, 11 };

From index 2 all values will be assigned to 0.The previous code is equivalent to the
following:

int foo [5] = { 1, 2, 11, 0, 0 };

4) You also can skip specifying the array size when you pass an array into a function,
like this:

int AddUp(int Numbers[], int Count) {


int loop;
int sum = 0;
for (loop = 0; loop < Count; loop++) {sum += Numbers[loop];}
return sum;}
● This technique is particularly powerful because the AddUp() function can work for
any size array. You can call the function like this:

std::cout << AddUp(MyNumbers, 10) << std::endl;

● But this way to do it is kind of annoying because you have to specify the size each
time you call in to the function. However, you can get around this problem. Look
at this line of code:

std::cout << AddUp(MyNumbers, sizeof(MyNumbers) / 4)


<<std::endl;

c) Accessing the Values of an Array:

Remember that Indexes in arrays start at 0 not at one.


The following syntax shows how to access array values:

Syntax: arrayName[index]

Example:

int foo [5] = { 1, 2, 11, 0, 0 };

foo[0] //returns the value 1

d) Number of Elements in an Array

Use the sizeof operator to obtain the total size in bytes of an array. You can
use a simple trick to determine the number of elements in an array: divide
the size of the array by the size of a single constituent element:

short array[] = { 104, 105, 32, 98, 105, 108, 108, 0 };


size_t n_elements = sizeof(array) / sizeof(short);

f) Assigning Values To a Different Variable:

int fooFirstElement = foo[0] // the value of fooFirstElement is


equal to 1.

g) Changing The Value Of An Array’s Element

int books[5] = { 1, 2, 3, 4, 5 };

books[2]=8;
h) Building Simple Constant Arrays

It works like any other array. What you can't do is change the numbers inside it.

const int Permanent[5] = { 1, 2, 3, 4, 5 };

2) MULTIDIMENSIONAL ARRAYS

a) Multidimensional Arrays Declaration

Multidimensional Arrays are commonly described as arrays of arrays, where an array's


elements are other arrays.
The following syntax illustrates a bi-dimensional array:

Syntax: type name [n][m]; n is the dimension of the array.


Example: int array [3][4]; m is the dimension of its elements.

b) Initializing Multidimensional Arrays

You can initialize a multidimensional array with braces and commas.


This array has 5 data structure arrays and each of them has a length of 6.

int Numbers[5][6] = {
{1,2,3,4,5,6},
{7,8,9,10,12},
{13,14,15,16,17,18},
{19,20,21,22,23,24},
{25,26,27,28,29,30}
};

c) Building Constant Multidimensional Arrays.

More member arrays can be added but you can't modify the given values.

const int Numbers[2][6] = { {1,2,3,4,5,6}, {7,8,9,10,12}};

d) Passing Multidimensional Arrays into Loops

You use two for loops, one that takes the array's quantity and an additional one that loops
through the elements contained.
int AddAll(int numbers[5][6]) {
int x,y;
int sum = 0;
for (x = 0; x < 5; x++) {
for (y = 0; y < 6; y++) {
sum += numbers[x][y];}
return sum;}

e) Storing Arrays of Pointers and Arrays of Arrays

The following is an array of characters holding three pointers:

1. The first points to a character string in memory containing Tom (which is followed by
a null-terminator, \0);

2. The second points to a string in memory containing Suzy


ending with a null-terminator;

3. The third points to a string in memory containing Harry also


ending with a null-terminator;

Example Array:

const char* NamePointers[] = {


"Tom",
"Suzy",
"Harry"
};

std::cout << NamePointers[0] << std::endl;// prints tom

f) Class Arrays

Array elements can also be objects of a class type. The array is known as a class array in
this case. When you declare an array of this type, you only need to state the type of the
array elements.

A class array is initialized as usual by an initialization list. The list contains a constructor call
for each array element.

g) Array of Objects
Once an object is instantiated it can be added to an array just like any other data
structure/data type.

Program example :

#include<iostream>
using namespace std;

class birds{
public:
string name;
string color;
string habitat;

birds(string name,string color,string habitat){


this->name=name;
this->color=color;
this->habitat=habitat;
}
};

int main () {

birds bird1 ("aussie","blue","dessert");

birds data[]={ bird1};

std::cout << data[0].name << std::endl;}

Chapter ARRAYS and Arguments

1. ARRAY AS ARGUMENTS

The argument is an array, there are two equivalent methods for declaring parameters.
This point is illustrated by the example using strlen() to return the length of a Cstring.

I. Simple Arrays as Arguments

a) You can declare the parameter as an array.


Program Example :

// Compute length of int i; str without '\0'.

int strlen(char str[]){ for( i = 0; str[i] != '\0'; ++i);


return (i);}

Program Example B:

#include<iostream>
using namespace std;

int* arrNumbers(int arr[]); // prototype declaration

int main()
{
int arr1[5];
int* newArr[5] {arrNumbers(arr1)};
return 0 ;}

int* arrNumbers(int arr[]){ // function def.


for (int i = 0; i<5; i++){
arr[i]={i};
std::cout << arr[i] << std::endl;
}
return arr ;
}

b) You can declare the parameter as a pointer.

Program Example: int strlen( char *str);


{ /* as above */ }

In both cases the parameter str is a pointer that stores the starting address of the
array. Array notation is preferable if you intend to use an index to access the elements of
an array. Calling strlen("REGAL") leads to the following situation:

c) Return by Reference using Global Variable

The C++ return reference functions are mostly used in Standard Templating libraries(STL) to
perform an effective update of a variable.

The following example illustrates a program that updates values in an array using the return
reference in C++:

Program Example :

#include <iostream>
using namespace std;

// declaring global array


int original_array[] = {1,2,3,4,5};

// function that returns a reference

int& setValues( int j ) {

// return the value at j th index in the array


return original_array[j];
}

int main () {

cout << "Original_array: ";

// find the number of elements in the array


int size=sizeof(original_array)/sizeof(original_array[0]);
// iterate over the array
for ( int i = 0; i <size ; i++ ) {
cout << original_array[i] << "\t";
}

// use return by reference to update values in the array

setValues(1) = 5;
setValues(3) = 10;

// print the updated array

cout << endl << "Original_array changed after function: ";

for ( int i = 0; i < size; i++ ) {

cout << original_array[i] << "\t";


}

return 0;
}

d) Update a value using return reference

In the above example, the C++ return reference function is used to update the values in the
array. The function call of setValues(1) is similar to original_array[1] as the
function returns a reference to the setValues() function and the respective values on the
right-hand side(RHS) of the assignment will be assigned to the array.

The output of the above example is,

Original_array: 1 2 3 4 5
Original_array changed after function: 1 5 3 10 5

II. Multidimensional Arrays as ARG/PARAMS

In a parameter declaration for multidimensional arrays, you need to state every dimension
with the exception of the first. Thus, a parameter declaration for a two-dimensional array
will always contain the number of columns.

long func( int num[][10] ); // ok.

long func( int *num[10] ); // also ok.


I. There are three ways to pass a 2D array to a function:

a) The parameter is a 2D array

int array[10][10];

void passFunc(int a[][10]){

// … }

passFunc(array);

b) The parameter is an array containing pointers

int *array[10];

for(int i = 0; i < 10; i++)

array[i] = new int[10];

//Array containing pointers

void passFunc(int *a[10]) {

// … }

passFunc(array);

c) The parameter is a pointer to a pointer

int **array;

array = new int *[10];

for(int i = 0; i <10; i++)

array[i] = new int[10];

void passFunc(int **a){ // … }

passFunc(array);

3. DYNAMIC MEMORY ALLOCATION FOR ARRAYS

Remind yourself that every time you use the NEW operator you are allocating objects in the
heap. Not on the stack. The C++ 20 syntax is used.
Deleting an array from the heap

syntax: delete[] MyArray;

int *MyArray = new int[50];

delete[] MyArray;

Array of Pointers graph

I. Defining Arrays of Pointers

Whenever you need a large number of pointers, you can define an array whose elements
are pointers. An array of this type is referred to as a pointer array.

Syntax: type* arr_name [];

Example: Account* accPtr[5];

In this case the array’s elements are of the type account.


The array accPtr contains five Account pointers accPtr[0], accPtr[1], ... ,
accPtr[4]. The individual pointers in the array can now be assigned object addresses.
Any pointers not currently in use should have the value NULL.

Example:

Account save("Novack, Kim", 1111, 9999.90);


Account depo("Davis, Sammy", 2222, 1000.);
accPtr[0] = &save;
accPtr[1] = &depo;
for( int i=2; i<5; ++i) accPtr[i] = NULL;

II. Initialization

As usual, an initialization list is used to initialize the array. In the case of a pointer array,
the list contains either valid addresses or the value NULL
.
Example:

Account* accPtr[5] = { &depo, &save, NULL};

The value NULL is automatically assigned to any objects for which the list does not contain
a value. This produces the same result as in the previous example.

CHAPTER 6

1. Arrays and Pointers

The name of the array is a pointer to the array itself. The array name points to the first item.
The following sections discuss arrays and pointers.
Just in case you don't know you can use the following syntax to get the address of any data
structure:

Syntax: &variable_name

Example : double decNumber=10


std::cout<<&decNumber<< endl;

Output: 0x7ffc855092b0

Now let’s go all over the following program :

1. There’s an array variable declaration

2. There are three print statements using the array’s variable.

Snippet:

#include<iostream>
using namespace std;

int main(){

int arr[5]{1,2,3,4,5};

std::cout<<arr <<endl; // printing arr variable


std::cout<<&arr<< endl; // printing arr’s address variable
std::cout<<&arr[0]; // printing arr’s 0 index address

};

Each of the following statements will print the same address.that’s why it is said that an array
works similar to pointers.

a) std::cout<<arr <<endl; // printing arr variable


b) std::cout<<&arr<< endl; // printing arr’s address variable
c) std::cout<<&arr[0]; // printing arr’s 0 index address

Output:

a) arr: 0x7ffe359cf1d1
b) &arr: 0x7ffe359cf1d1
c) &arr[0]: 0x7ffe359cf1d1

From the sample it can be inferred that an array is a pointer


that holds the address of its first element.

2. COMPARISON BETWEEN POINTERS AND ARRAYS:

Look at the following statement definition :

int *ptr = arr;

In this case, both ptr and arr are pointers to the array element arr[0].
3. Arithmetic With Arrays and Pointers

To Access Addresses

a) arr + 1 // access the address of the second element

b) &arr[1] // access the address of the second element

c) ptr + 1 // access the address of the second element

To Access Stored Values:

a) arr[1] // access the value of the address arr[1]

b) *(arr + 1) // access the value of the address arr[1]

c) ptr[1] // access the value of the address arr[1]

d) *(ptr + 1) // access the value of the address arr[1]

At first it might seem surprising that you can use the array notation ptr[i] for pointers.
The compiler translates arr[i] to *(arr + i)—in other words: “Start at address
arr, move i objects up, and access the object!” This also applies to ptr[i].
chapter 10

FUNCTIONS

Introduction

A function consists in a piece of code that executes a set of instructions, performs a certain
operation and returns a value(optional ). If nothing is returned, the Void type is used.
You must have seen that every C/C++ program begins its execution in main(). Well main
() is called that way because it's the main function where all your code is runned.

1. Main Function

The main function is an integral part of the programming languages such as C. The main
function in C/C++ is where the execution of a program starts. It is a user-defined function
that is mandatory for the execution of a program because when a C program is executed,
the operating system starts executing the statements in the main() function.

a) Syntax of C main() Function


return_type main() {

// Statement 1;
// Statement 2;
// and so on..

return;
}

We can write the main function in many ways in C/C++ language as follows:

1. int main(){} or int main(void){}

2. main(){} or void main(){} or main(void){} or void


main(void){}

In the above notations, int means integer return type, and void return type
means that nothing is returned , or (void) or () means that does not take any information
as parameters.

b) Important Points about C/C++ main Function

● It is the function where the program’s execution starts.

● Every program has exactly one main function.

● The name of this function should be “main” not anything else.

● The main function always returns an integer value or void.

● The main function is called by OS, not the user.

c) Types of C/C++ main Functions

1. Main function with no arguments and void return type

2. Main function with no arguments and int return type

3. Main function with the Command Line Arguments


1. Main Function with No Arguments and Void Return Type

Let’s see the example of the main function in which we have written a simple program for
printing a string.
In the below code, first, we include a header file and then we define a main function inside
which we write a statement to print a string using printf() function. The main function is
called by the operating system itself and then executes all statements inside this function.

Syntax :

void main()
{
// Function body
}

The function above is equivalent to:

void main (void)


{
// Function Body
}

Program Example :

// C Program to show main with no return type and no


// arguments

#include <stdio.h>

// Defining a main function


void main()
{

// code
printf("Hello!");
}

Output : Hello!

Note: The return type of main function according to C standard should be int only. Even if
your compiler is able to run void main(), it is recommended to avoid it

2. Main Function with No Arguments and int Return Type


In this example, we use the int return type in the main() function that indicates the exit
status of the program. The exit status is a way for the program to communicate to the
operating system whether the program was executed successfully or not. The convention is
that a return value of 0 indicates that the program was completed successfully, while any
other value indicates that an error occurred.

Syntax

int main()
{
// Function body
}

or

int main(void)
{
// Function Body
}

Example :

// C Program to show the main function with int return type


// and no arguments

#include <stdio.h>

int main()
{
printf("Hello Geek!");
return 0; }
Output : Hello Geek!
3. Main Function with the Command Line Arguments

In this example, we have passed some arguments in the main() function as seen in the
below code. These arguments are called command line arguments and these are given at
the time of executing a program.
The first argument argc is the argument count which means it stores the number of
arguments passed in the command line and by default, its value is 1 when no argument is
passed.
The second argument is a char pointer array called argv[] which stores all the
command line arguments passed.

Syntax

int main(int argc, char* argv[])


{
// Function body
}

Example :

// C Program to illustrate the main function with command line


arguments

#include <stdio.h>

int main(int argc, char* argv)


{
// printing the count of arguments

printf("The value of argc is %d\n", argc);

// printing each argument

for (int i = 0; i < argc; i++) {


printf("%s \n", argv[i]);
}

return 0;
}

chapter 11

1. FUNCTION CREATION

Here I've just detailed the creation process to put functions into work. These are user
defined functions ,which means they’re not included in any other header file. You have
already stepped into one of them in previous chapters ( the printf() function ).
The creation process is detailed below:
I. Declaration

a)int sum ( ); // no parameters declaration

b)int sum (int a, int b ) // declaration with parameters

II. Definition

a) int sum { //non-parameterized function definition


int num1 =2;
int num 2= 5;
int total = num1 + num2 ;
cout<< total;};

b) int sum (int a, int b){ // parameterized function definition


int total = a +b;
cout<< total ;}

III. Calling :

a) sum (); // non parameterized function call

b) sum (1+2); // parameterized function call

I. Function Declaration
To declare a function you need to state the returned value type, the function’s name and a
list of parameters.
Parameters can be optional but you must specify the type of each of them.
Function calling can be carried out with or without parameters.

Syntax: return_type - function_name - (optional parameter list );

a) Parameterized Function Creation. Declaration Process explained :

1. Just like when you declare a variable you state its type but in this case what you're
stating is its return type. if the result of the operation returns an integer you must start
the declaration with an Int type. Otherwise the Void keyword must be stated.

2. Next you decide which name the function will take according to some rules related to
the kind of operation , which at the time of writing turns out to be a “sum“.

3. Last step is to insert into the parenthesis the type of values it is going to take to
execute the defined set of instructions (statements) in the definition step, when the
function is called.

This would only be the case if you expect the function to take in values coming from
outside its own code. These are called parameters and they are function variables
belonging to the function itself, which expect to be passed a value called argument to
perform a given operation.

1.int 2.sum 3.(int a , int b ) ;


//// TWO INT PARAMS. PARAMETERS ARE LOCAL VARIABLES THAT YOUR
FUNCTION IS GOING TO USE TO PERFORM THE STATED OPERATIONS. THEY ARE
GOING TO BE PASSED 2 INT VALUES AND GET STORED IN VARIABLES a AND
b.

Final composition : int sum (int x,int y);

b) Unparameterized function creation

It takes no parameters but still executes the defined statements.

1.int 2.sum 3.( );

Final composition: int sum ();

II. Function Definition

When we define a function, we need to make sure that the signature (the return
value, the name, and the parameters) are the same as the declaration.
You must always use the return keyword unless nothing is returned.
If there’s no value to return, the Void type is used.

a) Parameterized Function Definition

int sum (int a, int b ){


int result = a+ b;
return result;
}

b) Non-parameterized Function Definition

int sum ( ){
int a= 10;
int b=11;
int result = a+ b;
return result;
}

// in non-paremeterized functions you still perform a sum operation.


the difference between parameterized and non-parameterized function
is that the latter gets its local variables defined inside the
function and doesnt expect any code outside the function.

III. CALLING METHODS:

a) Function Call without parameters

This is going to print “hello" on the console.

// DECLARATION

string sayHello();

// DEFINITION

string sayHello(){
string hello = “hello”;
std::cout<< hello <<std::endl; };

// FUNCTION CALL

sayHello();

b) Function call with parameters

To call a function that takes parameters, you would write the name of the function,
followed by parenthesis with the given arguments . Calling a function means to execute the
code / statements defined in the function body.

Syntax : function_name( arguments) ;

sum (1,2);

output: 3
Note

A parameter is a variable that was defined by a function, and can be used to


provide data to the function that comes from the rest of the program :

// int x and int y are the parameters of the sum function.

int sum (int x,int y);

An argument is the actual value the caller (calling step) wants to bind to the parameters of
the function. arguments are the values you give to the parameter variables that will be used
by the function to execute its code.

// number 1 and number 2 are the arguments you provide to the


parameterized function sum.When you call the function you are giving
it these two numbers as arguments. so arguments are the actual value
you assign to the defined parameters .

sum (1,2);

Parameters can be variables of any data type :

integer, double ,float , char , string , wchat_t, boolean , pointer,


reference, objects ,arrays and you can even take a whole function.

Example:

int a , b = 2;

int sum (int num1, int num2){


int result = num1+ num2
return result; }

sum (a,b);

Output: 4

IV. Lamda
. In their simplest form a lambda expression can be defined as follows:
[ capture clause ] (parameters) -> return-type
{
definition of method
}
Generally, the return-type in lambda expressions is evaluated by the compiler itself and we
don’t need to specify it explicitly. Also the -> return-type part can be ignored. However, in
some complex cases e.g. conditional statements, the compiler can’t determine the return
type and explicit specification is required.

Refer to the collections chapter to understand what a vector is and analyze the following
code.

Example :
// C++ program to demonstrate lambda expression in C++

#include <bits/stdc++.h>
using namespace std;

// Function to print vector

void printVector(vector<int> v) {

// lambda expression to print vector

for_each(v.begin(), v.end(), [](int i) {

std::cout << i << " ";


});

cout << endl;


}

int main() {

vector<int> v {4, 1, 3, 5, 2, 3, 1, 7};

printVector(v);

// below snippet find first number greater than 4


// find_if searches for an element for which
// function(third argument) returns true

vector<int>:: iterator p = find_if(v.begin(), v.end(),


[](int i) {return i > 4; });

cout << "First number greater than 4 is : " << *p << endl;

// function to sort vector, lambda expression is for sorting in


non-increasing order Compiler can make out return type as bool, but
shown here just for explanation

sort(v.begin(), v.end(), [](const int& a, const int& b) -> bool {

return a > b; });

printVector(v);

// function to count numbers greater than or equal to 5

int count_5 = count_if(v.begin(), v.end(), [](int a) {

return (a >= 5);


});

cout << "The number of elements greater than or equal to 5 is : "


<< count_5 << endl;

// function for removing duplicate element (after sorting all


//duplicate comes together)

p = unique(v.begin(), v.end(), [](int a, int b) {

return a == b; });

// resizing vector to make size equal to total different number

v.resize(distance(v.begin(), p));

printVector(v);

// accumulate function accumulate the container on the basis of


function provided as third argument

int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

int f = accumulate(arr, arr + 10, 1, [](int i, int j) {

return i * j; });
cout << "Factorial of 10 is : " << f << endl;

// We can also access function by storing this into variable

auto square = [](int i) {

return i * i; };

cout << "Square of 5 is : " << square(5) << endl;

Output:
4 1 3 5 2 3 1 7

First number greater than 4 is : 5

7 5 4 3 3 2 1 1

The number of elements greater than or equal to 5 is : 2

7 5 4 3 2 1

Factorial of 10 is : 3628800

Square of 5 is : 25

A lambda expression can have more power than an ordinary function by having access to
variables from the enclosing scope.

We can capture external variables from the enclosing scope in three ways :

Capture by reference
Capture by value
Capture by both (mixed capture)

Syntax used for capturing variables :

[&] : capture all external variables by reference


[=] : capture all external variables by value
[a, &b] : capture a by value and b by reference

A lambda with an empty capture clause [ ] can only access variables which are local to it.
Chapter 12

PARAMETER TYPES AND FUNCTION OVERLOADING

1. Function Overloading

This means that you can define two functions with the same identifier(name) as long as their
parameters and return types are different .
Depending on the arguments you assign when you call a function the compiler will execute
the one that matches them.

#include <iostream>
using namespace std;

void Combine(string first, string second){

std::cout << first << " " << second <<std:: endl; }

void Combine(int first, int second){

int sum = first + second;

std::cout << first << " " << second << " " << sum << std::endl;}

int main(){

Combine("David","Letterman");

Combine(15,20);

return 0;}

2. PARAMETER TYPES

I. DEFAULT PARAMETERS

A function can be defined to take Default Parameters. See the example below:

int multiply(int multiplied, int multiplier = 1);


The caller of the function can call multiply either with 1 or 2 arguments. if no second
argument is provided the value 1 is going to be used by the function.

multiply(10); // Returns 10

multiply(10, 2); // Returns 20

Note

PASSING BY VALUE VS BY REFERENCE

The first term, passing by value, means sending the actual value of a variable to a function
when you call it. When working with C++, you accomplish this
task by calling the function with the variable. The second term,
passing by reference, means sending the address of the variable to the function so that
the function can modify the original content of that variable.

II. AS A VALUE TYPE

a. Pass by Value

When the parameter is a value type, we say that the function is taking
an argument by value or the argument is passed by value.
In this case ,arguments are copied and a new local object is created each time the function
is called.

int sum (int a , int b){

int total = a +b;

return total ;}

int main () {

int a =10;

int b=11;

sum(a,b); }

Output: 21
b. Returning by Value

When a function that returns by value reaches a return statement, the program
creates a new object, which is initialized from the value of the expression in the return
statement.
This object is called temporary because its lifetime is valid only while the full expression
in which it is created is executed.

#include <iostream>
using namespace std

int multiplication (int x,int y);

int main() {

int x = 3;
int y=5;
int result;
result=multiplication(x,y);
return 0; }

int multiplication (int x,int y){


int total= x * y;
return total; }

c. Passing by const Value

In this program you are passing two const variables to the function multiplication.
If you try to change the value of any of the passed arguments you'll get an increment of
read only parameter error.

#include <iostream>

using namespace std;

int multiplication (const int x, const int y);

int main() {

// notice the following , here below x and y are defined as const


int but you could also define them as int. this won’t alter the
constness of the parameters defined in the function. when you pass
by value you get a copy of the value of the original variable you’re
passing as an argument.
therefore a new const variable will be created by the function and
it’ll exist until it reaches its final execution statement or return
statement .

const int x = 3;
const int y=5;
int result=multiplication(x,y);
std::cout<< result<<std::endl; }

int multiplication (const int x, const int y){

x=10*10; // error

x++ //error

int total= x * y; // this is totally fine you are not modifying any
of the variables, you’re using them as an expression to return and
store a value in the Total variable.

return total;}

d. Returning By Const Value

There is no widespread reason to return by const value since the calling code often
assigns the value to a variable, in which case the const-ness of the variables is going
to be the deciding factor, or passes the value to a next expression, and it is rare for an
expression to expect a const value.
Returning by const value also inhibits the move semantic of C++11, thus reducing
performance.

#include <iostream>
using namespace std;

const int multiplication ( int x, int y);

const int multiplication (const int x, const int y){


const int total= x * y;
return total;

}
III. AS REFERENCE TYPE

When the parameter type of the function is a reference type, we say that the function is
taking an argument by reference or the argument is passed by reference..
This means that the function has access to the object the calling code provided and can
modify it. it has access to the actual object it doesn't get a copy as it does when passing by
value .

a. Passing values by reference

Keep in mind that I don't intend to return the reference, I'm just passing it as an argument .

#include <iostream>
using namespace std ;

void displayState(int& state){


state=1;
std::cout<< state <<std::endl;

};

int main (){

int state =0;

displayState(state);

Note

Unless the function must modify the variable, always use const references, as we
will see later.

A parameter accepted as a reference should be returned as a const reference if the


caller is not expected to modify it.

b. Returning values by Reference

A function whose return type is a reference is said to return by reference.


When a function returning a reference reaches a return statement, a new reference is
initialized from the expression that's used in the return statement.

Syntax:

data_type& function_name(data_type& original_variable){

// perform any actions to the original variable

return original_variable;
}

A. The data_type& is the return data type of the function.

B. The function_name is the name of the function.

C. The parameter of the function is the original_variable.

Steps :

1. Declare a function with return type and parameter as reference variables.

2. Perform the required actions with the reference variable inside the function.

3. Return the reference variable.

// declare function

int params =1;

int& ref_function(int& params){

// required action --> incrementing


params=params+1;

// return reference variable


return params; }
In the syntax above, a variable is passed to the function and a new reference variable or an
implicit pointer to the original variable is created and returned by the function. Therefore it is
important to notice that a reference is used in both the return statement and the argument
passed to the function.

Example 1:

Consider the following case:

int original_variable=10;

int& ref_func(int& original_variable_ref){

return original_variable_ref;
}

Here oringinal_variable is a normal variable. On passing this to the function, the


following occurs,

int& original_variable_ref=original_variable;

Now original_variabel_ref is an alias , a reference variable to


original_variable.

c. Copy of a Reference Variable

In the caller, the function call expression is substituted by the returned reference.

The object referenced by the return value must exist after leaving the function. Otherwise the
program will throw an error.

Example 1 :

The example also illustrates the difference between returning the reference variable itself
and returning a copy of the reference variable,

#include <iostream>
using namespace std;

// function that returns a reference


int& copy(int& reference){

reference++;

return reference;
}

int main() {

// In the example below , the int copy_number stores the copy of the
reference variable returned by the copy() function.
This occurs because the copy_number is declared as a normal int
variable instead of a reference variable (int&).
This stores the copy_number in a new location of the memory and this
number will be independent of the original variable.
Any changes made to the copy_number variable will not be reflected
in the original variable.

// normal variable
int original= 10;

// return copy of a reference


int copy_number=copy(original);

//Copy_number copied: 11
std::cout<< "Copy_number copied: <<copy_number << std::endl;

//Original number: 11
std::cout << "Original number: " << original << std::endl;

// increment copy variable


copy_number++;

//Copy_Number incremented: 12

std::cout<< "Copy_Number incremented: " << copy_number << std::endl;

// The int& reference_number is declared as a reference variable and


will store the reference variable returned by the copy() function.
When the reference_number is modified, then the original variable
referenced by it will also be modified.
This is illustrated by incrementing the reference_number and
checking the increment of the original variable.

//Original number: 11

std::cout << "Original number: " << original << std::endl;

// return the reference variable

int& reference_number=copy(original);

//Reference copy number: 12

std::cout << "Reference copy number: " << reference_number << std::
endl;

//Original number: 12

std:: cout << "Original number: " << original << std:: endl;

// increment the reference variable

reference_number++;

//Reference number increment: 13

std:: cout << "Reference number increment:"<<reference_number <<


std::endl;

//Original number: 13

std::cout << "Original number: " << original << std::endl;


return 0; };

output

Reference number increment: 13


Original number: 13

Example 2 :

// C++ program to illustrate return


// by reference
#include <iostream>
using namespace std;

// Global variable
int x;

// Function returns as a return


// by reference

int& retByRef() {

return x;
}

int main() {

// Function Call for return


// by reference

retByRef() = 10;

// Print X
cout << x;
return 0;
}

Let us see different examples to understand the working of C++ return by reference,

The following example illustrates the manipulation of address in functions that return a
reference and also explains the concept of using a function that returns a reference on the
left side of an assignment operator.

skip this program from now and come back at the end of the chapter.

An example is detailed below : a function that returns by reference and takes a reference ,
prints it and returns it .
#include <iostream>
using namespace std;

int& return_by_ref(int& original){

// print reference

cout<< "Address of Original: "<< &original << endl;

// return reference

return original; }

// main function

int main() {

// normal variable
int a = 20;

// printing the address of the normal variable


cout<< "Address of A: "<< &a << endl;

// reference variable
int& b = return_by_ref(a);

// printing address of reference variable


cout << "Address of B: "<< &b << endl;

// copy variable
int c=return_by_ref(a);

// printing address of copy variable


cout << "Address of C: "<< &c << endl;

// calling the function on the left side


return_by_ref(a) = 13; //a=13

cout<< "Address of A: "<< &a << endl;


return 0;}

Address of A: 0x43e9ff814
Address of Original: 0x43e9ff814
Address of B: 0x43e9ff814
Address of Original: 0x43e9ff814
Address of C: 0x43e9ff810
Address of Original: 0x43e9ff814
Address of A: 0x43e9ff814

The important part of using references correctly as return values is to make sure that
the object outlives the reference: the object must always be alive – at least until there is
a reference to it. This is essential to working with references.

• Never return a reference to a local variable (or a part of it)

• Never return a reference to a parameter accepted by value (or a part of it)

d. Passing values by Const Reference:

Const is extremely important in pass by reference, and any time you use a reference
in the parameters of a function, you should also add const to it (if the function is not
designed to modify it).

#include <iostream>
using namespace std;

// function declaration

int ReturnX( const int& x );

// function definition

int ReturnX (const int& x){


return x;}

int main() {

int x =1;
int result ;

result = ReturnX(x);

std::cout<< result<<std::endl;
result++;
std::cout<< result<<std::endl;
std::cout<< x <<std::endl;
std::cout<< &x <<std::endl;
std::cout<< &result<<std::endl;

return 0; };

output :

1
2
1
0xfbe9fffdfc
0xfbe9fffdf8

EXAMPLE : assigns a reference to another variable by using


const_cast and incremtents it.

#include <iostream>
using namespace std;

void f(const int& x) {

//const_cast makes it possible to create a reference from a non


const variable to a const variable or you’d need to have to integer
references otherwise.

int& y = const_cast<int&>(x);
++y;

int main() {

int a = 5;
f(a);
cout << a << endl;

output : 6
e. Returning values by const Reference:

The return type of a function can also be a reference type. The function call then represents
an object, and can be used just like an object.

CHAPTER 13

1. PASSING INSTANCED OBJECTS AS ARGUMENTS

This is related to classes ,which will be discussed in the next chapter. Feel free to
skip this section if you have no knowledge about how classes work and come back to
check how a function works with created objects based on a class.

If an object is passed as an argument to a function, two possible situations occur:

● The parameter in question is the same type as the object passed to it. The function
that is called is then passed a copy of the object (passing by value)

Here’s an example of a class employee. employee1 is instantiated and passed as an


argument to the printData function.

#include <iostream>
#include<string>
using namespace std;

class Employee {
public :

string name;
string department; };

string printData (Employee x) {

std::cout<<x.name <<std::endl;
string name = x.name;
return name ; }
int main (){

Employee employee1;

employee1.name = "tomas cragnolini";


employee1.department = "programming";

printData(employee1);
return 0; };

● The parameter in question is a reference. The parameter is then an alias for the
argument, that is, the function that is called manipulates the object passed by the
calling function (passing by reference).

As for the example it’s just the same the employee class except that the function parameter
takes reference

string printData (Employee& x) {

std::cout<<x.name <<std::endl;
string name = x.name;
return name ; }

However, there is a third way of passing by reference—passing pointers to the function.

I. Passing Instanced Objects By Value and By Reference

This follows a complex example of passing by value and by reference .

// DayTime.h
// The class DayTime represents the time in hours, minutes and
seconds.

class DayTime {

private:

// private short variable declaration


short hour, minute, second;
bool overflow;

public:

// there’s a set function defined .


//IF the settime function returns false the constructor below bool
//setTime initializes the private variables to 0. otherwise it
//returns true and the arguments passed to the function
initialize //the private variables .

//the this.hour indicates that the class variables declared private


//are going to be initialized and not in the variables declared
as //parameters.
// it is used to distinguish between variables that belong to the
class and other variables .

bool setTime(int hour, int minute, int second = 0)


{
if( hour >= 0 && hour < 24
&& minute >= 0 && minute < 60
&& second >= 0 && second < 60 )
{
this->hour = (short)hour;
this->minute = (short)minute;
this->second = (short)second;
return true;
}
else
return false;
}

// the constructor provides an initial value to the private


variables.
//if the setTime function returns false then the constructor
initializes the variables to 0

DayTime( int h = 0, int m = 0, int s = 0){


overflow = false;
if( !this->setTime( h, m, s))
this->hour =0;
this->minute =0;
this-> second = 0; }

// just get methods to access the value of the variables privately


declared . they return the value of the private variables

int getHour() const { return hour; }


int getMinute() const { return minute; }
int getSecond() const { return second; }

// daytime in seconds returns the total time in seconds

int asSeconds() const { return (60*60*hour + 60*minute + second); }

// takes a daytime object and compares that object’s total time in


seconds to a different object’s total time which is supplied as an
argument. the argument object is passed by value

bool isLess( DayTime t) const {


return asSeconds() < t.asSeconds();}

};

int main()
{
DayTime day1;
DayTime day2;
day1.setTime(10,20,55);
day2.setTime(11,52,41);
day1.isLess(day2);

return 0;};

Calling setTime() and isLess() by Value

day1.setTime(10,20,55);// 36000 1200 55//37255


day2.setTime(11,52,41);//39600 3120 41//42761
day1.isLess(day2);

The Output Returns 1 which is True;


As you already know, passing by value copies an object that was passed as an argument to
the corresponding parameter of a function being called. The parameter is declared as an
object of the class in question.

Example: bool isLess( DayTime t) const;

When the method isLess() is called, the function isLess executes and initializes the
created object, t, with the argument.

day1.isLess(day2) ;

II. Instanced Objects Passed by Reference

The overhead caused by creating and cleaning up objects can be avoided by passing
arguments by reference. In this case, the parameter is declared as a reference or pointer.

Example: bool isLess( DayTime& t) const;

This new declaration of the isLess() method is preferable to the previous declaration.
There is no formal difference to the way the method is called. However, isLess() no
longer creates an internal copy, but accesses directly the object being passed. Of course,
the object cannot be changed, as the parameter was declared read-only.

2. Returning Instantiated Objects

I. Returning an Object by value (sometimes call a copy );

Returning a copy of an object is time-consuming and only makes sense for small-scale
objects.

Example:

// using a public variables:

// C++ program to show passing of objects to a function

#include<iostream>
using namespace std;

class Example {

public:

// public declaration of an int variable

int a;

//public declaration of a function that adds the object property


//values ( the int variables value)
// This function will take object as arguments and return object

Example add(Example Ea, Example Eb) {

Example Ec;

Ec.a = Ea.a + Eb.a;

// returning the object

return Ec;
}
};

int main() {

// this line of codes creates three objects of type example

Example E1, E2, E3;

// Values are initialized for all objects

E1.a = 50;
E2.a = 100;
E3.a = 0;
// values are printed

std::cout << "Initial Values"<<std::endl;


std::cout << "Value of object 1: " << E1.a <<std::endl;
std::cout << "Value of object 2: " << E2.a <<std::endl;
std::cout << "Value of object 3: " << E3.a <<std::endl;

// you need to call the add function from an instantiated object


since the add function is defined inside the class.
// then the passed objects get added and the value assigned to the
third object

E3 = E3.add(E1, E2);

// Changed values after passing object as an argument being printed

std::cout << "New values: " <<std::endl;


std::cout << "value of object 1: " << E1.a<< std::endl;
std::cout << "value of object 2: " << E2.a<< std::endl;
std::cout << "value of object 3: " << E3.a<< std::endl;

return 0; }

II. Using Pointers as Return Values

Instead of returning a reference, a function can also return a pointer to an object. In this
case too, you must ensure that the object still exists after exiting the function.

Example of random pointer return type function:

// Read-only function with a return type of pointer

const DayTime* currentTime() { return &currTime }

Returning Pointers Example :


Functions can return values, including pointers. To set up a function to return a
pointer, specify the type followed by an asterisk at the beginning of the function
header.

Example:

#include <iostream>
#include <sstream>
#include <stdlib.h>
using namespace std;

string *GetSecretCode() {

string *code = new string;


code->append("CR");

int randomnumber = rand();


ostringstream converter;
converter << randomnumber;
code->append(converter.str());
code->append("NQ");
return code;

The main() function creates a pointer to a string named newcode. GetSecret-


Code() returns a pointer to a string, so newcode and the function return value
match. When you use newcode, you must dereference it.

int main() {
string *newcode;
for (int index = 0; index < 5; index++) {
newcode = GetSecretCode();
std::cout << *newcode << std::endl;
}

return 0;}

When you run this application, you see something like the following output:

CR41NQ CR18467NQ CR6334NQ CR26500NQ CR19169NQ


Random Numbers and Strings

Some special code appears in GetSecretCode() that requires explanation. The call
to int randomnumber = rand(); generates a random number. To obtain a random
number and convert it to a string, you add two more include lines:

#include <stdlib.h>
#include <sstream>

The first line provides access to the rand() function. The second line provides access to
the ostringstream type. Here are the three lines that perform the magic:

int randomnumber = rand();


ostringstream converter;
converter << randomnumber;

The first of these creates a random number by calling rand(), which returns an int.
The next line creates a variable of type ostringstream, which is a type that’s handy for
converting numbers to strings. You can use the insertion operator (<<), except that instead
of going to the console, anything you write goes into a string of type ostringstream .

You can add the resulting string onto the code string variable using:

code->append(converter.str());

The part inside parentheses — converter.str() — returns an actual string version


of the converter variable. You use the append() function to add the string to code.

In the case of returning a pointer, the function is still returning


just a value — it is returning the value of the pointer, which is a number representing
an address.

CHAPTER

1. PASSING ARRAYS TO FUNCTION


I. How to pass an array by value, i.e., how to make sure that we have a new copy
of array when we pass it to function?

This can be done by wrapping the array in a structure or class and creating a variable of
type of that structure or class and assigning values to that array.
After that, passing the variable to some other function and modifying it as per requirements.
Note that array members are copied when passed as parameters, but dynamic arrays are
not. So this solution works only for non-dynamic arrays (created without new or malloc).

a. Simple Arrays

Example 1:

// C program to demonstrate passing an array


// by value using structures.

#include<stdio.h>
#include<stdlib.h>

# define SIZE 5

// A wrapper for array to make sure that array


// is passed by value.

struct ArrayWrapper {
int arr[SIZE];
};

// An array is passed by value wrapped in temp

void modify(struct ArrayWrapper temp) {

int *ptr = temp.arr;


int i;

// Display array contents

printf("In 'modify()', before modification\n");

for (i = 0; i < SIZE; ++i)

printf("%d ", ptr[i]);

printf("\n");

// Modify the array

for (i = 0; i < SIZE; ++i)

ptr[i] = 100; // OR *(ptr + i)

printf("\n In 'modify()', after modification\n");

for (i = 0; i < SIZE; ++i)

printf("%d ", ptr[i]); // OR *(ptr + i)


}

// Driver code

int main(){

int i;
struct ArrayWrapper obj;

for (i=0; i<SIZE; i++)

obj.arr[i] = 10;

modify(obj);

// Display array contents

printf("\n\nIn 'Main', after calling modify() \n");

for (i = 0; i < SIZE; ++i)

printf("%d ", obj.arr[i]); // Not changed

printf("\n");

return 0;
}

Example 2 :

// the function divides the size of array which is 8 items by the


size of arr 0, which returns 4 and loops through the array items
using the result of the division , which in this case evaluates to 2
and then prints the first 2 values of arr[]

#include <stdio.h>

void fun(int *arr) {

int i;

unsigned int n = sizeof(arr)/sizeof(arr[0]);

for (i=0; i<n; i++)

printf("%d ", arr[i]); }

// Driver program

int main()
{
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8};
fun(arr);
return 0;
}

Output
1 2

Example 3 : in this case the function is passed a pointer to a char


array , it computes the array length and store the value inthe
variable n . it prints the length of n which is 9 and then loop
through the array as long as the variable i is less than 9 and also
prints all the values.

#include <stdio.h>
#include <string.h>

void fun(char *arr)


{
int i;
unsigned int n = strlen(arr);
printf("n = %d\n", n);
for (i=0; i<n; i++)
printf("%c ", arr[i]);
}

// Driver program

int main()
{
char arr[] = "geeksquiz";
fun(arr);
return 0;
}

Output

n = 9
g e e k s q u i z

b) PASS A 2D ARRAY
Example 1: the function print takes an integer array of 2
dimensions. it is passed arr as an argument and uses two declared
variables M and N and two for loops to go through every dimension
and print its values.

i represents the array's dimension , which turns up to be 3 and j


loops over the elements of the array.
while the array dimension is not greater than M (3) loop over every
dimension and while j is not greater than N (3) print the array
values

#include <stdio.h>
const int M = 3;
const int N = 3;

void print(int arr[M][N]){

int i, j;

for (i = 0; i < M; i++){

for (j = 0; j < N; j++){

printf("%d ", arr[i][j]); }

int main()
{
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
print(arr);
return 0;
}

Output
1 2 3 4 5 6 7 8 9

Example 2 :

//Using a single pointer .


// In this method, we must typecast the 2D array when passing to
function.

#include <stdio.h>
void print(int *arr, int m, int n)
{
int i, j;
for (i = 0; i < m; i++)
for (j = 0; j < n; j++)
printf("%d ", *((arr+i*n) + j));
}

int main()
{
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int m = 3, n = 3;

// We can also use "print(&arr[0][0], m, n);"

print((int *)arr, m, n);

return 0;}

Output

1 2 3 4 5 6 7 8

CHAPTER 15

1. Using a Function Prototype

Most programmers declare a function on top of the file and define it after the main function
with the objective of making the code more manageable , more organized and more
readable.

#include <iostream>
using namespace std;

// function declaration or function prototype

void PrintName(string first, string last);

// main function
int main()
{
PrintName("Thomas", "Jefferson");
return 0;
}

// function definition

void PrintName(string first, string last){


string fullname = first + " " + last;
std::cout << fullname << std::endl;
}

Although the previous structure of the code is the most preferred you could also
declare and define a function at a different place like in a header file or in any different order
in the same file although some compilers might complain if you don't follow the previous
order .Nevertheless try to stick to the above model since it is the standard followed by most
c++ programmers.

2. A special kind of function. Inline Functions

C++ provides inline functions to reduce the function call overhead.


when the inline function is called, the whole code of the inline function gets inserted or
substituted at the point of the inline function call. This substitution is performed by the C++
compiler at compile time.

The compiler inserts the code of an inline function at the address where the function is
called and thus avoids jumping to a subroutine. The definition of an inline function is
introduced by the inline keyword in the function header.
An inline function must be defined in the source file in which it is called. You cannot
simply supply a prototype of the function. The code containing the instructions must
also be available to the compiler. It therefore makes sense to define inline functions in
header files, in contrast to “normal” functions. This means the function will be available
in several source files.
a) Inline and Standard Function Calling
3. Lambda

It is defined with a syntax very similar to the one of functions:

[captured variables] (arguments) { body }

This creates a new object that, when called with the arguments specified in the lambda
expression, executes the body of the function.

Arguments is the list of arguments the function accepts, and body is the sequence of
statements to execute when the function is invoked. The same rules for functions apply.

For example, let's create a lambda that takes two integers and returns their sum:

#include <iostream>

int main()

{
auto sum_numbers = [] (int a, int b) { return a + b; };
std::cout << sum_numbers(10, 20) << std::endl;
}

Output:
30

By default, the body of the lambda can only reference the variables that are defined in
the argument list and inside the body, like for functions.

Additionally, lambdas can capture a variable in the local scope, and use it in their body.
Captured variables entail a list of variable names that can be referenced in the body of
the lambda.
When a variable is captured, it is stored inside the created function object, and it can be
referenced in the body.

By default, the variables are captured by value, so they are copied inside the function
object:

#include <iostream>

int main()
{

int addend = 1;
auto sum_numbers = [addend](int b) { return addend + b; };
addend = 2;
std::cout << sum_numbers(3) << std::endl;

Output:

When we created the lambda, we captured addend by value: it was copied into the
sum_numbers object. Even if we modified the value of addend, we did not change the copy
stored inside sum_numbers, so when sum_numbers is executed, it sums 1 to b.
In some situations, we want to be able to modify the value of a variable in the scope in
which the lambda is created, or we want to access the actual value, not the value that
the variable had when the lambda was created.
In that case, we can capture by reference by prepending & to the variable name.

Note

When we capture by reference, we need to make sure that the variable that's been
captured by reference is still valid when the lambda is invoked, otherwise the body
of the function accesses an invalid object, resulting in bugs.
Prefer to capture by value when it is possible.

Let's look at an example:

#include <iostream>
int main()
{
int multiplier = 1;
auto multiply_numbers = [&multiplier](int b) { return multiplier *
b; };
multiplier = 2;
std::cout << multiply_numbers(3) << std::endl;
}
Output:
6

Here, we capture the multiplier variable by reference: only a reference to it was stored
into multiply_numbers.
When we invoke multiply_numbers, the body accesses the current value of multiplier,
and since multiplier was changed to 2, that is the value that's used by the lambda.
A lambda can capture multiple variables, and each one can be either captured by value
or by reference, independently one from the other.

CHAPTER 16

1. INTRODUCTION TO SCOPE

The scope of a variable in C is the block or the region in the program where a variable is
declared, defined, and used. Outside this region, we cannot access the variable and it is
treated as an undeclared identifier.

1) Types of Scope Rules in :


a) Global Scope in C:

1. The global scope refers to the region outside any block or function.
2. The variables declared in the global scope are called global variables.
3. Global variables are visible in every part of the program.
4. Global is also called File Scope as the scope of an identifier starts at the
beginning of the file and ends at the end of the file.

Example :

// C program to illustrate the global scope

#include <stdio.h>

// variable declared in global scope

int global = 5;

// global variable accessed from


// within a function

void display()
{
printf("%d\n", global);
}

// main function

int main()
{
printf("Before change within main: ");
display();

// changing value of global


// variable from main function
printf("After change within main: ");
global = 10;
display();
}

Output

Before change within main: 5


After change within main: 10
Example of External Linkage

//Linkage of Variables in Global Scope


//Global variables have external linkage by default. It means that
the variables declared in
//the global scope can be accessed in another C source file. We have
to use the extern
//keyword for that purpose.

file1.c

// filename: file1.c

int a;

int main(void)
{
a = 2;
}

// filename: file2.c
// When this file is linked with file1.c, functions
// of this file can access a

extern int a;

int myfun()
{
printf("%d", a);
}

Output

Note: To restrict access to the current file only, global variables can be marked as static.

b) Local Scope in C

The local scope refers to the region inside a block or a function. It is the space enclosed
between the { } braces.
The variables declared within the local scope are called local variables.
Local variables are visible in the block they are declared in and other blocks nested inside
that block.
Local scope is also called Block scope.
Local variables have internal linkage.

Example :

// C program to illustrate the local scope

#include <stdio.h>

// Driver Code
int main()
{
{
int x = 10, y = 20;
{
// The outer block contains
// declaration of x and
// y, so following statement
// is valid and prints
// 10 and 20

printf("x = %d, y = %d\n", x, y);


{
// y is declared again,
// so outer block y is
// not accessible in this block
int y = 40;

// Changes the outer block


// variable x to 11
x++;

// Changes this block's


// variable y to 41
y++;

printf("x = %d, y = %d\n", x, y);


}

// This statement accesses


// only outer block's
// variables
printf("x = %d, y = %d\n", x, y);
}
}
return 0;
}

Output

x = 10, y = 20
x = 11, y = 41
x = 11, y = 20

Q1. What if the inner block itself has one variable with the same name?
If an inner block declares a variable with the same name as the variable declared by the
outer block, then the visibility of the outer block variable ends at the point of the declaration
by inner block.

Q2. What is the difference between the scope and lifetime?


The scope and lifetime of a variable are often confused with one another

● The scope of the variable is the region where it is valid to refer to the variable using
its name.

● The lifetime of the variable is the time between it is allocated memory and it is
deleted from the memory.

Q2. What about functions and parameters passed to functions?


A function itself is a block. Parameters and other local variables of a function follow the same
block scope rules.

Q3. Can variables of the block be accessed in another subsequent block?


No, a variable declared in a block can only be accessed inside the block and all inner blocks
of this block.

CHAPTER …

1. INTRODUCTION TO STORED CLASSES AND OBJECTS

1) Object

An object is a piece of data in the program's memory.


A variable is a name we give to an object.There is a distinction in C++ between the scope of
a variable and the lifetime of the object it refers to.

2) Lifetime

The lifetime of an object, on the contrary, is the time during execution wherein the
object is valid to access.

Essentially, an object is only available after you have declared it within a translation
unit.

A translation unit, also referred to as module, comprises the source file you are compiling
and any header files you have included.

Lifetime of An Object

1. The object’s storage duration begins, and storage is allocated.


2. The object’s constructor is called.
3. The object’s lifetime begins.
4. You can use the object in your program.
5. The object’s lifetime ends.
6. The object’s destructor is called.
7. The object’s storage duration ends, and storage is deallocated.

As a programmer, you can define an object with:

● Block scope: The object is only available in the code block in which it was
defined. The object is no longer visible once you have left
the code block.

● File scope : The object can be used within a single module. Only the
functions within this module can reference the object. Other
modules cannot access the object directly.

● Program scope: The object is available throughout the program, providing a


common space in memory that can be referenced by any program function. For this
reason, these objects are often referred to as global.

Access to an object as defined by the object’s storage class is independent of any


access controls for the elements of a class. Namespaces that subdivide program scope and
classes will be introduced at a later stage.
EXAMPLES :

#include <iostream>

//GLOBAL

int house =1 ;// global variable. it’s out of the main function
which makes it available everywhere

//LOCAL

void print (){


string text= “happy lawyer’s day”;
std::cout<<text<<std::endl;
}

// LOCAL
int main () {
int buyers=4; // local scope . this variable can only be accessed in
the main function
return 0 ;}

3) Block Scope

I. Local (automatic) Storage Duration Local Block Scope

An automatic object is allocated at the beginning of an enclosing code block,


and it’s deallocated at the end. The enclosing block is the object’s
scope.

Automatic objects are said to have local storage duration. Note that
function parameters are automatic, even though notationally they appear
outside the function body.

void Plant (string plantName) {


std::cout<< plantName<<std::endl;
string season = 1;
}

plantName and season both start their object life cycle when the function runs and they
finish when it ends.

II. Keeping your variables local

● When you create a variable inside the code for a function, that variable will be known
only to that particular function. When you create such variables, they are called local
variables, and people say that they are local to that particular function.

void PrintName(string first, string last){

string fullname = first + " " + last;

std::cout << fullname <<std::endl;}

int main(){
PrintName("Thomas", "Jefferson");

return 0;

● Notice in the PrintName() function that you declare a variable called fullname.You
then use that variable in the second line in that function, the one starting withcout.
But you cannot use the variable inside main(). If you try to, as in the following code,
you get a compile error:

int main(){

PrintName("Thomas", "Jefferson");
std::cout << fullname <<std::endl; // error out of scope.
return 0;

● You can declare a variable called fullname inside main(). But, if you do that, this
fullname is local only to main(), whereas the other variable, also called fullname, is
local only to the PrintName() function.

int main(){

PrintName("Thomas", "Jefferson")
string fullname =”main declaration”;
std::cout << fullname << std::endl;

return 0;

SUMMARY

In other words, each function has its own variable; they just happen to
share the same name. But they are two separate variables.

Local variables are in the function scope and can only be accessed by the function.
It is desirable to use local variables over global variables because they enable
encapsulation: only the code inside the function body can access and modify the
variable, making the variable invisible to the rest of the program.

Always use the const qualifier with global variables whenever possible.
Try to avoid using mutable global variables.

4) FILE SCOPE

I. Internal linkage:

When you use the static keyword, you specify internal linkage. Internal
linkage means that a variable is inaccessible to other translation units.

a) global static duration

A static object is declared using the static keyword. You declare


static variables at global scope (or namespace scope). Static objects with global scope have
static storage duration and are allocated when the program starts and deallocated when the
program stops.

#include<stdlib.h>
#include<stdio.h>
#include <iostream>
#include <cstdio>
using namespace std;

static int total = 200;

void addNumber(int newNumber) {

total = total + newNumber;

const auto newTotal= total * 20; }

int main() {

printf("total: %d\n", total);

addNumber(100); {
printf("total: %d\n", total);}

return 0;

};

total: 200
total: 300

The variable total is static because it’s declared at global scope with the static
keyword. Another feature of being declared at global scope is that total can be
accessed from any function in the translation unit.

b) Local Static Variables

Local static variables are declared at function scope, just like automatic variables. But their
lifetimes begin upon the first invocation of the enclosing function and end when the program
exits.

Example :

#include <cstdio>

void addNumber(int newNumber) {

static int total= 200;

total= total + newNumber;

const auto newTotal= total* 20;

printf("total: %d\n", total );

int main() {

addNumber(100);

addNumber(500);
}

// Run the code to check the output.

You cannot refer to total from outside of


the addNumber function due to its local scope. This is an example of a
programming pattern called encapsulation, which is the bundling of data with
a function that operates on that data. It helps to protect against unintended
modification.

II. External Linkage:

You can alternately specify external linkage, which makes a variable accessible to
other translation units. For external linkage, you use the extern keyword instead of static.

#include <cstdio>
extern int total = 200; // External linkage
--snip--

With extern rather than static, you can access total from
other translation units.

III. Static Members

Static members are members of a class that aren’t associated with a particular
instance of the class. Normal class members have lifetimes nested within
the class’s lifetime, but static members have static storage duration.
These members are essentially similar to static variables and functions
declared at global scope.

You must refer to them by the containing class’s name, using the scope resolution operator.

In fact, you must initialize static members at global scope. You cannot initialize a static
member within a containing class definition.

Like other static variables, static members have only a single instance.
All instances of a class with static members share the same member, so if
you modify a static member, all class instances will observe the modification.

Example :

#include <cstdio>

struct test {

static int total ;


static void addNumber(int newNumber) {

total= total+ newNUmber;


const auto newTotal= total * 20;
printf("total: %d\n", total); } };

int test::total = 200;

int main() {

std::cout<<number1.total<<std::endl;
test::addNumber(100);

std::cout<<number1.total<<std::endl;
test::addNumber(500);

std::cout<<number1.total<<std::endl;

return 0;}

The Test struct contains the static variable total and the static function addNumber.
Because total is a member of the Test struct you don't need the scope resolution
operation (:).

In the main function you can see how the output of the instanced class number1 keeps
changing every time it gets printed.

IV) Auto And Register.

a) Auto Objects

The storage class auto (automatic) includes all those objects defined within a function
but without the static keyword. The parameters of a function are also auto objects.
You can use the auto keyword during a definition.

Example: auto float radius; // Equivalent to:


// float radius;

When the program flow reaches the definition, the object is created on the stack, but in
Unlike a static type object, the object is destroyed on leaving the block.
auto objects have no specific initial value if they are not initialized explicitly. However,
objects belonging to a class type are normally initialized with default values, which
can be specified in the class definition.
b) Using CPU Registers

To increase the speed of a program, commonly used auto variables can be stored in
CPU registers instead of on the stack. In this case, the register keyword is used to
declare the object.
A register is normally the size of an int variable. In other words, it only makes sense
to define register variables if the variable is not too large, as in the case of types such
as char, short, int or pointers. If you omit the type when defining a register variable,
an int is assumed.
However, the compiler can ignore the register keyword. The number of registers
available for register variables depends on your hardware, although two registers are
normally available. If a program defines too many register variables in a code block, the
superfluous variables are placed in the auto storage class.

IV. Dynamic Storage Duration

Objects with dynamic storage duration are allocated and deallocated on request.
You have manual control over when a dynamic object’s life begins and when it
ends. Dynamic objects are also called allocated objects for this reason.

The primary way to allocate a dynamic object is with a new expression:

int*my_int_ptr = new int;

You declare a pointer to int and initialize it with the result of the new
expression on the right side of the equal sign . The new expression is composed
of the new keyword followed by the desired type int . When the
new expression executes, the C++ runtime allocates memory to store an int
and then returns its pointer.

You can also initialize a dynamic object within a new expression, as


shown here:

int* my_int_ptr = new int{ 42 }; // Initializes dynamic object to 42

After allocating storage for the int, the dynamic object will be initialized
as usual.

You deallocate dynamic objects using the delete expression.


Delete expressions always return void.

To deallocate the object pointed to by my_int_ptr, you would use the following:
delete expression:

delete my_int_ptr;

Example Program:

//Simple Program showing object life cycle

#include <iostream>
const int globalVar = 10; // 1

int* foo(const int* other) {


int fooLocal = 0; //5
std::cout << "foo's local: " << fooLocal << std::endl;
std::cout << "main's local: " << *other << std::endl;
return &fooLocal; //6
}

int main() {

int mainLocal = 15; //2


int* fooPointer = foo(&mainLocal);//3
std::cout << "main's local: " << mainLocal << std::endl;
std::cout << "We should not access the content of fooPointer! It's
not valid." << std::endl;

return 0;}
chapter … Namespaces

Namespaces

A namespace starts a scope in which all the names declared inside are part of the
namespace.

Using the directive allows you to import all the identifiers in a namespace.

Example: using namespace std;

To create a namespace, we use the namespace keyword, followed by the identifier and
then the code block:

namespace example_namespace {
// code goes here
}

To access an identifier inside a namespace, you prepend the name of the identifier with
the name of the namespace in which it is declared, followed by “:: “ .

You might have noticed that, before we were using std::cout. This is because the C++
standard library defines the std namespace and we were accessing the variable named cout.

Note

● To better organize your code and avoid naming conflicts, you should always put
your code inside a namespace that's specific to either your application or library.
Namespaces can also be used to specify that some code is used only by the
current code.

● Let's imagine you have a file called a.cpp that contains int default_name = 0; and
another file called b.cpp with int default_name = 1;. When you compile the two files
and link them together, we get an invalid program: the same variable has been declared
with two different values, and this violates the One Definition Rule (ODR).

● But you never meant for those to be the same variable. To you, they were some
variables that you just wanted to use inside your .cpp file.

● To tell that to the compiler, you can use anonymous namespaces: a namespace with
no identifier.

● All the identifiers created inside it will be private to the current translation unit
(normally the .cpp file).
● How can you access an identifier inside an anonymous namespace? You can
access the identifier directly, without the need to use the namespace name, which
does not exist, or the using declaration

All the identifiers created inside it will be private to the current translation unit
(normally the .cpp file).

Note: You should only use anonymous namespaces in .cpp files.

CHAPTER …17

1. CLASSES

A class defines a blueprint or the design of an object.It is used to create objects according to
its defined properties, also called data members, which include fields (variable
declarations) , methods (that's how functions are called) , other data types and data
structures. Multiple objects can be created from the same class and are called instances.

A class is a type. It includes properties and methods. Properties describe


the class, and methods describe its behaviors.
● Common practice when creating a class is to put the class definition in a
header file of the same name as the class but with an .h extension.
● And you put the class method code in a source code file of the same name as the
class but this time with a .cpp extension.

When you divide the class, you put part in the header file and part in the source
code file. The following list describes what goes where:

Header file: Put the class definition in the header file. Properties appear as
part of the class definition within the header. You can include the method
code inside the class definition.

Source file: If your class has methods, and you didn’t put the code in the class
definition, you need to put the code in a source file. When you do, precede the
function name with the class name and the scope resolution operator (::). If
you named the header file the same as the class, you probably want to name
the source file is the same as the class as well but with a .cpp extension.

2. Class Declaration

The definition begins with the keyword class followed by the class name. The data
members and methods are then declared in the subsequent code block:

Syntax: class class_name;

Example: class Car ;

Classes use access specifiers to determine what´s available for the user to access.
You have already come across the static specifier. I've detailed below the remaining class
(struct) specifiers.

● Private . they cannot be accessed externally. Members declared as private can only
be accessed by the functions inside the class and are not allowed to be accessed
directly from outside.

● Protected: properties can only be accessed by inner functions.Members declared


protected can only be accessed by functions inside the class and the derived
classes.

● Public: members are available for external access.


Members declared as public can be accessed from anywhere in the program.
Note

By default, class members have the private access modifier. On the other hand struct
properties are defined as public by default.

3. Class Definition

When we declare and define a class, we create a new scope called the class scope.
The members defined inside a class are accessible only from/through the same class scope
.

a) Syntax:

Program Example:

// A Class Definition

//A class named coordinate is defined using a public specifier


containing two floating-point variables and a method declaration.

class Coordinates {

public:

float latitude;// float field variable declaration

float longitude;//float field variable declaration


float distance(const Coordinates& other_coordinate);// member
function declaration(also method)

};

To define a method outside the class the scope resolution operator must be used
“:”. Failure to supply the class name results in a global function definition.

Example :

float Coordinates::distance(const Coordinates& other_coordinate) {


return pythagorean_distance(latitude, longitude,
other_coodinate.latitude, other_coordinate.longitude);}

4. Creating an Object Out of a Class.

Here's an example that shows a class with two instanced objects :

Coordinates newYorkPosition; // new object (NY is now an object


belonging to the coordinates class )

Coordinates tokyoPosition // new object (tokyoPosition is now


another object belonging to the coordinates class )
To create an object it’s necessary to include the class name identifier plus some objects’s
name related to the class you’re dealing with :

● Coordinates is the class identifier ;


● newYorkPosition is the object’s name ;

.
5. Accessing The Properties of a Created Object.

Once an object is created it is going to inherit its class data members . The operator to
access data members of a class is the dot (.) operator.

Example :

newYorkPosition.latitude;

To initialize the value of an object’s property the assignment operator is used :

newYorkPosition.latitude=120.50;
Note

If you had a lot of members to initialize you’d have to follow this syntax for each of them one
by one .That would then become strenuous and your code would likely become more prone-
error . That’s when constructors come in handy.

6. Constructor Object Initialization

Constructors are special functions meant to achieve object initialization.

When you create a new instance of a class, you may want to do some basic object
setup. Suppose you have a class called Cellphone, with a private property called
quantity and a method called addCellPhone(). The code for addCellPhone()
adds 1 to quantity.

When you create a new instance of Cellphone, you probably want to start quantity at
0. The best way to do this is by adding a special method, a constructor, to your class. This
method has a line of code such as

quantity = 0

Think of the constructor as an initialization function: The computer calls it when


you create a new object.

When you delete an instance of a class (object) , you might want some cleanup code. It's
essential to delete those other objects. You put cleanup code in a special function called
a destructor. A destructor is a finalization function that the computer calls before
it deletes your object.

Every class automatically contains four standard methods:

● The default constructor


● The destructor
● The copy constructor
● The assignment.

a. Constructor Declaration and Definition

Constructors can be identified by their names. In contrast to other member functions,


the following applies:
I. The name of the constructor is also the class name.

II. A constructor does not possess a return type—not even void.

Syntax : class_name (parameters list ){...};

Constructors are normally declared in the public section of a class. This allows you to
create objects wherever the class definition is available.

Constructors can be overloaded, just like other functions. Constructors belonging to a


class must be distinguishable by their signature (that is, the number, order, and type of
parameters). This allows for different methods of object initialization.

b. Constructor Types

A default constructor is a constructor that takes no parameters. You can have a default
constructor in a class either by coding it or by letting the compiler implicitly build one for you.
Every class that lacks a constructor usually has a default constructor created by the
compiler.

The following includes explicitly a default constructor with no parameters:

class Coordinates {

public:

float latitude;// float field (variable) declaration

float longitude;//float field(variable) declaration

Coordinates(){} // explicitly defined constructor

};
c. Standard Constructor

I. Definition Inside The Class:

Example Program :

#include<iostream>

using namespace std;

class Coordinates{

public:

int latitude;
int longitude;

Coordinates(int lat,int lon){

latitude=lat;
longitude=lon;
}

};

II. Definition Outside The Class

Coordinates::Coordinates(int lat,int lon){


latitude=lat;
longitude=lon; }

int main()
{

// creating and initiliazing an object by constructor calling

Coordinates argentina(20,220);
std::cout<<argentina.latitude;

};
d) Accessing Private Data Members( Setters and Getters)

Access methods offer a far more useful way of accessing the private data members.
Access methods allow data to be read and manipulated in a controlled manner.

Example :

class Coordinates {

private:

int latitude;// field (variable) declaration

int longitude;// field(variable) declaration

public:

void printCoordinates( ){
std::cout<<latitude<<std::endl;
std::cout<<longitude<<std::endl;
}

void setLatAndLon(int lat, int lon){


latitude=lat;
longitude=lon;
}

};

f. Copy Constructor

The copy constructor initializes an object with another object of the same type. It is
called automatically when a second, already existing object is used to initialize it .

Copy constructor with a Coordinates object as parameter copies data of the obj parameter.
Coordinates(Coordinates &obj) {
latitude= obj.latitude;
longitude= obj.longitude;
}

Coordinates argentina(20,220);
Coordinates brazil(argentina);

The following initialization process takes place:

brazil.latitude = argentina.latitude;
brazil.longitude=argentina.longitude;

g. Assignment Constructor

In contrast to initialization using the copy constructor for assignment requires two existing
objects.

brazil = argentina;

h. Overloading constructors

It's the same as overloading functions . it means that you can define more than one
constructor with the same identifier but different parameters. That way the compiler will know
which function to call according to the arguments provided.

class Simple {
public:
int x,y;
void Write() {
cout << x << " " << y << endl;
}
Simple() {}
Simple(int startx) { x = startx; }
};

7. Initialization Lists

C++ provides a feature to initialize the values of fields of the class


in a more ergonomic way: initialization lists.
Initialization lists allow you to call the constructor of the data members of class before the
constructor body is executed.
To write an initializer list, insert a colon (:) and a comma-separated list of initializations for
class members before the constructor's body.
A member initializer is the name of the member followed by a
braced initialization { }. Member initializers allow you to set the value of
const fields at runtime.

Let's look at the following example:

class Rectangle {

private:

int width;
int height;

public:

//Empty function body, as the


variables have already been initialized

Rectangle(): width(0), height(0) { }

};

Note how, in this last case, the constructor does nothing other than initialize its
members. Hence, it has an empty body.
Now, if we try to print the width and the height of the Rectangle object, we will notice
that they are correctly initialized to 0:

Rectangle rectangle;

std::cout << "Width: " << rectangle.width << std::endl; // 0


std::cout << "Height: " << rectangle.height << std::endl; // 0

Initializer lists are the recommended way to initialize member variables in C++, and they
are necessary when a data member is const.
When using an initializer list, the order in which the members are constructed is the
one in which they are declared inside the class; not the one in which they appear in the
Initializer list.

Let's look at the following example:

class Example {
Example() : second(0), first(0) {}
int first;
int second;
};

When calling the default constructor of the Example class, the first method will be
initialized first, and the second method after it, even if they appear in a different order in
the initializer list.

Note

You should always write the members in the initializer list in the same order as
they are declared; compilers will help you by warning you when the order differs
from the expected one.

8. Private Destructor
What is the use of a private destructor?
Whenever we want to control the destruction of objects of a class, we make the destructor
private. For dynamically created objects, it may happen that you pass a pointer to the object
to a function and the function deletes the object. If the object is referred after the function
call, the reference will become dangling.
Predict the Output of the Following Programs:

// CPP program to illustrate


// Private Destructor
#include <iostream>
using namespace std;

class Test {
private:
~Test() {}
};
int main() {}

chapter …18

Destructors
1. Destructors Declaration

Destructors are declared in the public section and follow this syntax:

Syntax: ∼class_name(void);

Just like the constructor, a destructor does not have a return type. Neither does it
have any parameters, which makes the destructor impossible to overload. Each class thus
has one destructor only.

2. Definition

If the class does not define a destructor, the compiler will create a minimal version of
a destructor as a public member, called the default destructor.

∼class_name::∼class_name(){} // Nothing to do

3. Calling Destructors

A destructor is called automatically at the end of an object’s lifetime:

● For local objects except objects that belong to the static storage class, at the
end of the code block defining the object

● For global or static objects, at the end of the program.

Example : // dynamic memory allocation destructors and constructors

The WalnutClass example uses a constructor and destructor.


This application involves two classes, the main one called Squirrel that demonstrates
the constructor and destructor, and one called Walnut.

#include<iostream>

using namespace std;


class Walnut {
public:
int Size;
};

class Squirrel {

private:

Walnut *MyDinner;

public:

Squirrel();// THIS IS A CONSTRUCTOR DECLARATION


~Squirrel();//THIS IS A DESTRUCTOR DECLARATION

};

// constructor definition outside the class scope

Squirrel::Squirrel(){
MyDinner = new Walnut;
MyDinner->Size = 30;

//destructor definition

Squirrel::~Squirrel() {
cout << "Cleaning up my mess!" << endl;
delete MyDinner;

int main() {

Squirrel *Sam = new Squirrel;


Squirrel *Sally = new Squirrel;
delete Sam;
delete Sally;
return 0;
}

The Squirrel class has a property called MyDinner that is a pointer to a Walnut
instance. The Squirrel constructor creates an instance of Walnut and stores it
in MyDinner. The destructor deletes the instance of Walnut. In main(), the code
creates two instances of Squirrel. Each instance gets its own Walnut to eat.

Each Squirrel creates its Walnut when it starts and deletes the Walnut when the
Squirrel is deleted.

Notice in this code that the constructor has the same name as the class,
Squirrel(). The destructor also has the same name, but with a tilde, ~, tacked on
to the beginning of it. Thus, the constructor is Squirrel() and the destructor is
~Squirrel(). Destructors never take parameters and you can’t call them directly,
but the runtime calls them automatically when it’s time to destroy an object.

When you run this application, you can see the following lines, which were spit
up by the Squirrel in its constructor and destructor. (You see two lines of each
because main() creates two squirrels.)

Starting!
Starting!
Cleaning up my mess!
Cleaning up my mess!

If the Walnut class also had a constructor and destructor, and you made the
MyDinner property a variable in the Squirrel class, rather than a pointer, the
computer would create the Walnut instance after it creates the Squirrel instance,
but before it calls the Squirrel() constructor. It then deletes the Walnut instance
when it deletes the Squirrel instance, after calling the ~Squirrel() destructor.
The code performs these steps for each instance of Squirrel.

The code below creates two Squirrels on the heap by using pointers and calling new.

Squirrel *Sam = new Squirrel;


Squirrel *Sally = new Squirrel;

But you could also create them on the stack by declaring them without pointers:

With Stack Variables

Squirrel Sam;
Squirrel Sally;

If you do this, the application will run fine, provided that you remove the delete lines.
You do not delete stack variables. The computer calls the destructor when the main()
function ends. That’s the general rule with objects on the stack: They are created when
you declare them, and they stay until the function ends.
4. Adding Parameters To Constructors

Like other methods, constructors allow you to include parameters. When you do,
you can use these parameters in the initialization process. To use them, you list
the arguments inside parentheses when you create the object:

Squirrel(string StartName);

Then, you create a new instance like so:

Squirrel *Sam = new Squirrel("Sam");

The constructor is expecting a string, so you pass a string when you create the
object.

The SquirrelClass example, presents an application that includes all the basic elements
of a class with a constructor that accepts parameters.

Example program :

// Placing Parameters in Constructors

#include <iostream>
using namespace std;
class Squirrel {
private:
string Name;
public:
Squirrel(string StartName);

void WhatIsMyName();
};

Squirrel::Squirrel(string StartName) {
cout << "Starting!" << endl;
Name = StartName;
}

void Squirrel::WhatIsMyName() {
cout << "My name is " << Name << endl;
}

int main()
{
Squirrel *Sam = new Squirrel("Sam");
Squirrel *Sally = new Squirrel("Sally");
Sam->WhatIsMyName();
Sally->WhatIsMyName();
delete Sam;
delete Sally;
return 0;
}

In main(), you pass a string into the constructors. The constructor code takes the
StartName parameter and copies it to the Name property. The WhatIsMyName()
method writes Name to the console.

5. Objects’s Dynamic Memory Allocation Through Initialization Lists

a. Explicit Initialization

To initialize an object explicitly, you can state its initial values in parentheses when you call
new using an initialization list .

Syntax: Type *ptr = new Type(initializing_list);

The values in the initialization list are passed as arguments to the constructor. If the
compiler is unable to locate a suitable constructor, an error message occurs.

class planet

public:

string name;
string size;
string galaxy;
string type;

planet(string name,string size,string galaxy, string type){


this->name = name;
this->size=size;
this->galaxy= galaxy;
this->type=type;

planet earth(“earth”,”medium”,”oreon”,”water”)
planet *ptr= new planet (“earth”,”medium”,”oreon”,”water” )

The delete keyword calls the destructor of the object pointed to by the pointer
provided to it, and then gives the memory we initially requested back to the operating
system.

It is of absolute importance that, for each new expression, we call the delete expression
only once, with the same returned pointer.
If we forget to call the delete function on an object returned by calling the new function,
we will have two major problems:

• The memory will not be returned to the operating system when we do not need it
anymore. This is known as a memory leak. If this repeatedly happens during the
execution of the program, our program will take more and more memory, until it
consumes all the memory it can get.

• The destructor of the object will not be called.

We can now see why it is extremely important to define a virtual destructor in the base
class if we derive from it: we need to make sure that the destructor of the runtime
type is called when calling the delete function on the base object. If we call delete on a
pointer to the base class while the runtime type is the derived class, we will only call the
destructor of the base class and not fully destruct the derived class.
Making the destructor of the base class virtual will ensure that we are going to call the
derived destructor, since we are using dynamic dispatch when calling it.

Note

For every call to the new operator, there must be exactly one call to delete with the
pointer returned by new!
This error is extremely common and leads to many errors.

b. Calling new to create arrays of objects

Like single objects, we can also use dynamic memory to create arrays of objects. For
such use cases, we can use the new[] and delete[] expressions:

int n = 15;
Car* cars = new Car[n];
delete[] cars;

The new[] expression will create enough space for n Car instances and will initialize
them, returning a pointer to the first element created. Here, we are not providing the
arguments to the constructor, so the class must have a default constructor.
With new[], we can specify how many elements we want it to initialize. This is different
from std::array and the built-in array we saw earlier because n can be decided at
runtime.

We need to call delete[] on the pointer returned by new[] when we do not need the
objects anymore.

Function Example :

The function will call the new expression in its body to create an instance of the correct
class, and it will then return a pointer to the base class so that the code calling it does
not need to know about the type of logger created:

Logger* createLogger() {

if (are_tests_running()) {

TestLogger* logger = new TestLogger();


return logger;

} else {

ReleaseLogger logger = new ReleaseLogger("Release logger");

return logger; }

There are three things to note in this function:

● Even if we wrote the new expression twice, new will be called only once per
function call.

● This shows us that it is not enough to make sure we type new and delete an equal
number of times; we need to understand how our code is executed.

● There is no call to delete! This means that the code calling the createLogger
function needs to make sure to call delete.
From these two points, we can can see why it is error prone to manage memory
manually, and why it should be avoided whenever possible.

Let's look at an example of how to call the function correctly:

Logger* logger = createLogger();

myOperation(logger, argument1, argument2);

delete logger;

If myOperation does not call delete on the logger, this is a correct use of dynamic
memory. Dynamic memory is a powerful tool, but doing it manually is risky, error prone,
and easy to get wrong.

Fortunately, modern C++ provides some facilities to make all this much easier to do. It is
possible to write entire programs without ever using new and delete directly.
We will see how in the next section.

6. Nested Class Declarations

Declaring a class inside another class is called nesting a class.

Since a nested class declaration happens inside the outer class, the outer class has access
to all the declared names as if it were part of the outer class: it can access even private
declarations
On the other hand, a nested class is not associated with any instance, so it can only
access static members.

I. Example To Access a Nested Class :

// Declaration
class Coordinate {
...
struct CoordinateDistance {
float x = 0;
float y = 0;
static float walkingDistance(CoordinateDistance distance);
}
};

// Create an instance of the nested class CoordinateDistance


Coordinate::CoordinateDistance distance;

//Invoke the static method walkingDistance declared inside the


nested class CoordinateDistance

Coordinate::CoordinateDistance::walkingDistance(distance);

7. Static Member Functions

Since static data members are independent of any objects, access to them should also be
independent of any objects. Static member functions are used for this purpose. For example,
you can call a static member function for a class even though no objects exist in that class.
The static keyword is used to define static member functions.

Example: static double getMin(); // Within class.

Definitions outside of the class do not need to repeat the static keyword.
A static member function can be called using any object belonging to the class or,
preferably, using a range operator.

Example: temperature.setMax(42.4); // Equivalent


Result::setMax(42.4); // Calls.

Calling a static member function does not bind the function to any class object. The
this pointer is therefore unavailable, in contrast to normal member functions.
This also means that static member functions cannot access data members and methods
that are not static themselves.

Note

Note that we do not repeat the static keyword.


It is important to define the values of the static variables in the .cpp file. If we
define them inside the header file, the definition will be included anywhere inside
the header, which will create multiple definitions, and the linker will complain.

Always use const when declaring a static field. Any instance can access the static
fields of its class; if they are mutable, it becomes extremely hard to track down
which instance is modifying the value. In programs that use multiple threads, it is
common to create bugs by modifying the static fields from different threads at the
same time.

8. Using const Member Functions In Classes (const methods)


The member functions of a class can be qualified as const, which means that the
function limits its access to be read-only. Moreover, a member function is required
to be const when it accesses const member data.

To declare a member function as const, we use the const keyword in the function
declaration after the function name and before its body:

const std::string& getColor() const {


// Function body
}

The const member function will be called when an object is declared const; otherwise,
the non-const function is called. Let's examine the following code:

class Car {

std::string& getColor() {}

const std::string& getColor() const {}

};

Car car;

// Call std::string& getColor()

car.getColor();

const Car constCar;

// Call const Color& getColor() const

constCar.getColor();

The following examples show three versions of the const function:


1. The first one is a const member function: type& function() const {}
2. The second returns a const reference: const type& function() {}
3. The third one is a const function that returns a const reference:
const type& function() const {}

9. THE THIS KEYWORD

When the this keyword is used in the class context, it represents a pointer whose
value is the address of the object on which the member function is called. It can appear
within the body of any non-static member function.
In the following example, setColorToYellow() and setColorToGreen() perform the
same action. Both set a data member, but the former uses the this keyword to refer to the
current object:

class Car {
std::string color;

// explicit use of this


void setColorToYellow(){ this->color = "yellow";}

// same as this->color = "green";

void setColorToGreen(){
color = "green"; }

};

Note

pointer->member is a convenient way to access the member of the struct pointed


by pointer. It is equivalent to (*pointer).member.

Up until now, we have learned how to declare data members, how to use them in
functions with a public specifier, and how to access them. Now, let's explore how to set a
value to them.

10. Pointers to Objects


// listing a pointers to objects example
//object creation and access to properties using pointers

class Car {
bool start ;
bool brake;
int km ;
}

Car car1;

Car* ptrToCarOne=&car1;

(*car1).start= false;
(*car1).brake=false;
(*car1).km=20000;

An alternative Way to Initialize An Object’s Property

car1->start=false;
car1->brake= false;
car1-> km =10000;

CAR car1;

Note

It is important to distinguish between a const function and a function returning a


const type. Both make use of the same const keyword, but in different places in
the function prototype. They express a different concept and are independent

11. References To Objects

Example :

// listing a reference to objects example


//object creation and access to properties using references
class Car {
bool start ;
bool brake;
int km ;
}

Car car1;

Car &ptrToCarOne= car1;

car1.start= false;
car1.brake=false;
car1.km=20000;

12. Structs

Classes can be defined by the struct keyword.


The main difference consists in the way data is encapsulated. In structs every property
has public access unless the private keyword is employed.

struct Book {
char name[256];
int year;
int pages;
bool hardcover;};

int main() {

Book neuromancer;
neuromancer.pages = 271;
std::cout<<neuromancer.pages<<std::endl;

13. Dynamic Memory Allocation in Classes // revisar explicacion

Example I :

// Creating objects using the this keyword and allocating them in


the heap

#include <iostream>
using namespace std ;

class book{

public:

string title;
string edition;
int year;

book( string title, string edition, int year){


this->title=title;
this->edition=edition;
this->year=year;}

};

int main (){

book *book1;
book1= new book ("A christmas Tale","newark",2010);

std::cout<<book1->title<<std::endl;

return 0;

REFERENCE

create a reference and allocate an object in the heap

book &book2 = *(new book)

Example II :

// A class representing dynamic arrays of floats


Example III :

// array of objects
14. Unions

a. Memory Usage

In normal classes, each data member belonging to an object has its own separate memory
space. However, a union is a class whose members are stored in the same memory space.
Each data member has the same starting address in memory. Of course, a union cannot
store various data members at the same address simultaneously. However, a union does
provide for more versatile usage of memory space.

b. Definition

Syntactically speaking, a union is distinguished from a class or a struct only by the keyword
union.
This example defines the union Number and two objects of the same type. The union
Number can be used to store either integral or floating-point numbers.
Unless a private label is used, all union members are assumed to be public. This
is similar to the default setting for structures. This allows direct access to the members n
and x in the union Number.

Example :

number1.n = 12345; // Storing an integer


number1.n *= 3; // and multiply by 3.
number2.x = 2.77; // Floating point number

CHAPTER…19

1. Enums

An enumeration is a user-definable, integral type. An enumeration is defined using the


enum keyword. A range of values and a name for these values are also defined at the
same time. The values defined are constants.

There are two types of this data structure:

a. Unscoped Enums

This statement defines the enumerated type Shape.


Their values can be deduced from the list order.

Similar to arrays, The first constant has a value of 0, and so on .

In the previous example, Line thus represents a value of 0, Rectangle a value of 1,


and Ellipse a value of 2.

A Shape type variable can only assume one of these values.


You can leave out the type name, if you only need to define the constants.

This statement defines the constants OFF and OUT, setting their value to 0, and the
constants ON and IN with a value of 1. The values for OFF and ON are implicit.

b. Scoped Nums

Also called an enum class, it has the following syntax.

Syntax : enum class shape {Line, Rectangle, Ellipse};

To access the values you need to use the name of the type followed by the scope operator:

shape shape= shape:: Rectangle;

c. Using a Switch Statement with an Enumeration Class

enum class shape {Line, Rectangle, Ellipse};


shape shape = shape::Rectangle;

switch(shape) {
case shape::Line: {
printf("You work hard.");}
break;
case shape::Rectangle: {
printf("You are very strong.");
} break;
Chapter …

1. INHERITANCE

Inheritance allows the combination of one or more classes.

A. Derived Classes

To derive a class means to extend the properties of the base class to another class so you
don't have to rewrite the same data members all over again . The new class inherits (shares)
the properties from the main class.

Let’s put it down to earth:

You define a class named vehicle as the main class:

class Vehicle {
public:
int NumberOfWheels;

void Drive() {
cout << "Driving, driving, driving..." << endl;
} };

You then create a class named Toyota and you want it to have the fields and methods
from the vehicle class given that Toyota belongs to a vehicle brand (vehicle
class).

You could define the class toyota with the same data members contained in the
vehicle class but that’d be a lot of overwork to rewrite the same code for each new brand
class you want to define.

That’s when derived classes come into play to facilitate this process,which is called
inheritance.

In order to create a derived class just follow these steps:

Class keyword - Class_name : the public keyword - main_class_name

class Toyota : public Vehicle {

public:

void MeAndMyToyota() {
cout << "Just me and my Toyota!" << endl; }
};

When you do so, the class you create (Toyota) inherits the properties and methods
from the parent class (Vehicle).

For example, if Vehicle has a public property named NumberOfWheels and a public
method called Drive(), the class Toyota has these members, although you didn’t explicitly
write them in the class.

The definition of the Toyota class doesn’t show a Drive() function. The Drive() function
is inherited from the Vehicle class.So even if it’s not explicitly defined in the derived
class , it has access to this method.

#include <iostream>
using namespace std;

class Vehicle {

public:

int NumberOfWheels;

void Drive() {
cout << "Driving, driving, driving..." << endl; } };

class Toyota : public Vehicle {

public:

void MeAndMyToyota() {
cout << "Just me and my Toyota!" << endl;}
};

int main() {

Toyota MyCar;

MyCar.MeAndMyToyota();

MyCar.Drive();

return 0;

When you run this application, you see the output from two functions:
Just me and my Toyota!

Driving, driving, driving…

B. Understanding types of inheritance

When you create a class, its methods can access both public and private properties
and methods. Users of the class can access only the public properties and
methods.

I. Access to Private Members of the Base Class

When you derive a new class, it cannot access the private members in
the parent class. Private members are reserved for a class itself and not for any
derived class.

When members need to be accessible by derived classes you only have indirect access to
the private data members of the base class so one way to access them is through the
public defined base class methods.these are called setters and getters. you define functions
in the public section of the class that can access the class private data members.
you saw that in the time example where you had get methods that return hour, minute and
second.
I’ll leave another example for practicality

class Employee {
private:
// Private attribute
int salary;

public:
// Setter
void setSalary(int s) {
salary = s;
}
// Getter
int getSalary() {
return salary;
}
};

int main() {
Employee myObj;
myObj.setSalary(50000);
cout << myObj.getSalary();
return 0;
}

Although there’s a specification you can use beyond public and private: protected.

Protected members and private members work the same way from a user perspective,
but derived classes can access both protected and public members.
Private members are hidden from both users and derived classes.

Always use protected members when possible when you plan to derive classes from
a parent class.

II. Constructor Calls

The constructor of a derived class is required to create an object of the derived class type.

As the derived class contains all the members of the base class, the base class objects must
also be created and initialized.

hint :

both constructors must be called (derived class and base class


constructors);

The order in which the constructors are called is important.

The base class constructor is called first, then the derived class
constructor. The object is thus constructed from its core outwards.
// C++ program to show the order of constructor call
// in single inheritance

#include <iostream>
using namespace std;

// base class
class Parent
{
public:

// base class constructor


Parent() { cout << "Inside base class" << endl;}
};

// sub class

class Child : public Parent {


public:

//sub class constructor


Child() { cout << "Inside sub class" << endl; }
};

// main function
int main() {
// creating object of sub class
Child obj;

return 0;
}

output

inside base class


inside sub class

How to call the parameterized constructor of base class in derived class constructor?

// C++ program to show how to call parameterized Constructor


// of base class when derived class's Constructor is called

#include <iostream>
using namespace std;

// base class
class Parent {

int x;

public:

// base class's parameterized constructor

Parent(int i) {
x = i;
cout << "Inside base class's parameterized constructor" << endl; }
};

// sub class

class Child : public Parent {


public:

// sub class's parameterized constructor

Child(int x): Parent(x) {

cout << "Inside sub class's parameterized constructor" << endl; }


};

// main function
int main()
{

// creating object of class Child


Child obj1(10);
return 0;
}

III. Destroying Objects

When an object is destroyed, the destructor of the derived class is first called, followed by
the destructor of the base class.

The reverse order of the constructor calls applies.

You need to define a destructor for a derived class if actions performed by the constructor
need to be reversed. The base class destructor need not be called explicitly as it is executed
implicitly.

IV. Calling Methods in the Base Class

// safe.h : The classes Safe and Castle

#include <iostream>
using namespace std;

class Safe {

private:

int topSecret;
protected:

int secret;
void setTopSecret( int n) { topSecret = n;}
int getTopSecret() const { return topSecret;}
void setSecret( int n){ secret = n;}
int getSecret() const { return secret;}

public:

int noSecret;

Save(){ topSecret = 100; secret = 10; noSecret = 0; }


};

//derived class

class Castle : public Safe {

public:

Castle()
{
// topSecret = 10; // Error, because private
setTopSecret(10); // ok, because protected
secret = 1; // ok, because protected
noSecret = 0; // ok, because public
}

void test()
{
// topSecret = 200; // Error, because private
setTopSecret(200); // ok, because protected
secret = 20; // ok, because protected
noSecret = 2; // ok, because public
}
};

The classes Safe and Castle, show that protected members of


the base class can be accessed directly in a derived class. In contrast to this, protected
members are inaccessible to users of these classes.
Example:

Castle treasure;
treasure.topSecret = 1; // Error: private
treasure.secret = 2; // Error: protected
treasure.setTopSecret(5); // Error: protected
treasure.noSecret = 10; // ok

Note :

Protected declarations should be used with caution. If you change the declaration of a
protected member, every class derived from this class must be examined to ascertain
whether additional modifications are necessary

V. Protected Data Members

Let's look at an example of inheritance:

class Vehicle {
public:
TankLevel getTankLevel() const;
void turnOn();
};

class Car : public Vehicle {


public:
bool isTrunkOpen();
};

In this example, the Car class inherits from the Vehicle class. Here Vehicle is the
base class, and Car is the derived class.

VI. Declaring multiple derived classes

When defining a class, we can specify the classes it derives from by appending :
followed by one or more classes, separated by a comma:

class Car : public Vehicle, public Transport {}

When specifying the list of classes to derive from, we can also specify the visibility of
the inheritance – private, protected, or public.

The methods of the base class can be accessed as methods of the derived class based on
the following rules:
● Car car;
● car.turnOn();

1. Only the derived class and classes that derive from it know about inheritance;
2. External code sees the two classes as unrelated.

3. When deriving with a private modifier, all the public and protected methods and fields
of the base class are accessible by the derived class as private.

4. The private methods and fields of a class are never accessible outside of that class.

5. Accessing the fields of the base class follows the same rules.

Let's see a summary:

VII. One More Example


The Orange class can be derived from a Citrus class, which is in turn derived from a
Fruit class. Here is how it can be written:

class Fruit {
};

class Citrus: public Fruit {


};

class Orange: public Citrus {


};

The class Citrus can access the public and protected methods of class
Fruit, whereas class Orange will be able to access both Citrus' and Fruit's
public and protected methods . (Fruit's public methods are accessible
through Citrus).

Note

If you use inheritance to group together some functionality when implementing a


class, it is often correct to use private inheritance, as that is a detail of how you
are implementing the class, and it is not part of the public interface of the class.
If, instead, you want to write a derived class that can be used in place of the base
class, use public inheritance.

When inheriting from a class, the base class gets embedded into the derived class. This
means that all the data of the base class also becomes part of the derived class in its
memory representation:

VIII. Representation Of Derived Classes


A. We need to initialize the base class when we initialize the derived class, otherwise,
part of the class would be left uninitialized. When do we initialize the base class?

class A {
public:
A(const std::string& name);
};

class B: public A {
public:
B(int number) : A("A's name"), d_number(number) {}
private:
int d_number;
};
}

When B's constructor is called, the A needs to be initialized.


Since A doesn't have a default constructor, the compiler cannot initialize it for us: we have
to call A's constructor explicitly.

The copy constructor and the assignment operator generated by the compiler take
care of calling the constructor and operator of the base class.
When, instead, we write our implementation of the copy constructor and the assignment
operators, we need to take care of calling the copy constructor and assignment operator.
chapter … Friend Classes and functions

1. introduction to friend classes


A friend class can access private and protected members of other classes in which it is
declared as a friend. It is sometimes useful to allow a particular class to access private and
protected members of other classes.
For example, a LinkedList class may be allowed to access private members of Node.
We can declare a friend class in C++ by using the friend keyword.

Syntax:

friend class class_name; // declared in the base class

Note: We can declare friend class or function anywhere in the base class body whether its
private, protected or public block. It works all the same.

2. Friend Function

Like a friend class, a friend function can be granted special access to private and protected
members of a class in C++. They are the non-member functions that can access and
manipulate the private and protected members of the class for they are declared as friends.
A friend function can be:

I. A global function
II. A member function of another class

Syntax:
friend return_type function_name (arguments); // for a
global function. or

friend return_type class_name::function_name


(arguments); // for a member function of another class

I. 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++:

Example:

// C++ program to create a global function as a friend


// function of some class

#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;
}

// driver code

int main() {

base object1;

friendFunction(object1);

return 0;
}

Output:

Private Variable: 10
Protected Variable: 99

In the above example, we have used a global function as a friend function. In the next
example, we will use a member function of another class as a friend function

II. Member Function of Another Class as Friend Function

We can also declare a member function of another class as a friend function in C++. The
following example demonstrates how to use a member function of another class as a friend
function in C++:

Example:

// C++ program to create a member function of another class


// as a friend function

#include <iostream>
using namespace std;

// forward definition needed

class base;

// another class in which function is declared

class anotherClass {

public:

void memberFunction(base& obj);


};

// base class for which friend is declared

class base {

private:

int private_variable;

protected:

int protected_variable;

public:

base(){
private_variable = 10;
protected_variable = 99;
}

// friend function declaration

friend void anotherClass::memberFunction(base&);


};

// friend function definition

void anotherClass::memberFunction(base& obj)


{
cout << "Private Variable: " << obj.private_variable
<< endl;
cout << "Protected Variable: " << obj.protected_variable;
}
// driver code

int main()
{
base object1;
anotherClass object2;
object2.memberFunction(object1);

return 0;
}

Output

Private Variable: 10
Protected Variable: 99

Note: The order in which we define the friend function of another class is important and
should be taken care of. We always have to define both the classes before the function
definition. That's why we have used out of class member function definition.

3. Features Of Friend Functions

● A friend function is a special function in C++ that in spite of not being a member
function of a class has the privilege to access the private and protected data of a
class.

● A friend function is a non-member function or ordinary function of a class, which is


declared as a friend using the keyword “friend” inside the class. By declaring a
function as a friend, all the access permissions are given to the function.

● The keyword “friend” is placed only in the function declaration of the friend function
and not in the function definition or call.

● A friend function is called like an ordinary function. It cannot be called using the
object name and dot operator. However, it may accept the object as an argument
whose value it wants to access.
● A friend function can be declared in any section of the class i.e. public or private or
protected.

Below are some more examples of friend functions in different scenarios:

chapter … plymorphism 21

Types of Polymorphism

1. Compile-time Polymorphism
2. Runtime Polymorphism

1. Compile-Time Polymorphism

This type of polymorphism is achieved by function overloading or operator overloading.

A. Function Overloading

When there are multiple functions with the same name but different parameters, then the
functions are said to be overloaded. Functions can be overloaded by changing the number
of arguments or/and changing the type of arguments

// C++ program to demonstrate


// function overloading or
// Compile-time Polymorphism

#include <bits/stdc++.h>

using namespace std;

class Geeks {

public:

// Function with 1 int parameter

void func(int x)
{
cout << "value of x is " << x << endl;
}

// Function with same name but


// 1 double parameter

void func(double x)
{
cout << "value of x is " << x << endl;
}

// Function with same name and


// 2 int parameters

void func(int x, int y){


cout << "value of x and y is " << x << ", " << y
<< endl; } };

// Driver code

int main()
{
Geeks obj1;

// Function being called depends


// on the parameters passed

// func() is called with int value


obj1.func(7);

// func() is called with double value

obj1.func(9.132);

// func() is called with 2 int values

obj1.func(85, 64);
return 0;
}

Output

value of x is 7
value of x is 9.132
value of x and y is 85, 64

2. Runtime Polymorphism or dynamic binding

This type of polymorphism is achieved by Function Overriding. The function call is resolved
at runtime in runtime polymorphism. In contrast, with compile time polymorphism, the
compiler determines which function call to bind to the object after deducing it at runtime.

A. Dynamic Binding

Dynamic binding is the ability for a reference or a pointer of a base type to point to an
object of a derived type at runtime. Let's explore the following example:

struct A { }; // base type A

struct B: A{}; // derived type B

struct C: A {};// derived type C

// We can write. main function omitted

B b;
C c;
A& ref1 = b;
A& ref2 = c;
A* ptr = nullptr;

if (runtime_condition()) {
ptr = &b;

} else {

ptr = &c;

Note

1. To allow dynamic binding, the code must know that the derived class derives from
the base class.

2. If the based class access is private , the derived class will be able to bind the object
to a pointer or reference of the base class.

3. If the inheritance is protected, then the derived class and every class deriving from it
will be able to perform dynamic binding.

4. Finally, if the inheritance is public, the dynamic binding will always be allowed.

This creates the distinction between the static type and the dynamic (or run-time) type.
The static type is the type we can see in the source code.
In this case, we can see that ref1 has a static type of a reference to the A struct.
The dynamic type is the real type of the object: the type that has been constructed in
the object's memory location at runtime.

For example, the static type of both ref1 and ref2 is a reference to the A struct, but
the ref1 dynamictype is B, since ref1 refers to a memory location in which an object of
type B has been created, and the ref2 dynamic type is C for the same reason.

As said, the dynamic type can change at runtime. While the static type of a variable is
always the same, its dynamic type can change: ptr has a static type, which is a pointer
to A, but its dynamic type could change during the execution of the program:

A* ptr = &b; // ptr dynamic type is B


ptr = &c; // ptr dynamic type is now C

B. Dynamic Dispatch
As said before, C++ supports dynamic dispatch. This is done by marking a method with a
special keyword: virtual.

If a method is marked with the virtual keyword, when the method is called on a
reference or a pointer, the compiler will execute the implementation of the dynamic
type instead of the static type.

C. Function Overriding

Runtime Polymorphism with Data Members

// C++ program for function overriding with data members

#include <bits/stdc++.h>

using namespace std;

// base class declaration.


class Animal {

public:

string color = "Black";


};

// inheriting Animal class.

class Dog : public Animal {

public:

string color = "Grey";


};

// Driver code

int main(void)
{
Animal d = Dog(); // accessing the field by reference
// variable which refers to derived
cout << d.color;
}

Output :

Black
D. Virtual Function

A virtual function is a member function that is declared in the base class using the keyword
virtual and is re-defined (Overridden) in the derived class. It tells the compiler to perform
dynamic binding

Some Key Points About Virtual Functions:

● Virtual functions are Dynamic in nature.

● They are defined by inserting the keyword “virtual” inside a base class and are
always declared with a base class and overridden in a child class

● A virtual function is called during Runtime


Now, we’ll look at an example without using the concepts of virtual function to clarify your
understanding.

// C++ program to demonstrate how we will calculate


// area of shapes without virtual function

#include <iostream>
using namespace std;

// Base class

class Shape {

public:

// parameterized constructor

Shape(int l, int w) {

length = l;
width = w;
}

int get_Area() {

cout << "This is call to parent class area\n";

return 1;
}

protected:
int length, width;
};

// Derived class

class Square : public Shape {

public:

// declaring and initializing derived class

Square(int l = 0, int w = 0 : Shape(l, w){}

// constructor

int get_Area() {

cout << "Square area: " << length * width << '\n';

return (length * width);

};

// Derived class

class Rectangle : public Shape {

public:

// declaring and initializing derived class

Rectangle(int l = 0, int w = 0) : Shape(l, w){}

// constructor

int get_Area(){

cout << "Rectangle area: " << length * width << '\n';

return (length * width);}

};
int main(){

Shape* s;

// object of child class Square

Square sq(5, 5);

// object of child class Rectangle

Rectangle rec(4, 5);

s = &sq; // reference variable

s->get_Area();

s = &rec; // reference variable

s->get_Area();

return 0; // too tell the program executed

// successfully

Output

This is call to parent class area

This is call to parent class area

In the above example:

● We store the address of each child’s class Rectangle and Square object in s
● Then we call the get_Area() function on it,

● Ideally, it should have called the respective get_Area() functions of the child
classes but

● Instead, it calls the get_Area() defined in the base class.

● This happens due to static linkage which means the call to get_Area() is
getting set only once by the compiler which is in the base class.

E. Virtual Methods

In this section, we will take an in-depth look at how to tell the compiler to perform
dynamic dispatch on a method. The way to specify that we want to use dynamic
dispatch for a method is to use the virtual keyword.

The virtual keyword is used in front of a method when declaring it:

class Vehicle {
public:
virtual void turnOn(); };

We need to apply the virtual keyword to the type we are using in the
code. Let's examine the following exercise to explore the virtual keyword.

Exercise 22: Exploring the Virtual Method

We can write a function that accepts a reference to a base class, call methods on this base
class, and the methods of the derived classes will be executed:

void safeTurnOn(Vehicle& vehicle) {

if (vehicle.getFuelInTank() > 0.1 && vehicle.batteryHasEnergy()) {

vehicle.turnOn(); }
}

We can then call the function with many different types of vehicles,
and the appropriate methods will be executed:

Car myCar;
Truck truck;
safeTurnOn(myCar);
safeTurnOn(truck);

Let's create a program using the concept of inheritance using the virtual keyword:

1. First, make sure to add the required header file and namespace to compile the
program.

2. Now, add the Vehicle class as illustrated:

class Vehicle {
public:
void turnOn() {
std::cout << "Vehicle: turn on" << std::endl;}};

3. In the Car class, add the virtual keyword as illustrated :

class Car : public Vehicle {


public:
virtual void turnOn() {
std::cout << "Car: turn on" << std::endl;}};

void myTurnOn(Vehicle& vehicle) {


std::cout << "Calling turnOn() on the vehicle reference" <<
std::endl;
vehicle.turnOn(); }

4. Now, in the main function, invoke the Car class and pass the car object in the
myTurnOn() function:

int main() {
Car car;
myTurnOn(car); }

The output will be as follows:


Calling turnOn() on the vehicle reference
Vehicle: turn on

Here, the call will not be dynamically dispatched, and the call to the implementation
of Vehicle::turnOn() will be executed. The reason is that the static type of the variable
is Vehicle, and we did not mark the method as virtual, so the compiler uses static
dispatch.

The fact that we wrote a Car class that declares the method virtual is not important,
since the compiler only sees the Vehicle class being used in myTurnOn(). When a
method is declared virtual, we can override it in a derived class.

To override a method, we need to declare it with the same signature as the parent class:

● return type,
● name,
● parameters (including const-ness and ref-ness),
● const qualifier,
● the other attributes.

When a class overrides a virtual method of the base class, the method of the most
derived class will be executed when the method is called on a base class. This is true
even if the method is called from inside the base class, for example:

struct A {
virtual void foo() {
std::cout << "A's foo" << std::endl;}
};

struct B: A {
virtual void foo() override {
std::cout << "B's foo" << std::endl;}
};

struct C: B {
virtual void foo() override {
} };

int main() {

B b;
C c;
A* a = &b;
a->foo(); // B::foo() is executed
a = &c;
a->foo();

/* C::foo() is executed, because it's the most derived Class


overriding
foo(). */

Note

If a method is marked virtual, then the destructor should also be marked virtual.

chapter …. INTERFACES ABSTRACT CLASSES AND MULTIPLE INHERITANCE 22

1. Interfaces in C++

Functions and methods which accepts parameters as interface are a way of saying: in
order to perform my actions, I need these functionalities; it's up to you to provide them.

To specify an interface in C++, we can use an Abstract Base Class (ABC):

Abstract: This means that it cannot be instantiated.

Base: This means it is designed to be derived from.

Any class that defines a pure virtual method is abstract.


A pure virtual method is a virtual method that ends with = 0.

For example:

class Vehicle {

public:

virtual void turnOn() = 0;


};

A pure virtual method is a method that does not have to be defined. Nowhere in the
previous code have we specified the implementation of Vehicle::turnOn(). Because of
this, the Vehicle class cannot be instantiated, as we do not have any code to call for its pure
virtual methods.

We can instead derive from the class and override the pure virtual method. If a class
derives from an abstract base class, it can be either of the following:

• Another abstract base class if it declares an additional pure virtual method, or if it


does not override all the pure virtual methods of the base class

• A regular class if it overrides all the pure virtual methods of the base class

Let's continue with the previous example:

class GasolineVehicle: public Vehicle {

public:
virtual void fillTank() = 0;
};

class Car : public GasolineVehicle {


virtual void turnOn() override {}
virtual void fillTank() override {}

};

In this example, Vehicle is an abstract base class and GasolineVehicle is too, since it
does not override all the pure virtual methods of Vehicle. It also defines an additional
virtual method, which the Car class overrides together with the Vehicle::turnOn()
method. This makes Car the only concrete class, a class that can be instantiated.
The same concept applies when a class is deriving from multiple abstract base classes:
all the pure virtual methods of all the classes that need to be overridden in order to
make the class concrete and thus instantiable.
While abstract base classes cannot be instantiated, we can define references and
pointers to them.

Note

If you try to instantiate an abstract base class, the compiler will give an error
specifying which methods are still pure virtual, thus making the class abstract.
Functions and methods that require specific methods can accept references and
pointers to abstract base classes, and instances of concrete classes that derive from
them can be bound to such references.

It is good practice for the consumer of the interface to define the interface.
A function, method, or class that requires some functionality to perform its actions
should define the interface. Classes that should be used with such entities should
implement the interface.

Since C++ does not provide a specialized keyword for defining interfaces and interfaces
are simply abstract base classes, there are some guidelines that it's best practice to
follow when designing an interface in C++:

• An abstract base class should not have any data members or fields.
The reason for this is that an interface specifies behavior, which should be
independent of the data representation. It derives that abstract base classes
should only have a default constructor.

• An abstract base class should always define a virtual destructor.

The definition of a destructor should be the default one: virtual ~Interface() =


default. We are going to see why it is important for the destructor to be virtual
later.

• All the methods of an abstract base class should be pure virtual.


The interface represents an expected functionality that needs to be implemented;
a method which is not pure is an implementation. The implementation should be
separate from the interface.

• All of the methods of an abstract base class should be public.


Similar to the previous point, we are defining a set of methods that we expect to
call. We should not limit which classes can call the method only to classes deriving
from the interface.

• All the methods of an abstract base class should be regarding a single functionality.
If our code requires multiple functionalities, separate interfaces can be created,
and the class can derive from all of them. This allows us to compose interfaces
more easily.

Consider disabling the copy and move constructors and assignment operators on the
interface. Allowing the interface to be copied can cause the slicing problem we were
describing before:

Car redCar;
Car blueCar;

Vehicle& redVehicle = redCar;


Vehicle& redVehicle = blueCar;
redVehicle = blueVehicle;

// Problem: object slicing!

With the last assignment, we only copied the Vehicle part, since the copy constructor
of the Vehicle class has been called. The copy constructor is not virtual, so the
implementation in Vehicle is called, and since it only knows about the data members
of the Vehicle class (which should be none), the ones defined inside Car have not been
copied! This results in problems that are very hard to identify.

A possible solution is to disable the interface copy and move construct and assign
operator: Interface(const Interface&) = delete; and similar.
This has the drawback of disabling the compiler from creating the copy constructor and
assign operators of the derived classes.

An alternative is to declare copy/move constructor/assignment protected so that only


derived classes can call them, and we don't risk assigning interfaces while using them.

chapter … 23

MULTIPLE INHERITANCE

1. Introduction

Up to now, we have seen examples of single inheritance: a derived class has a single
base class. C++ supports multiple inheritance: a class can derive from multiple classes.
Let's look at an example:
struct A {
};
struct B {
};
struct C : A, B {
};

In this example, the C struct derives both from A and from B.


The rules on how inheritance works are the same for single and multiple inheritance:
the methods of all the derived classes are available based on the access specified,
and we need to make sure to call the appropriate constructors and assign an operator
for all of the base classes.

It is usually best to have a shallow inheritance hierarchy: there should not be many
levels of derived classes.
When using a multi-level inheritance hierarchy or multiple inheritance, it's more likely
that you'll encounter some problems, such as ambiguous calls.
A call is ambiguous when the compiler cannot clearly understand which method to call.

Let's explore the following example:

struct A {
void foo() {}
};
struct B {
void foo() {}
};
struct C: A, B {
void bar() { foo(); }
};

In this example, it is not clear which foo() to call, A's or B's. We can disambiguate that by
prepending the name of the class followed by two columns: A::foo().

2. Abstract classes

If a class comprises pure virtual methods, you cannot create objects of this class type.

Example: Coworker worker("Black , Michael");

The compiler will issue an error message here, as the Coworker class contains the
pure virtual method income(). This avoids calling a method for worker that still
needs to be defined.
A class that does not allow you to create any objects is referred to as an abstract
class.
A class with pure virtual methods is an abstract class.

I. Deriving Abstract Classes

When a class is derived from an abstract class, it inherits all the methods the base class
contains, particularly the pure virtual methods.
If all of these pure virtual methods are implemented in the derived class, you can then create
an object of the derived class type

A class derived from a class containing pure virtual methods is a concrete class, if it contains
a definition for each pure virtual function.

This means you need to implement the income() method in the Laborer class
shown opposite. Since the hourly wage and the number of hours worked are both defined
for blue-collar workers, it is possible to implement that method.
A class derived from a concrete class can again contain pure virtual methods, due to
additional definitions in the derived class. In other words, an abstract class can be
derived from a concrete class.

An abstract class does not necessarily need to contain pure virtual functions. If the
class contains a protected constructor, objects of the class type cannot be created.
The constructor can only be called then by methods in derived classes. A constructor of
This type normally acts as base initializer, when an object of a derived class type is created.

3. multiple inheritance

I. Introduction
A class can contain not just one but several different base classes. In this case the class is
derived from multiple base classes in a process known as multiple inheritance.

II. The Multiply-Derived Class MotorHome

This class Car is used to represent vehicles and the class Home contains characteristic
values for an apartment, such as floor space, number and type of rooms, and typical
operations, such as building, selling, or renting.

Using these two classes you can then derive the MotorHome class. The opposite page
shows the inheritance and definition schemes for the new class. An object of the
MotorHome class contains both the members of Car and the members of Home. More
specifically, the object contains two base sub-objects of type Car and Home.

III. Accessibility of Base Classes

Since the MotorHome class has two public base classes, it assumes the public interfaces
of both classes. A MotorHome type object not only allows access to the additional
public members but to all the public members of the base classes Car and Home.
When defining a multiply-derived class, the accessibility, private, protected, or
public, must be defined separately for each base class. The MotorHome class could
have the public base class Car and the protected base class Home.

Example: class MotorHome:public Car,protected Home


{ . . . };

If the keyword is omitted, the base class will default to private.


Example: class MotorHome : public Car, Home
{ . . . };

This statement defines the public base class Car and the private base class Home.
This makes all the public members in Home private members of the derived class.
In multiple inheritance each public base class establishes an is relationship. This is
similar to simple inheritance. If the MotorHome class inherits two public base classes,
a motor-home is a special kind of motor vehicle and a special kind of home.

class SUV : public PassCar, public Van


{
// Here are additional methods and data members
};

IV. Multiple Identical Base Classes


When multiply-derived classes are defined, a direct base class cannot be inherited more
than once. The following statement causes the compiler to issue an error message.

Example: class B : public A, public A // Error


{ . . . };

A class can be derived from several classes that have a common base class, however.
This class is then referred to as a multiple indirect base class.

The inheritance graph on the opposite page shows the multiply-derived class SUV,
which was derived from the classes PassCar and Van. Both base classes were themselves
derived from the Car class. This makes Car a multiple indirect base class of the
SUV class.

Ambiguity

An object of the SUV class thus contains the members of Car twice. Access to members
of the Car class results in ambiguity.

Example: SUV mySUV(. . .);


cout << mySUV.getProd(); // Error

Both the base classes PassCar and Van contain a method called getProd(), which
they both inherited from the Car class. In this case the compiler cannot decide which
method is meant.
Ambiguity in the context of multiple inheritance is also possible when several base
classes contain members with identical names. If both the Home class and the Car
class contain a method called getNr(), the getNr() method cannot be correctly
identified in the following statement.

Example: MotorHome motorHome( . . .);


motorHome.getNr();

To resolve ambiguity of this kind, you can use the scope resolution operator to determine
which base class is meant.

Example: cout << motorHome.Home::getNr();


cout << mySUV.PassCar::getProd();

The getNr() method in the Home class is called first, followed by the getProd()
method inherited by PassCar from the Car class.
class PassCar : public virtual Car {

// Here are additional members


// of class PassCar

};

class Van : public virtual Car {


// Here are additional members
// of class Van

};

I. Declaration

A direct base class is declared virtual when a derived class is defined. You can use the
virtual keyword, which directly precedes the name of the base class.
In the definition scheme shown opposite, the Car class becomes the virtual base class
of PassCar and Van. However, the fact that the base class Car is virtual has no
significance at this point.

A virtual base class takes effect in cases of multiple inheritance. The following definition
Example: class SUV ensures that the SUV class only contains one instance of the virtual
base class Car. An object my of the SUV class gets sufficient memory for only one Car class
sub-object.

: public PassCar, public Van


{ . . . };

ensures that the SUV class only contains one instance of the virtual base class Car.
An object my of the SUV class gets sufficient memory for only one Car class sub-object.
More specifically, the statement does not cause ambiguity.

Example: cout<<"Producer: " << mySUV.getProd();

The following items are important with respect to virtual base classes:

■ A virtual base class stays virtual even if further derivations are built. Each class
derived from PassCar also has the Car class as a virtual base class.

■ You cannot change the declaration of an indirect base class to virtual.


You must therefore decide what classes are to be declared virtual when you design the
class hierarchy. Later modifications will require modifications to the source code of any
derived classes.
II, Initialization

When an object is created in a simply-derived class, the sub-objects of the base classes
are created first on all levels of the class hierarchy. The sub-object whose class is nearer
to the top of the inheritance graph is created first.

The order of the constructor calls is “top down” and follows the inheritance graph.
The activation order used for the constructors in simple inheritance has been generalized
for multiple inheritance.

Inheritance Graph

Again, the inheritance graph, also called sub-object lattice, has an important job to do.
When a derived class is defined, the following rules apply:
■ In cases of multiple inheritance, base classes are entered into the inheritance
graph from left to right in the order in which they were stated when the class was
defined. The graph opposite illustrates this point.

If the class hierarchy does not contain any virtual base classes, the following applies to
the activation order of the constructors.

■ The base class constructors are executed first, top-down and from left to right on
each level.

■ Finally, the constructor belonging to the current class, which is at the bottom of
the inheritance graph is executed.

If we look at the example on the opposite page, this means that the sub-objects of the
Base classes Base1, Base2, and Base3 are created in this order. Then the constructor
of MultiDerived is executed.

III. Base Initializers

The constructor for the class at the bottom end of the inheritance graph uses base initializers
to pass the values to the direct and indirect base classes. If the base initializer definition
is missing in a constructor definition, the default constructor of the base class is
automatically executed.
Initial values are thus passed to the base class constructors “bottom up.”
IV. Constructor Calls in Virtual Base Classes

When an object is created for a multiply-derived class, the constructors of the base
classes are called first. However, if there is one virtual base class in the class hierarchy,
the virtual base class constructor is executed before a constructor of a non-virtual base
class is called.
The constructors of the virtual base classes are called first, followed by the constructors of
non-virtual base classes in the order defined in the inheritance graph.

✓ NOTE

The constructor of a virtual base class is called with the arguments stated for the base
initializer of the last class to be derived, i.e. class at the bottom end of the inheritance graph.

✓ NOTE

The constructor of the virtual base class nearest the top of the inheritance graph is
executed first. This does not necessarily mean the top level of the class hierarchy, since a
virtual base class can be derived from a non-virtual base class.
In our example with the multiply-derived class SUV (Sport Utility Vehicle) the constructor
for the virtual base class Car is called first, followed by the direct base classes
PassCar and Van, and last but not least, the constructor of the SUV class.
The example opposite shows SUV containing a constructor with one base initializer.
Its arguments are passed to the constructor of the virtual base class Car.

For the purpose of initialization, it does not matter whether a class derived directly
from Car contains a base initializer or not. Base initializers for virtual indirect base
classes defined in the constructor of a direct base class are ignored. If the base classes
PassCar and Van also contained base initializers for the virtual base class Car, these
would be ignored too.

If the constructor for the last derived class does not contain a base initializer, the
default constructor is executed for each virtual base class. Whatever happens, a default
constructor must then exist in every virtual base class! Thus, base initializers that happen
to exist in base classes are also ignored.

Chapter ….. 24

1. Introduction To Operators

When you initialize a variable you can start performing operations with it like additions,
subtraction and many more according to the data type you’re working with.
I’ll introduce operators and their use cases.
there’s a wide range of operators that will let you work along the program to achieve its
objectives.
I. Binary Operators

The figure below makes clear why they’re called binary operators because to perform a
calculation you need two operands

a) ARITHMETIC OPERATORS

The following table shows the binary arithmetic operators that can be used in your C
program.
A recommendation is to always use the same type to avoid program errors.

Example :

Program Description: calculate meters based on centimeters , add them and print the
values.

#include <stdio.h>

int main () {

float meters=3.65;
float centimeters=4.25;

float centimeterToMeters= centimeters/100;

printf("%f", centimeterToMeters);

float sum = centimeterToMeters +meters;

printf("%f",sum);

return 0;

b) ASSIGNMENT OPERATORS

Different types of assignment operators are shown below:

“=”: This is the simplest assignment operator. This operator is used to assign the value on
the right to the variable on the left.
For example:

a = 10;
b = 20;
ch = 'y';

“+=”: This operator is a combination of ‘+’ and ‘=’ operators. This operator first adds the
current value of the variable on left to the value on the right and then assigns the result to
the variable on the left.

Example:

If initially value stored in a is 5. Then (a += 6) = 11.

“=”This operator is a combination of ‘-‘ and ‘=’ operators. This operator first subtracts the
current value of the variable on the left from the value on the right and then assigns the
result to the variable on the left.

Example:

(a -= b) can be written as (a = a - b)
If initially value stored in a is 8. Then (a -= 6) = 2.

“*=”This operator is combination of ‘*’ and ‘=’ operators. This operator first multiplies the
current value of the variable on left to the value on the right and then assigns the result to
the variable on the left

Example:

(a *= b) can be written as (a = a * b)
If initially value stored in a is 5. Then (a *= 6) = 30.

“/=”This operator is a combination of ‘/’ and ‘=’ operators. This operator first divides the
current value of the variable on the left by the value on the right and then assigns the result
to the variable on the left.

Example:

(a /= b) can be written as (a = a / b)
If initially value stored in a is 6. Then (a /= 2) = 3.

Program Description :
// C program to demonstrate
// working of Assignment operators

#include <stdio.h>

int main()

// Assigning value 10 to a
// using "=" operator
int a = 10;
printf("Value of a is %d\n", a);

// Assigning value by adding 10 to a


// using "+=" operator
a += 10;
printf("Value of a is %d\n", a);

// Assigning value by subtracting 10 from a


// using "-=" operator
a -= 10;
printf("Value of a is %d\n", a);

// Assigning value by multiplying 10 to a


// using "*=" operator
a *= 10;
printf("Value of a is %d\n", a);

// Assigning value by dividing 10 from a


// using "/=" operator
a /= 10;
printf("Value of a is %d\n", a);

return 0;
}

c) COMPARISON OPERATORS
Relational operators commonly called comparison operators are used to perform
comparison binary operations (you also need two operands) . they always return a boolean
value when evaluating the expressions . So they evaluate to true or false once the task is
finished.
you’ll see them most commonly used in action when writing conditionals ( it’ll be explained
in the next chapter)

Take a look at the table below and their use cases .

This table shows some examples :

Program Description : comparing if 5 is greater than 6.

#include <stdio.h>
int main () {

_Bool comparisonResult= 5>6;

printf("%\n", comparisonResult); // prints 0 which that means false

return 0;
}

d) LOGICAL OPERATORS

Logical operators comprise the boolean operators && (AND), || (OR), and ! (NOT).
They can be used to create compound conditions and perform conditional execution of a
program depending on multiple conditions.
A logical expression results in a value false or true, depending on whether the logical
expression is correct or incorrect, just like a relational expression.

In other words it takes two expressions . An expression can be defined as any code that
gets executed and ends up being a Value.

Example :

int sum = 2 + 2 ; // it ends up being a value assigned to the sum


variable

The following table shows variable A and B , their possible boolean values and compares
them using logical operators showing the returned boolean value.
I. && And Operator

The and operator && evaluates an expression and returns a boolean value (true or false) .
it will return true only if both operands are true, so the logical expression:

check the following program:

#include <stdio.h>

int main () {
int x=2;
int y=3;

// it’s going to return true if both expressions are true


// two compared expressions that returns a value (true)

_Bool result =(x>0 && y==3)

// returns 1 (true)

printf(“%d”,result);}

II. The OR operator ||


will return true only if at least one operand is true, so the value
of the expression

#include <stdio.h>

int main () {
int x=2;
int y=4;

_Bool result= ( x>0 || y==3);

// just because X is bigger than 0 . The expression comparison


returns true

printf(“%d”,result);

return 0;

}
III. The NOT operator !

Will return true only if its operand is false. If the variable flag contains the value false (or the
value 0), !flag returns the boolean value true.

Program Description: Not !operator example use.

// A remainder any value that does not equal 0 . When used along the Not! operator will
return 1. The Value 0 represents False when it comes to Boolean types and 1 represents
True.
Now comes the trick , any values exceeding 0 will be treated as truthy values .
when you apply this operator it returns the opposite boolean value.
Let's check this down below.

#include <stdio.h>

int main (){

int x=0; // this is considered a falsy value


int z=3;//this is considered a truthy value

printf(“%d”,!x ); // when applied it returns 1 that is the opposite

printf(“%d”,!x ); // when applied it returns 0. all values above 0


are considered truthy after the expression get evaluated it returns
0.

return 0;

};
3. Unary Arithmetic Operators

They require one operand to perform an operation.

Program description: increment and decrement variables

#include <stdio.h>

int main () {

int num=1;

// increment operator . adds 1 to num


num++;
printf("%d\n", num);

// adds 1 to num
num ++;
printf("%d\n",num);

// decrement operator. Subtracts 1 to num

num--;

printf("%d\n", num);

// subtracts one to num

num--;

printf("%d\n",num);

return 0;
};

Chapter 25

1. Conditionals / Control Flow Statements

A control flow statement is a statement that results in a choice being made as to which of two or
more paths to follow.

2. If Statements

if-statements are used to diagram the flow execution of a program


according to a certain condition.
you might have noticed that a program execution starts in the main
function from the first line of code until it reaches the return
statement.
if-statements let you execute a piece of code or skip its execution
depending on whether the expression provided in the condition
results or ends up being true or false .

here’s a snip syntax :


if (condition) {
// block of code to be executed if the condition is true
}

Program Description : compares two numbers and if the expression results in numOne
being smaller than numTwo the printf() function executes that is the program executes the
code between the braces if not the program skips the code and just ends without any
further execution.

#include <stdio.h>

int main () {

int numOne=2;
int numTwo=5;

if (numOne<numTwo){

printf(“numTwo is greater than numOne”)

}
return 0;

};

output

numTwo is greater than numOne

3. If-Else Statements
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
}

Program Description : it is the same case as before but the program now executes the
code block in the else statement if the condition is not satisfied.
I changed the numOne value to 5 when the if expression gets evaluated it evaluates to false
but this isn't going to stop the program . in this case the else printf() executes before
reaching the return statement.

#include <stdio.h>

int main () {

int numOne=5;
int numTwo=5;

if (numOne<numTwo){

printf(“numTwo is greater than numOne”);

} else {

printf(“numOne is greater than numTwo”);


};

return 0;

};

4. If-else-if statements

Imagine that you want to evaluate several conditions. To accomplish this you’d need
to make use of several if /else-if statements . when one expression returns true the block
stops executing. if more than one condition is satisfied the one reached first executes and
the rest is not evaluated.

5. Structogram else-if chain syntax


if (condition 1) {
statement 1

} else if (condition 2) {

statement 2

}else{

statement 3

};

An alternative way is to sequence several if keywords, as follows:

if (condition 1) {
statement 1
}

if (condition 2) {
statement 2
}
if (condition 3) {
statement 3
}

Program Description : If- else-if statement used.

// C program to illustrate nested-if statement

#include <stdio.h>

int main()
{
int i = 20;

if (i == 10)
printf("i is 10");
else if (i == 15)
printf("i is 15");
else if (i == 20)
printf("i is 20");
else
printf("i is not present");

return 0;
}

6. Selection Statement – switch

The expression gets evaluated and executes the code that matches the case.
The break keyword prevents further execution.
if there’s no matching case the default case gets executed when included in the statement.
switch (expression)
{

case constant 1:
group-of-statements-1;
break;

case constant 2:
group-of-statements-2;
break;
...
default:
default-group-of-statements;
break;
}

Switch program example :

//C Program to illustrate the use of switch statement

#include <stdio.h>

int main()
{
// variable to be used in switch statement
int var = 2;
// declaring switch cases
switch (var) {
case 1:
printf("Case 1 is executed");
break;
case 2:
printf("Case 2 is executed");
break;
default:
printf("Default Case is executed");
break;
}

return 0;
}

7. Ternary Operator

It is kind of similar to the if-else statement as it follows the same algorithm as the if-else
statement but the conditional operator takes less space and helps to write the if-else
statements in the shortest way possible. It is also known as the ternary operator in C as it
operates on three operands

The conditional operator can be in the form:

variable = Expression1 ? Expression2 : Expression3;

Or the syntax can also be in this form:

variable = (condition) ? Expression2 : Expression3;

Or syntax can also be in this form:


(condition) ? (variable = Expression2) : (variable = Expression3);

The working of the conditional operator in C is as follows:

Step 1: Expression1 is the condition to be evaluated.


Step 2A: If the condition(Expression1) is True then Expression2 will
be executed.
Step 2B: If the condition(Expression1) is false then Expression3
will be executed.
Step 3: Results will be returned.

Example program

Description:

// C program to find largest among two


// numbers using ternary operator

#include <stdio.h>

int main()
{
int m = 5, n = 4;

(m > n) ? printf("m is greater than n that is %d > %d",


m, n)
: printf("n is greater than m that is %d > %d",
n, m);

return 0;
}

CHAPTER 26
1. LOOPS

Loops in programming are used to repeat a block of code until the specified condition is met.
A loop statement allows programmers to execute a statement or group of statements
multiple times without repetition of code.

2. For Loops

It is a repetition control structure that allows programmers to write a loop that will be
executed a specific number of times. for loop enables programmers to perform n number of
steps together in a single line.

for (initialization; condition; increase){

statement1;
statement2;
...
statementN;
}

#include <stdio.h>

int main () {

for (int i = 0; i < 5; i++) {


printf(“%d”,i); };

return 0;

};

The example above can be splitted in three :

a) int i = 0 // it’s a variable declaration.


b) i < 5 // it’s the condition during which the statements
in the loop will be executed.
c) i++ // as long as the condition is true the i variable will
be incremented by one

the output of the following code will be : 4

3. Iteration Statement – while loop

Another iteration statement is the while loop. It is simpler than the for loop. it will execute the
code as long as the condition evaluates to true (expression is true).
Program description : C program to demonstrate while loop

#include <stdio.h>

int main()
{
// Initialization of loop variable
int i = 0;

// setting test expression as (i < 5), means the loop


// will execute till i is less than 5
while (i < 5) {

// loop statements
printf("GeeksforGeeks\n");

// updating the loop variable


i++;
}

return 0;
}

4. Iteration Statement – do-while loop

A Similar loop is to the do-while loop, where the condition is checked after the execution
of the body statements, instead of before.This means that the code gets executed at least
once. that's the main difference between the other loops , which won't run the code if the
condition isn’t met (it’s evaluated first)
Program Description: C Program to demonstrate the use of do...while loop

#include <stdio.h>

int main()
{

// loop variable declaration and initialization


int i = 0;
// do while loop
do {
printf("Geeks\n");
i++;
} while (i < 3);

return 0;
}

5. THE BREAK ,GOTO And CONTINUE STATEMENTS

I. The continue statement in C

It is a jump statement that is used to bring the program control to the start of the loop. We
can use the continue statement in the while loop, for loop, or do..while loop to alter the
normal flow of the program execution. Unlike break, it cannot be used with a C switch case.

a) What is continue in C?

The C continue statement resets program control to the beginning of the loop when
encountered. As a result, the current iteration of the loop gets skipped and the control moves
on to the next iteration. Statements after the continue statement in the loop are not
executed.

b) Syntax of continue in C

The syntax of continue is just the continue keyword placed wherever we want in the loop
body.
continue;

c) Use of continue in C

The continue statement in C can be used in any kind of loop to skip the current iteration. In
C, we can use it in the following types of loops:

● Single Loops

● Nested Loops

Using continue in infinite loops is not useful as skipping the current iteration won’t make a
difference when the number of iterations is infinite.

The continue statement can be used in for loop, while loop, and do-while loop.

Program Example :

// C program to explain the use


// of continue statement with for loop

#include <stdio.h>

int main()
{
// for loop to print 1 to 8

for (int i = 1; i <= 8; i++) {

// when i = 4, the iteration will be skipped and for


// will not be printed

if (i == 4) {continue;}
printf("%d ", i); }

printf("\n");

int i = 0;

// while loop to print 1 to 8

while (i < 8) {

// when i = 4, the iteration will be skipped and for


// will not be printed

i++;
if (i == 4) {continue;}
printf("%d ", i);}

return 0;}

Output

1 2 3 5 6 7 8
1 2 3 5 6 7 8

d) How does it work ?

The working of the continue statement is as follows:


● STEP 1: The loop’s execution starts after the loop condition is evaluated to be
true.

● STEP 2: The condition of the continue statement will be evaluated.

● STEP 3A: If the condition is false, the normal execution will continue.

● STEP 3B: If the condition is true, the program control will jump to the start of the
loop and all the statements below the continue will be skipped.

● STEP 4: Steps 1 to 4 will be repeated till the end of the loop.

e) FLOWCHART OF CONTINUE IN C
II. The Break Statement

a) Use of break in C

The break statement in C is used for breaking out of the loop. We can use it with any type of
loop to bring the program control out of the loop. In C, we can use the break statement in the
following ways:

● Simple Loops

● Nested Loops

● Infinite Loops

● Switch case

Example 1:
//Break statements in C can be used with simple loops i.e, for loops, while loops, and do-
while loops.

// C Program to demonstrate break statement with for loop.

#include <stdio.h>

int main() {

// using break inside for loop to terminate after 2


// iteration

printf("break in for loop\n");


for (int i = 1; i < 5; i++) {
if (i == 3) {
break;
} else {
printf("%d ", i);}
};

// using break inside while loop to terminate after 2


// iteration

printf("\nbreak in while loop\n");

int i = 1;
while (i < 20) {
if (i == 3)
break;
else
printf("%d ", i);
i++;
}

return 0;
};

b) How does the break statement work ?


c) The working of the break statement in C is described below:

1. STEP 1: The loop execution starts after the test condition is evaluated.

2. STEP 2: If the break condition is present the condition will be evaluated.

3. STEP 3 A: If the condition is true, the program control reaches the break
statement and skips the further execution of the loop by jumping to the
statements directly below the loop.

4. STEP 3 B: If the condition is false, the normal flow of the program control
continues.
d) COMPARING BREAK AND CONTINUE STATEMENTS

III. The Goto Statement


The C goto statement is a jump statement which is sometimes also referred to as an
unconditional jump statement. The goto statement can be used to jump from anywhere to
anywhere within a function.

I. Type 1: In this case, we will see a situation similar to as shown in Syntax1


above. Suppose we need to write a program where we need to check if a
number is even or not and print accordingly using the goto statement. The
below program explains how to do this:

// C program to check if a number is


// even or not using goto statement

#include <stdio.h>

// function to check even or not


void checkEvenOrNot(int num)

{
if (num % 2 == 0)
// jump to even
goto even;
else
// jump to odd
goto odd;

even:
printf("%d is even", num);
// return if even
return;
odd:
printf("%d is odd", num);
}

int main(){

int num = 26;


checkEvenOrNot(num);
return 0;
}

Output:
26 is even

II. Type 2: In this case, we will see a situation similar to as shown in Syntax2
above. Suppose we need to write a program that prints numbers from 1 to 10
using the goto statement. The below program explains how to do this.

// C program to print numbers


// from 1 to 10 using goto statement
#include <stdio.h>

// function to print numbers from 1 to 10


void printNumbers()
{
int n = 1;
label:
printf("%d ", n);
n++;
if (n <= 10)
goto label;
}

// Driver program to test above function


int main()
{
printNumbers();
return 0;
}

Disadvantages of Using goto Statement :

● The use of the goto statement is highly discouraged as it makes the program
logic very complex.

● The use of goto makes tracing the flow of the program very difficult.

● The use of goto makes the task of analyzing and verifying the correctness of
programs (particularly those involving loops) very difficult.

● The use of goto can be simply avoided by using break and continue statements.

6. For ( declaration : range ) Statement;

A range is a series of values that go from one value to another value and include
the values in between. For example, the range a through d is a, b, c, and d. An integer
range of 1 through 5 is 1, 2, 3, 4, and 5.

#include <iostream>
using namespace std;

int main()
{
int range[] = {1, 2, 3, 4, 5};
for (int i : range)
{
cout << i << endl;
}
return 0;
}
CHAPTER… 28

PREPROCESSOR DIRECTIVES

1. Introduction

Think of the preprocessor as just a machine that transforms your code into a temporary,
fixed-up version that’s all ready to be compiled. For example, look at this
preprocessor directive:

#include <iostream>

If the preprocessor sees this line, it inserts the entire text from the file called
iostream (yes, that’s a filename; it has no extension) into the fixed-up version of
the source code. Suppose that the iostream file looks like this:

int hello = 10;


int goodbye = 20;

Just two lines are all that’s in it. (Of course, the real iostream file is much more
sophisticated.) And suppose that your own source file, MyProgram.cpp, has this in
it (as found in the Preprocessor example):

#include <iostream>
int main(){
std::cout << "Hello world!" << std::endl;
return 0;
}

Then, after the preprocessor finishes its preprocessing, it creates a temporary


fixed-up file (which has the lines from the iostream file inserted into the MyProgram.
cpp file where the #include line had been) to look like this:

int hello = 10;


int goodbye = 20;
int main()
{
std::cout << "Hello world!" << std::endl;
return 0;
}

In other words, the preprocessor replaced the #include line with the contents of
that file. Now, the iostream file could have #include lines, and those lines would
be replaced by the contents of the files they refer to. As you may imagine, what
started out as a simple application with just a few lines could actually have hundreds
of lines after the preprocessor gets through with it.

2. Creating constants and macros


with #define

The preprocessor also provides you with a lot of other directives besides #include.
One of the more useful ones is the #define directive. Here’s a sample #define
line:

#define MYSPECIALNUMBER 42

After the preprocessor sees this line, every time it encounters the word MYSPECIALNUMBER,
It replaces it with the word 42 (that is, whatever sequence of letters,
numbers, and other characters follow the definition). In this case, #define creates
a kind of constant where the word is easier to understand than the value associated
with it. But #define also lets you create what are called macros, which are a
sort of script. This line defines the oldmax() macro:

#define oldmax(x, y) ((x)>(y)?(x):(y))

After the preprocessor sees this line, it replaces every occurrence of oldmax()
followed by two arguments with ((x)>(y)?(x):(y)), using the appropriate substitutes
for x and y. For example, if you then have this line

q = oldmax(abc, 123);

the preprocessor replaces the line with:

q = ((abc)>(123)?(abc):(123));

The variable q is set to the value in abc if the abc value


is greater than 123; otherwise, the q gets set to 123.

Performing conditional compilation

#ifdef DEBUG
cout << "The value of j is " << j << endl;
#else
cout << j << endl;
#endif

The lines that begin with # are preprocessor directives. The preprocessor has its
own version of if statements. In your code, you can have a line like the following,
with nothing after it:

#define DEBUG

This line defines a symbol (rather than a constant with a value). It works just like
the symbols described earlier, except that it’s not set to be replaced by anything.
You can also define such symbols in the command-line options to GCC or whichever
compiler you use.

Now, when the preprocessor starts going through your application and gets to the
#ifdef DEBUG line, it checks to see whether the DEBUG symbol is defined. If
the symbol is defined, it spits out to its fixed-up file the lines that follow, up
until the #else line. Then it skips any lines that follow that, up until the #endif
line. For the earlier example in this section, if DEBUG is defined, the block of code
starting with #ifdef DEBUG through the line #endif is replaced by the code in the
first half of the block:

cout << "The value of j is " << j << endl;

But if the DEBUG symbol is not defined, the preprocessor skips over the lines up
until the #else, and spits out the lines that follow, up until the #endif. In this
case, it’s replaced by the code following the #else line:

cout << j << endl;

#include <iostream>
using namespace std;
#ifdef UNIVAC
const int total = 200;
const string compname = "UNIVAC";
#elif defined(HAL2000)
const int total = 300;
const string compname = "HAL2000";
#else
const int total = 400;
const string compname = "My Computer";
#endif
// This is outdated, but you might see it on
// occasion. Don't write code that does this!
#define oldmax(x, y) ((x)>(y)?(x):(y))
#define MYSPECIALNUMBER 42
int main() {
cout << "Welcome to " << compname << endl;
cout << "Total is:" << endl;
cout << total << endl << endl;
// Try out the outdated things.
cout << "*** max ***" << endl;
cout << oldmax(5,10) << endl;
cout << oldmax(20,15) << endl;
cout << MYSPECIALNUMBER << endl << endl;

// Here are some standard redefined macros.


cout << "*** Predefined Macros ***" << endl;
cout << "This is file " << __FILE__ << endl;
cout << "This is line " << __LINE__ << endl;
cout << "Compiled on " << __DATE__ << endl;
cout << "Compiled at " << __TIME__ << endl << endl;
// Here's how some people use #define, to
// specify a "debug" version or "release" version.
cout << "*** total ***" << endl;
int i;
int j = 0;
for (i = 0; i<total; i++)
{
j = j + i;
}
#ifdef DEBUG
cout << "The value of j is " << j << endl;
#else
cout << j << endl;
#endif
return 0;
}

you see this output:

Welcome to My Computer
Total is:
400
*** max ***
10
20
42
*** Predefined Macros ***
This is file C:\CPP_AIO\BookI\Chapter09
\Preprocessor2\main.cpp
This is line 35
Compiled on Apr 23 2020
Compiled at 15:19:38
*** total ***
79800

/// test code


#include <iostream>
#include <string>
using namespace std;

class plants {

public:

string name;
string family;
string season;

plants(string Name ,string Family, string Season);

};

plants::plants(string Name ,string Family, string Season){


name = Name;
family=Family;
season=Season;
};

plants display (plants obj){


std::cout<<obj.name<<endl;
std::cout<<obj.family<<endl;
std::cout<<obj.season<<endl;
return obj ;
};

int main (){


plants Pine ("pine", "tree", "all ");
display (Pine);
};

chapter Templates 29
1. Introduction To Templates

Templates allow you to construct both functions and classes based on


types that have not yet been stated.

● A function template defines a group of statements for a function using a parameter


instead of a concrete type.

● A class template specifies a class definition using a parameter instead of a concrete


type.It can provide a generic definition that can be used to represent various types of
arrays, for example. During instantiation, that is, when a concrete type is defined, an
individual class is created based on the template definition.
Suppose that you have a class called MyHolder. This class will hold some integers.
Nothing special, but it looks like this:

class MyHolder {

public:

int first;
int second;
int third;

int sum() {
return first + second + third;
}
};

This class is easy to use; you just create an instance of it and set the values of its
members.

The following code creates ten instances of the class ( the instances are heap- stored ), calls
sum(), and prints the return value of sum():

// class myHolder

myHolder { … }
//pointer of type myHolder

MyHolder *hold;

// variable declaration to be used in a loop

int loop;

// loop that creates 10 pointers of type Myholder


//each time it finishes creating the pointer it deletes it
// and starts the loop again

for (loop = 0; loop < 10; loop++) {

hold = new MyHolder;


hold->first = loop * 100;
hold->second = loop * 110;
hold->third = loop * 120;
cout << hold->sum() << endl;

delete hold;

Suppose you discover that this class MyHolder is handy, for having a version of it that
holds floats instead of ints.

You could create a second class just like the first that uses the word float instead
of int, like this:

class AnotherHolder {

public:

float first;
float second;
float third;
float sum() {
return first + second + third;
}
};

This class works the same way as the previous class, but it stores three float
types instead of int types. Instead of typing two different versions of the class, type one
version of the class that you can, effectively, modify when you need a different version.

Look at this code:

template <typename T> class CoolHolder {


public:
T first;
T second;
T third;
T sum() {
return first + second + third;
}
};

That’s what a template does: It specifies a placeholder for a class.


But it doesn’t actually create a class . . . yet. You accomplish this task by
writing code to create a variable or by using the class somehow.

Look at this code:

CoolHolder<int> IntHolder;
IntHolder.first = 10;
IntHolder.second = 20;
IntHolder.third = 30;

This code tells the compiler to create a class by replacing every instance of T
with int in the CoolHolder template. In other words, the compiler creates a
class named CoolHolder<int>. These four lines of code create an instance of
CoolHolder<int> called IntHolder and set its properties. The computer creates
this class at compile time. Remember, types are created at compile time, and this
example code is no exception to this rule.

It's time to put that template into action. The CoolHolder example,
contains a complete application that uses the CoolHolder
template.

2. Methods in a class using the template type as a parameter:


template<typename T> class MyArray {

void printName (const T& element);

};

Program Example :

#include <iostream>
using namespace std;

template <typename T> class CoolHolder {


public:
T first;
T second;
T third;
T sum() {
return first + second + third;
}
};

int main()

// this instantiates an intHolder class of type CoolHolder<int>


it will replace the T type occurrences in the CoolHolder class with
an integer type

//This line declares a variable called IntHolder.


For this variable, the compiler creates a type called
CoolHolder<int>, which is a type based on the CoolHolder template,
where T is replaced by int. Here’s another line where the code
declares a variable:

CoolHolder<int> IntHolder;
IntHolder.first = 10;
IntHolder.second = 20;
IntHolder.third = 30;

// Another instance of CoolHolder<int>. Same as the previous point.

//This time, the compiler doesn’t have to create another type


because it just created the CoolHolder<int> type earlier. But again,
this line uses the same type based on the template, where T is
replaced by int.
CoolHolder<int> AnotherIntHolder;
AnotherIntHolder.first = 100;
AnotherIntHolder.second = 200;
AnotherIntHolder.third = 300;

//this instantiates an intHolder class of type CoolHolder<float>


it will replace the T type occurrences in the CoolHolder class with
an float type
//It’s instantiated at FloatHolder:

CoolHolder<float> FloatHolder;

// When the compiler sees this line, it creates another type by


using the template, and it replaces T with the word float. So in
this case, the first, second, and third properties of FloatHolder
each hold a floating-point number. Also, the sum() method returns a
floating-point number.

CoolHolder<float> FloatHolder;
FloatHolder.first = 3.1415;
FloatHolder.second = 4.1415;
FloatHolder.third = 5.1415;

cout << IntHolder.first << endl;


cout << AnotherIntHolder.first << endl;
cout << FloatHolder.first << endl;

// dynamic instantiation of a pointer to a class of type


CoolHolder<int>

CoolHolder<int> *hold;

for (int loop = 0; loop < 10; loop++) {


hold = new CoolHolder<int>;
hold->first = loop * 100;
hold->second = loop * 110;
hold->third = loop * 120;
cout << hold->sum() << endl;
delete hold;
}
return 0;
}
When you run this application, you see a bunch of results from calls to sum():

Output :

10
100
3.1415
0
330
660
990
1320
1650
1980
2310
2640
2970

3. Template Function Definitions

Templates are a way to define functions that can work for many different
types, while still writing them only once.

template<typename T>

T square(T value) {
return value * value;
}

// the syntax for instantiating a template function:

// square function of type int calling and instantiation.

square<int>(10);

program Example:

// instantiation of a template function that squares a value:


int main() {

//This calls the templated function max, specifying int as the type
parameter. We say that we instantiate the templated function max
with type int, and then called that instance.

square<int>(10); // you define the concrete type as int between the


angle brackets

square<float>(10.5)// you define the concrete type as float between


the angle brackets

square<double>(10.20)// you define the concrete type as double


between the angle brackets

Note

Since the compiler generates the code from the template definition, it means that
the full definitions need to be visible to the calling code, not only the declaration,
as was the case for functions and classes.
when writing templates that should be accessed by several
files, both the definition and the declaration of the templates must be in the header file.

This restriction does not apply if the template is used only in one file.

I. Classes can also have templated methods. Templated methods are similar to
template functions, but they can access the class instanced data.

template<typename T> class MyArray {

template<typename Comparator>

void sort (const Comparator & element);

};
The sort method will accept any type and will compile if the type satisfies all the
requirements that the method imposes on the type.

To call the method, the syntax follows the one for calling functions:

MyArray<int> array;

MyComparator comparator;

array.sort<MyComparator>(comparator);

II. Defining template methods outside the class

The following program shows how to define template functions prototypes. the syntax
is alike to the one used for declaring and defining non-templated functions

Program Example :

#include <iostream>
using namespace std;

template <typename T>

class ImFree {

protected:

T x;

public:

T& getx();
void setx(T);

};

//template declaration
template <typename T>

// the get function prototype

T &ImFree<T>::getx() {
return x;

// template declaration

template <typename T>

// set function prototype

void ImFree<T>::setx(T newx) {


x = newx;
}

int main() {

// template class instantiation


ImFree<int> separate;

// setting x to 10
separate.setx(10);

//gettingthe value of x

cout << separate.getx() << endl;


return 0;

Look closely at one of the methods:

template <typename T>


T &ImFree<T>::getx() {
return x;
}

Note that the getx() method returns a reference instead of a variable of type T.
There’s a good reason for doing it. In the main() function you create
the class based on the template with an integer parameter:
ImFree<int> separate;

However, you can create the class with some other class:

ImFree<SomeOtherClass> separate;

a) Including static members in a template

You can think of the static member as being a member of the class itself.
When you include a static member in a template, each class that you create based
on the template gets its own static member. Further, you need to tell the compiler
how to store the static member just as you do with static members of classes that
aren’t created from templates.

1) Using Static Members in a Template

#include <iostream>

using namespace std;

//You supply the same template header you would use for the class
and then specify the static member type (in this case, T, which is
the template parameter).

template <typename T> class Electricity {

public:

static T charge;

};

//Next, you refer to the static member by using the usual


classname::member name syntax. But remember that the class name gets
the template parameter in angle brackets after it.

template <typename T>

T Electricity<T>::charge;
int main() {

// This code creates two classes based on the templates Electricity


<int> and Electricity <float>.Each of these classes has its own
instance of the static member;

// the <int> version contains 10

Electricity<int>::charge = 10;

// the <float> version contains 98.6.

Electricity<float>::charge = 98.6;

// to show that there’s only a single static member per class, the
code creates an instance of Electricity<int> and sets its static
member to 22

Electricity<int> inst;
inst.charge = 22;

// you can see that the output for the two Electricity<int> lines
are the same and the Electricity<float> output is different.

cout << Electricity<int>::charge << endl;


cout << Electricity<float>::charge << endl;
cout << inst.charge << endl;
return 0;
}

4. Deriving Templates

If you think about it, you can involve a class template in a derivation in at least
three ways.

You can:

»»Derive a class from a class template


»»Derive a class template from a class
»»Derive a class template from a class template
a) Deriving classes from a class template

Think of the process like this:

1. From a template, you create a class.

2. From that created class, you derive your final class.

Suppose you have a template called MediaHolder, and the first two lines of its
declaration look like this:

template <typename T> class MediaHolder

Then you could derive a class from a particular case of this template as in this
header for a class:

class BookHolder : public MediaHolder<Book>

Here you create a new class (based on MediaHolder) called MediaHolder<Book>.


From that class, you derive a final class, BookHolder.

Program Example :

#include <iostream>
using namespace std;

// book class definition

class Book {

public:

string Name;
string Author;
string Publisher;

//constructor

Book(string aname, string anauthor, string apublisher) :


Name(aname), Author(anauthor), Publisher(apublisher){}

};
// magazine class definition

class Magazine {

public:

string Name;
string Issue;
string Publisher;

// constructor

Magazine(string aname, string anissue,


string apublisher) : Name(aname), Issue(anissue),
Publisher(apublisher){}

};

// mediaholder template class definition

template <typename T> class MediaHolder {

public:

T *array[100];

int Count;

void Add(T *item) {

array[Count] = item;

Count++;

MediaHolder() : Count(0) {}

};
// derived class bookholder

class BookHolder : public MediaHolder<Book> {

public:

enum GenreEnum {childrens, scifi, romance,horror, mainstream,


hownotto};

GenreEnum GenreOfAllBooks;

};

//derived class magazineholder

class MagazineHolder : public MediaHolder<Magazine> {

public:

bool CompleteSet;

};

int main() {

// magazine holder class instantiation (creating object of type


MagazineHolder)

MagazineHolder dl;

dl.Add(new Magazine( "Dummies Life", "Vol 1 No 1", "Wile E."));

dl.Add(new Magazine("Dummies Life", "Vol 1 No 2", "Wile E."));

dl.Add(new Magazine("Dummies Life", "Vol 1 No 3", "Wile E."));

dl.CompleteSet = false;
cout << dl.Count << endl;

// book holder class instantiation

BookHolder bh;

bh.Add(new Book( "Yellow Rose", "Sandy Shore", "Wile E."));


bh.Add(new Book( "Bluebells", "Sandy Shore", "Wile E."));
bh.Add(new Book( "Red Tulip", "Sandy Shore", "Wile E."));
bh.GenreOfAllBooks = BookHolder::childrens;
cout << bh.Count << endl;
return 0;
}

Output :

When you run this example, you see the magazine count of 3 first,
and the book count of 3 second.

Let’s go for a different example :

#include <iostream>
using namespace std;

// template class declaration

template <typename T> class Base {

public:

T a;

};

// derived template class based on the Base class template


template <typename T> class Derived : public Base<T> {

public:

T b;

};

// two functions each taking a different class. The first one takes
Base<int> as a parameter. The Second one takes Base<double >. Both
take a pointer to a class.

void TestInt(Base<int> *inst) {


cout << inst->a << endl;
}

void TestDouble(Base<double> *inst) {


cout << inst->a << endl;

int main() {

// When a function, such as TestInt() or TestDouble(), takes a


pointer to a class, it can legally pass a pointer to an instance of
a derived class, which means that you can create thisvariable:
You pass this variable to the function that takes a Base<int> and it
compiles.

Base<int> base_int;

// same as above

Base<double> base_double;

// Derived<int> is derived from Base<int>

Derived<int> derived_int;

// Derived<double> is derived from Base<double>

Derived<double> derived_double;

//it outputs four numbers: two int values and two double values.
TestInt(&base_int);
TestInt(&derived_int);
TestDouble(&base_double);
TestDouble(&derived_double);

return 0;
}

Output :

two int and two double values

5. Parameterizing a Template

I. Putting different types in the parameter

1) You can put many more keywords inside the parameter beyond just the
boring word typename.

2) For example, suppose you have a class that does some comparisons to
make sure that a product isn’t too expensive for a person’s budget.

3) Each person would have several instances of this class, one for each
product. This class would have a constant in it that represents the maximum
price the person is willing to spend.

4) But there’s a twist: Although you would have multiple instances of this class,
one for each product the person wants to buy, the maximum price would be
different for each person.

II. Making Templates Easier to Use

We always said that we need to provide the template arguments to the parameters of a
template function or class. Now, in this section, we are going to see two features that
C++ offers to make it easier to use templates.
These features are default template arguments and template argument deduction.

III. Parameter and Argument Types


We are going to see how, based on different pairs of parameters and arguments, the
type is deduced:

The error happens because we cannot bind a temporary value, like 1, to a non-const
reference.

As we can see, the compiler tries to deduce a type so that when it is substituted in the
parameter, it matches the argument as best as possible.

The compiler cannot always find such a type; in those situations, it gives an error and
it's up to the user to provide the type.

The compiler cannot deduce a type for any of the following reasons:

The type is not used in the parameters.

For example: The compiler cannot deduce a type if it is only used in the return type, or only
used inside the body of the function.

The type in the parameter is a derived type.

For example:

template<typename T> void


foo(T::value_type a).

The compiler cannot find the type T given the parameter that's used to call the function.

Knowing these rules, we can derive a best practice for the order of the template
parameters when writing templates: the types that we expect the user to provide need
to come before the types that are deduced.

The reason for this is that a user can only provide the template arguments in the same
order they have been declared.
Let's consider the following template:

template<typename A, typename B, typename C>


C foo(A, B);

When calling foo (1, 2.23), the compiler can deduce A and B, but cannot deduce C.
Since we need all the types, and the user has to provide them in order, the user has to
provide all of the types:

foo<int, double, and float>(1, 2.23);.

Let's say we put the types that cannot be deduced before the types that can be
deduced, as in the following example:

template< typename C, typename A, typename B>


C foo(A, B);

We could call the function with foo<float>(1, 2.23). We would then provide the type to
use for C and the compiler would automatically deduce A and B.

In a similar way, we need to reason about default template arguments.

Since they need to come last, we need to make sure to put the types that the user is
more likely to want to modify first, since that will force them to provide all the template
arguments up to that parameter.

Let's see how the deduction works for forwarding references:

Note

Let's say the type is not deduced, but, it is provided explicitly, for example:

int x = 0;
do_action<int>(x);

Here, T will be int, since it was explicitly stated.


The advantage, as we saw before, is that we work with any kind of reference, and when
the calling code knows it can move the object, then we can make use of the additional
performance provided by the move constructor, but when a reference is preferred, then
the code can use it as well.

Additionally, some types do not support copying, and we can make our template work
with those types as well.

When we write the body of the template function, the parameter is used as an l-value
reference, and we can write code ignoring whether T is an l-value reference or an
r-value one:

template<typename T>

void do_action(T&& obj) { /* forwarding reference, but we can access


obj as if it was a normal l-value reference */

obj.some_method();

some_function(obj);

You can create such a situation with or without templates. Here’s


a way you can do it with a template:

//In this case, the template parameter isn’t a type at all — it’s an
integer value, an actual number. Then, inside the class, you use
that number as a constant.

//As you can see in the TestPrice function, the code compares Price
to the MaxPrice constant. So this time, instead of using T for the
name of the template parameter, the code views it as a value, not a
type.

template <int MaxPrice> class PriceController {

public:
int Price;

void TestPrice(){

if (Price > MaxPrice){

cout << "Too expensive" << endl;}

};

Complete Program :

#include <iostream>
using namespace std;

// Person Instantiation Class

template <typename T> class SomethingForEveryone {

public: T member; };

// Template Variable Declaration

template <int MaxPrice> class PriceController {

public:

int Price;

void TestPrice(string Name){

if (Price > MaxPrice){

cout << Name << " too expensive!" << endl; }

}
};
int main() {

SomethingForEveryone<int> JustForMe;

JustForMe.member = 2;

cout << JustForMe.member << endl;

// THIS FREDMAXPRICE CONST VARIABLE REPLACES THE TEMPLATE VARIABLE


DECLARATION IN THE PRICECONTROLLER CLASS AND INSTANTIATES A
PRICECONTROLLER CLASS WITH AN ITEM FOR THAT PERSON.

1)
const int FredMaxPrice = 30;
PriceController<FredMaxPrice> FredsToaster;
FredsToaster.Price = 15;
FredsToaster.TestPrice("Toaster");

2)
PriceController<FredMaxPrice> FredsDrawingSet;
FredsDrawingSet.Price = 45;
FredsDrawingSet.TestPrice("Drawing set");

// CREATES A JULIEMAXPRICE CONSTANT VARIABLE


//DEFINES AND INSTANTIATES THE PRICECONTROLLER CLASS USING
JULIEMAXPRICE
//

1)
const int JulieMaxPrice = 60;
PriceController<JulieMaxPrice> JuliesCar;
JuliesCar.Price = 80;
JuliesCar.TestPrice("Car");
return 0;
}

Each person gets a different class that reflects the maximum price they’re
willing to pay.

You can see that Fred gets a class called PriceController <FredMaxPrice>.
Julie gets a class called PriceController <JulieMaxPrice>.

And remember, these really are different classes. The compiler created
two different classes, one for each item passed in as a template parameter. Also
notice that the parameters are constant integer values. FredMaxPrice is a constant
integer holding 30. JulieMaxPrice is a constant integer holding 60.
For the first one, PriceController <FredMaxPrice>, the code creates two
instances. For the second onJe, PriceController <JulieMaxPrice>, the code
creates one instance.

In all instances, the code sets the price of the item and
then calls TestPrice() with the item name. If the item is too expensive, the
PriceController outputs a special message.

Here’s the output from this example:

Drawing set too expensive!


Car too expensive!

When working with some older versions of C++, you can’t use certain types, such
as float, for your template. Doing so can cause the build process to fail with all
sorts of odd messages. However, you also see the following message, which tells
you precisely where the problem lies:

error: 'float' is not a valid type for a template non-type


parameter

Starting with C++ 11, you can use std::nullptr_t; as a parameter type, as in
template <std::nullptr_t N>.
When working with C++ 20, you gain access to
these types as well:

»»Floating-point type
»»Literal class type with the following properties:

• All base classes and nonstatic data members are public and non-mutable.
• The types of all base classes and non-static data members are structural
types. Depending on the compiler, you might also be able to use a
multidimensional array of the structural type.
CHAPTERS 30
CONTAINERS C++ FUNDAMENTALS BOOK

1. Introduction

Containers are used to store collections of elements of a certain type. Usually, the
type of the container is a template parameter There are several container classes, each of
them with different characteristics and features.

2. Sequence Containers

IN Sequence containers the order in which their elements are stored is decided by the
programmer rather than by the values of the elements. Every element has a certain position
that is independent of its value.
List of Sequence containers with included description
Time for insertion and removal of Objects

I. Array

The array container is a fixed-size data structure of contiguous elements.


The array class is defined in the <array> header file, which has to be included before
usage.
a) Description:

An array's size needs to be specified at compile time.


Once defined, the size of the array cannot be changed.
When an array is created, the size elements it contains are initialized next to each other in
memory. While elements cannot be added or removed, their values can be
modified.

b) declaration : std::array<int,3> myArray;

c) definition: std::array<int,3> myArray{0,1,2};

d) Arrays access:

● By index using the square brackets operator []


● By the at() member function
● The first element by the front() function
● The last element by the back() member function
● Fill() , which fills the array with a certain value. For example:
myArray.fill(5) outputs : 5,5,5

e) Complexity :

These operations are fast: since the elements are contiguous, we can compute the
position in memory of an element given its position in the array, and access that
directly.

• Insertion : Not possible.


• Deletion : Not possible .
• Access : O(1).

f) Determine an array’s size:

● using the size() member function.

g) Check for an empty array :

● The empty() function, which returns true if size() is zero.


Array supported operations
II. Working with std::array to Overcome array Limitations

#include <iostream>
#include <array>
#include <algorithm>
#include <iterator>
using namespace std;

int main() {

// the declaration begins by providing the array type, char, and


the number of array elements, 5, as template parameters. Letters
contains five char values from 'a' through 'e'.
As shown in the example, you can use a standard iterated for loop to
display the individual entries.

array<char, 5> Letters = {'a', 'b', 'c', 'd', 'e'};

for (entry: Letters)


cout << entry << endl;

return 0;
}

Output:

a b c d e

III. Vector

The vector container is a data structure of contiguous elements whose size can be
dynamically modified: it does not require to specify its size at creation time.
The vector class is defined in the <vector> header file.

a) Description

A vector stores the elements it contains in a single section of memory. When a new element
is added to the vector, if there is enough space in the section of memory, the element is
added after the last element in the vector. If there isn't enough space, the vector gets a new,
bigger section of memory and copies all the existing elements into this new section, then it
deletes the old section of memory.

b) Elements access:

● By the at () function
● By square brackets []
● Front() can be used to get a reference to the first element,
● Back() returns a reference to the last element.
c) Elements can be appended using:

● The push_back() function

● The insert() method, which requires an iterator to the


position before which the new element should be inserted.

● Emplace () Suppose you have a vector of Person objects. You can add a new
person at the back in these two similar ways:

persons.push_back(Person("Sheldon", "Cooper"));

persons.emplace_back("Leonard", "Hofstadter");

The arguments to emplacement functions are perfectly forwarded to the element’s


constructors.

On a related note, addition and insertion members of containers generally have full support
for moving elements into containers, again to avoid the creation of unnecessary copies .

For example:

Person person("Howard", "Wolowitz");


persons.push_back(std::move(person));
d) Deletion:

● The last element can be removed using pop_back()

● At a generic position using the erase() function.There are two overloads of


erase():

1. erase(iter) : Removes the element to which the given iterator points.

2. erase(first, last) : Removes the range of elements given by the two


iterators, so (first , last ).

● To remove all elements, use clear() . This again does not affect capacity.

Appending or deleting the last element is fast, while inserting or removing other
elements of the vector is considered slow, as it requires moving all the elements to
make space for the new element or to keep all the elements contiguous:

Time Complexity:
Vectors, just like arrays, allow efficient access of elements at random positions.
A vector's size is also retrieved with the size() member function which returns the actual
number of elements in the vector, capacity() instead returns the maximum number of
elements that can be inserted in the current section of memory.

When you remove elements, the size of the vector changes, but its capacity does not. If you
want to reclaim unused memory, you can use shrink_to_fit() .

The operation of getting a new section of memory is called reallocation. Since


reallocation is considered an expensive operation, it is possible to reserve enough
memory for a given number of elements by enlarging a vector's capacity using the
reserve() member function. The vector's capacity can also be reduced to fit the number
of elements using the shrink_to_fit() function in order to release memory that is not
needed anymore.
Vector supported operations :
Example 1:

Let's look at the following example to understand how vector::front() and


vector::back() work in C++:

#include <iostream>

// Import the vector library

#include <vector>

int main() {

std::vector<int> myvector;

// Both front and back of vector contains a value 100

myvector.push_back(100);

// Now, the back of the vector holds 10 as a value, the front holds
100

myvector.push_back(10);

// We subtracted front value with back

myvector.front() -= myvector.back();

std::cout << "Front of the vector: " << myvector.front() <<


std::endl;
std::cout << "Back of the vector: " << myvector.back() << std::endl;

};

Output:

Front of the vector: 90


Back of the vector: 10

Example 2 :

#include <iostream>
#include <vector>

using namespace std;

int main() {

vector<string> names;
names.push_back("Tom");
names.push_back("Dick");
names.push_back("Harry");
names.push_back("April");
names.push_back("May");
names.push_back("June");
cout << names[0] << endl;
cout << names[5] << endl;
return 0;
};

Output:

1. Tom
2. June

Example 3:

Creating More Advanced Vectors

#include <iostream>

#include <vector>

using namespace std;

class Employee {
public:
string Name;
string HireDate;
int holidays;

// constructor initialization list

Employee(string aname, string ahiredate, int aholidays) :


Name(aname), HireDate(ahiredate),holydays(aholidays) {}
};
int main() {

// A vector that holds strings

vector<string> MyAliases;
MyAliases.push_back(string("Bud The Sailor"));
MyAliases.push_back(string("Rick Fixit"));
MyAliases.push_back(string("Bobalou Billow"));

for (auto entry : MyAliases) {

cout << entry << endl;

} ;

// A vector that holds integers

vector<int> LuckyNumbers;
LuckyNumbers.push_back(13);
LuckyNumbers.push_back(26);
LuckyNumbers.push_back(52);

for (auto entry : LuckyNumbers){

cout << entry << endl;

// A vector of default constructed ints.

vector<int> Default(5);

int i = 0;

vector<int>::reverse_iterator rentry = Default.rbegin();

for (; rentry != Default.rend(); rentry++) {

*rentry = ++i;

} ;

for (auto entry : Default) {


cout << entry << endl;

};

// A vector that holds Employee instances

vector<Employee> GreatWorkers;
GreatWorkers.push_back(Employee("George","123100", 50));
GreatWorkers.push_back(Employee("Tom","052002", 40));

for (auto entry : GreatWorkers) {


cout << entry.Name << endl;
return 0; };

this is the Output from the cout statements:

1. Bud The Sailor


2. Rick Fixit
3. Bobalou Billow
4. 13
5. 26

Notice that this example relies on an iterated for loop for each of the vectors.
Using an iterated for loop greatly reduces the amount of code you write. Plus, you
don’t have to worry about the size of the vector you’re processing. All you concern
yourself with are the individual entries.

The Default portion of the example is also interesting in that it declares a vector
of a specific size and reverse-fills the vector from the end to the beginning. Consequently,
Default[0] contains the value 5, rather than 1, as you might expect.
To work backward, you must use a standard for loop.

IV. Deque

a) Description

The deque class is defined in the <deque> header file.


The deque container is like a vector, but it allows direct access , insertion and deletion of
elements at the back and the front.
A Deque generally requires more memory than a vector, and a vector is more performant for
elements access and for push_back operations , so unless it is required to insert at the
front, a vector is usually preferred.

A deque does not have the concept of capacity because it does not have to store its
elements contiguously, so none of the methods related to capacity are available.

b) Elements Access:

● Bracket notation []

c) Elements Insertion:

● Push () inserts items at the beginning or end


● Pop () removes items off the beginning or end.
● Push_front() removes the first item
● Push_back() add items at the end

d) Deletion:

● pop_back() removes last item


● pop_front() removes first item

Example:

e) Operations Complexity :
• Insertion : Amortized constant O(1) at the beginning and end; otherwise linear in the
distance from the insertion point to the beginning or end O(N)

• Deletion : O(1) at the beginning or end; otherwise linear in the distance to the beginning
or end O(N)

• Access : O(1)

Integer Deck Example I :

deque<int> mydek;
mydek.push_front(10);
mydek.push_front(20);
mydek.push_back(30);
mydek.push_back(40);

Then you can loop through the deque, accessing its members with a
bracket, as if it’s an array:

int loop;

for (loop = 0; loop < mydek.size(); loop++) {


cout << mydek[loop] << endl;
};

You can also grab items off the front or back of the deque. Here’s
an example from the front:

while (mydek.size() > 0) {

cout << mydek.front() << endl;


mydek.pop_front();

};

Note
DEQUES VERSUS VECTORS

A deque doesn’t use the contiguous approach that vector does. Inserting is easier for deque
because it doesn’t need to shuffle things around. Also, a deque doesn’t have to add new
elements to perform a resize, whereas vector does when it runs out of space. And
finally, deque includes a push_front() method that allows adding an item at the
beginning.

Two functions show up here, front() and pop_front(). The front() function
returns a reference to the item at the front of the deque. The pop_front() function
removes the item that’s at the front of the deque.

V. LISTS

a) Description

The list class is defined in the <list> header file.


The list container is a data structure of nonadjacent elements that can be dynamically
grown.
A list is a simple container similar to an array. You make use of a list when you need to
access only one item in the list; you use it when you plan to traverse through all the list, item
by item.
Each element in the list has its memory segment and a link to its predecessor and its
successor (this applies only to Lists , Forward lists have only a link to its successor) .
The structure containing the element, which is the link to its predecessor
and to its successor, it is called a node.

b) List supported operations:


c) Data Structure Explanation

When an element is inserted in a list, the predecessor node needs to be updated so that
its successor link points to the new element. Similarly, the successor node needs to be
updated so that its predecessor link points to the new element:
When an element is removed from the list, we need to update the successor link of
the predecessor node to point to the successor of the removed node. Similarly, the
predecessor link of the successor node needs to be updated to point to the predecessor
of the removed node.

The advantage of list is that insertion and removal are fast at any position, if we
already know the node at which we want to insert or remove. The disadvantage of this
is that getting to a specific node is slow.

d) Types of Lists

I. Forward-List

As usual, the forward_list class is defined in the <forward_list> header file.


The forward_list container is similar to the list container, with the difference that its
nodes only have the link to the successor. For this reason, it is not possible to iterate
over a forward_list in backward order:
forward_list only supports forward iterators, so you always need to start at the
beginning.

a) Inserting elements :

● Using insert_after()which is a variation of the insert() function,


where the new element is inserted after the provided position.
● The forward_list class does not even provide push_back() or size()
● The push_front() function inserts the item in the beginning of the list,
in front of all the others that are presently in the list.
● The push_back() function adds the item to the end of the list.
● Using insert() and splice() enables you to place
items in other locations in the list

b) Deletion:

● It is done through erase_after(), which removes the element


after the provided position.

For operations in which you need a pointer to an item in the list, you need to use
an iterator. An iterator is simply a typedef for a pointer to an item in the list;
however, it points to the item in the list, not the original item you added to the
list. Remember, the containers hold copies. Thus, if you do an insert() into a list
and point to an original item, that item won’t be a member of the list, and the
insert() won’t work.

if you do need to do an insert and you’re willing to use iterators to move through the list to
find the location where you want to put the new item, insert() will do the job.

The following table lists the provided algorithms for list (L) and forward_list
(F):
Note

LISTS VERSUS VECTORS

Lists provide sequential access, which means that you can’t drop into the middle of the
list and look at whatever item is stored there (as you can with a vector). If you want to
look at the items in the list, you must start at the beginning or the end and work your
way through it one item at a time. A vector allows random access using brackets, as
in MyVector[3]. This requirement may seem like a disadvantage for the list, but the
ANSI document says that “many algorithms only need sequential access anyway.” Lists
have definite advantages. The list template allows you to splice together multiple lists,
and it has good support for sorting the list, for splicing members out of one list and into
another, and for merging multiple lists.
VI. Sequence Containers in Practice

All the sequence containers we have looked at are empty when they are first created.
When we want to create a container containing some elements, it can be repetitive to
call the push_back() or insert() functions repeatedly for each element.
Fortunately, all the containers can be initialized with a sequence of elements when they
are created.

The sequence must be provided in curly brackets, and the elements need to be comma
separated.

This is called an initializer list:

#include <vector>

int main(){

// initialize the vector with 3 numbers

std::vector<int> numbers = {1, 2, 3};

VII. Sequence Containers Operations

The following subsections give an overview of all the operations supported by vector (V),
deque (D), array (A), list (L), and forward_list (F), divided into categories.
2. Special Container

I. std::bitset

a) Description

A bitset is a container storing a fixed number of bits. The number of bits is specified as a
template parameter. For example, the following creates a bitset with 10 bits, all initialized to
0:

std::bitset<10> myBitset;
The values for the individual bits can be initialized by passing an integer to the constructor or
by passing in a string representation of the bits.

For example: std::bitset<4> myBitset("1001");

A bitset can be converted to an integer or a string with to_ulong() , to_ullong() , and


to_string() .

b) Complexity :

• Insertion : Not possible


• Deletion : Not possible
• Access : O(1)
CHAPTERS ASSOCIATIVE CONTAINERS 31

Associative Containers

1. Introduction

Associative containers allow for very fast element search. Sequential containers
have some natural ordering that allows you to iterate from the beginning of
the container to the end in a well-specified order. Associative containers are
a bit different. This container family splits along three axes:

• Whether elements contain keys (a set) or key-value pairs (a map)


• Whether elements are ordered
• Whether keys are unique

2. Iterators

All ordered associative containers support the same set of iterator-related methods as
supported by the vector container:

begin() , end() , cbegin() , cend() , rbegin() , rend() , crbegin()


, and crend() .

All associative containers support the following Size methods :


1) Complexity

The complexity for all four ordered associative containers is the same:

Insertion : O(log(N))
Deletion : O(log(N))
Access : O(log(N))

3. Types of associative containers

I. Set and Multiset

A Set is a container that contains a unique group of sorted elements.Multisets are the same
except that they do not allow duplicates.
II. Sets

The class template set<T, Comparator, Allocator> takes three template


parameters:

1. The key type T


2. The comparator type that defaults to std::less
3. The allocator type that defaults to std::allocator<T>

You have a lot of flexibility when constructing sets.

Each of the following constructors accepts an optional comparator and allocator


(whose types must match their corresponding template parameters):

1. A default constructor that initializes an empty set


2. Move and copy constructors with the usual behavior
3. A range constructor that copies the elements from the range into the set
4. A braced initializer

A set is similar to a map , but it does not store pairs, only unique keys without values . A
multiset supports duplicate keys. There is only one template type parameter: the key type.
The insert() method takes a single key instead of a pair .

1) Insertion methods:

Insert() function

2) Deletion:

Erase() function

3) Access:

Because the order of the elements is determined by the comparator, they do not take a
position argument like they do for sequential containers. Both insertion and removal are fast.
Since sets are optimized for element lookup, they provide special search functions. The
find() function returns the position of the first element equal to the provided value, or
the position past the end of the set when the element is not found. When we look for an
element with find, we should always compare it with the result of calling end() on the
container to check whether the element was found.

Let's examine the following code:

#include <iostream>
#include <set>

int main() {

std::set<int> numbers;

numbers.insert(10);

if (numbers.find(10) != numbers.end()) {
std::cout << "10 is in numbers" << std::endl;}

return 0;
}

Finally, count() returns the number of elements equal to the value provided.
The set and multiset classes are defined in the <set> header file.

Example of a set with a custom comparator:

#include <iostream>
#include <set>
#include <functional>

int main() {

std::set<int> ascending = {5,3,4,2,1};


std::cout << "Ascending numbers:";

for(int number : ascending) {


std::cout << " " << number;}

std::cout << std::endl;

std::set<int, std::greater<int>> descending = {5,3,4,2,1};


std::cout << "Descending numbers:";

for(int number : descending) {


std::cout << " " << number;
}

std::cout << std::endl;


return 0;
}

Output:

Ascending numbers: 1 2 3 4 5
Descending numbers: 5 4 3 2 1

Using set to Look up Items Listing 6-10

#include <iostream>
#include <set>
using namespace std;

class Emp {

public:

string Nickname;
string SSN;

Emp(string anickname, string asocial) :


Nickname(anickname),SSN(asocial) {}

Emp() : Nickname(""), SSN("") {}

};

bool operator < (const Emp& first, const Emp& second) {


return first.SSN < second.SSN;}

ostream& operator << (ostream &out, const Emp &emp) {


cout << "(" << emp.Nickname;
cout << "," << emp.SSN;
cout << ")";
return out;
}

int main() {

set<Emp> employees;
Emp emp1("sparky", "123-22-8572");
employees.insert(emp1);
Emp emp2("buzz", "234-33-5784");
employees.insert(emp2);
Emp emp3("albert", "123-22-8572");
employees.insert(emp3);

Emp emp4("sputz", "199-19-0000");


employees.insert(emp4);

// List the items

set<Emp>::iterator iter = employees.begin();

while (iter != employees.end()){


cout << *iter << endl;
iter++; }

// Find an item

cout << "Finding..." << endl;

Emp findemp("", "123-22-8572");

iter = employees.find(findemp);

cout << *iter << endl;

return 0;
}

When you compile and run this example, you see the following output:

1. (sparky,123-22-8572)
2. (sputz,199-19-0000)
3. (buzz,234-33-5784)
4. Finding...
5. (sparky,123-22-8572)

The comparison results in two things:

»»Ordering: The items in set are in Social Security number order. This isn’t true
with all containers, but it’s the way a set works.

»»Duplicates: The set ignores any attempt to add two employees with matching
SSN values (even if other properties differ).
You can see in this listing that the code tries to add two employees with the same
SSN values:

Employee emp1("sparky", "123-22-8572");


employees.insert(emp1);

and

Employee emp3("albert", "123-22-8572");


employees.insert(emp3);

Later, when the code prints all the items in set, you see only the one for "sparky",
not the one for "albert". set ignored the second employee.

III. A Set Summary Table


3. Unionizing and intersecting sets
When you work with sets, you commonly do the following:

1. Combine two sets to get the union (all the elements in both sets without any
duplicates).

2. Find the common elements to get the intersection (those unique elements
that appear in both sets).

When you #include <set>, you automatically get a couple of handy functions
for finding the union and intersection of some sets. The Sets2 example, shown
in Listing 6-11, demonstrates how you can find the intersection and union of two
sets.

Finding an Intersection and a Union Is Easy!

#include <iostream>
#include <set>
#include <algorithm>
using namespace std;

void DumpClass(set<string> *myset) {

set<string>::iterator iter = myset->begin();

while (iter != myset->end()){


cout << *iter << endl;
iter++; }

int main() {

set<string> English;
English.insert("Zeus");
English.insert("Magellan");
English.insert("Vulcan");
English.insert("Ulysses");
English.insert("Columbus");

set<string> History;
History.insert("Vulcan");
History.insert("Ulysses");
History.insert("Ra");
History.insert("Odin");
set<string> Intersect;

insert_iterator<set<string> >

IntersectIterate(Intersect, Intersect.begin());

set_intersection(English.begin(), English.end(),

History.begin(), History.end(), IntersectIterate);

cout << "===Intersection===" << endl;

DumpClass(&Intersect);

set<string> Union;

insert_iterator<set<string> >

UnionIterate(Union, Union.begin());

set_union(English.begin(), English.end(),

History.begin(), History.end(), UnionIterate);

cout << endl << "===Union===" << endl;

DumpClass(&Union);

return 0;
}

When you run the code in Listing 6-11, you see this output:

1. ===Intersection===
2. Ulysses
3. Vulcan
4. ===Union===
5. Columbus
6. Magellan
7. Odin
8. Ra
9. Ulysses
10. Vulcan
11. Zeus

But as you can see, something a little bizarre is in the code. Specifically, this part
isn’t exactly simple:
insert_iterator<set<string> >
IntersectIterate(Intersect, Intersect.begin());

This code is used in the call to set_intersection(). It’s a variable declaration.


The first line is the type of the variable, a template called insert_iterator. The
template parameter is the type of set, in this case set<string>.

The next line is the instance name, IntersectIterate, and the constructor
requires two things:

1. the set that will hold the intersection (called Intersect) .


2. an iterator pointing to the beginning of the set, which is Intersect.begin().

The variable that these two lines create is an iterator, which is a helper object
that another function can use to insert multiple items into a list. In this case, the
function is set_intersection(). The set_intersection() function doesn’t take
the sets as input; instead, it takes the beginning and ending iterators of the two
sets, along with the IntersectIterate iterator declared earlier.

You can see in Listing 6-11 that those are the five items passed to the
set_intersection()function. After calling set_intersection(), the Intersect object
contains
the intersection of the two sets. set_union() works precisely the same way as
set_intersection(), except it figures out the union of the two sets, not the
intersection.

To use set_intersection() and set_union(), you need to add #include


<algorithm> to the top of your listing. This is one of the header files in the Standard
Library.

If you find the code in Listing 6-11 particularly ugly, a slightly easier way to call
set_intersection(), one that doesn’t require you to directly create an instance
of insert_iterator, is available. It turns out that a function exists that will do
it for you. To use this function, you can remove the declarations for IntersectIterate
and UnionIterate, and then instead call set_intersection(), like this:

set_intersection(English.begin(), English.end(),

History.begin(), History.end(),

inserter(Intersect, Intersect.begin()));

// The third line simply calls inserter(), which creates an instance


//of insert_iterator for you. Then you can do the same for
//set_union():
set_union(English.begin(), English.end(),

History.begin(), History.end(),

inserter(Union, Union.begin()));

CHAPTERS Map and Multimap 32

1. Introduction

Map and multimap are containers that manage key/value pairs as elements.
It is a data structure that stores key-value pairs.The elements are ordered according to the
key. That is, when iterating over all elements contained in an ordered associative container,
they are enumerated in an order with increasing key values , not in the order these elements
were inserted. For a map there can be no duplicate keys, whereas a multimap supports
duplicate keys. When defining a map , you need to specify both the key type and the value
type. You can immediately initialize a map with a braced initializer:

2. Map

The class template map<Key, Value, Comparator, Allocator> takes four template
parameters. The first is the key type Key. The second is the value type Value.
The third is the comparator type, which defaults to std::less. The fourth
parameter is the allocator type, which defaults to std::allocator<T>.

The elements are sorted automatically according to the provided comparator and
applied to the key, the value does not influence the order of the elements:
Map allows you to associate a single value to a key, while multimap allows you to
associate multiple values to the same key.
The map and multimap classes are defined in the <map> header file.

1) Access:

A way to accessing a value from a key is to use at(), which takes a key and
returns the associated value. If there is no associated value, at() will throw an exception.

A different alternative to get the value associated with a key is to use operator[].
The operator[] returns the value associated with a key, and if the key is not present, it
inserts a new key/value pair with the provided key, and a default value for the value.
Because operator[] could modify the map by inserting into it, it cannot be used on a
const map:

#include <iostream>
#include <map>
int main()
{
std::map<int, int> map;
std::cout << "We ask for a key which does not exists: it is default
inserted: " << map[10] << std::endl;
map.at(10) += 100;
std::cout << "Now the value is present: " << map.find(10)->second <<
std::endl;
}

Map also provides a find() function, which looks for a key in the map and
returns the position of the key/value pair if it exists, or the same result of calling end().

From the position, we can access the key with position->first and the value with
position->second:

#include <iostream>
#include <string>
#include <map>

int main()
{
std::map<int, std::string> map;
map.insert(std::make_pair(1, "some text"));
auto position = map.find(1);
if (position != map.end() ) {
std::cout << "Found! The key is " << position->first << ", the value
is " << position->second << std::endl;
}
}

2) Insertion methods:

To insert values into a map, we can call insert(), providing a pair containing the key
and the value. The function also returns a pair, containing the position at which the element
was inserted, and a Boolean set to true if the element was inserted, or false if an element
with the same key already exists.

The keys of a map are sorted and unique, and map supports all the same operations as set.
In fact, you can think of a set as a special kind of map containing keys and empty values.
Accordingly, map supports efficient insertion, removal, and search, and you have control
over sorting with comparator objects.

The map constructors are direct analogues to the constructors of set:


a default constructor that initializes an empty map; move and copy constructors
with the usual behavior; a range constructor that copies the elements
from the range into the map; and a braced initializer. The main difference
is in the braced initializer, because you need to initialize key-value pairs
instead of just keys. To achieve this nested initialization, you use nested
initializer lists
The major advantage of working with a map instead of a set of pairs is
that map works as an associative array. An associative array takes a key rather
than an integer-valued index. Think of how you use the at and operator[]
methods to access indices in sequential containers. Because sequential containers
have a natural ordering of elements, you use an integer to refer to
them. The associative array allows you to use types other than integers to
refer to elements. For example, you could use a string or a float as a key.
To enable associative array operations, map supports a number of useful
operations; for example, allowing you to insert, modify, and retrieve values
by their associated keys.

3. Mapping your data

The Maps example, shown in Listing 6-4, demonstrates a type of container called
a map. A map works much the same as a vector, except for one main difference:
You look up items in vector by putting a number inside brackets, like this:

cout << names[0] << endl;

But with a map, you can use any class or type you want for the index (called a key),
not just numbers. To create an entry, you use a key (the index) and a value (the
data) as a pair.

4. Associating Objects using map

#include <iostream>
#include <map>
using namespace std;

int main() {

map<string, string> marriages;

marriages["Tom"] = "Suzy";
marriages["Harry"] = "Harriet";
cout << marriages["Tom"] << endl;
cout << marriages["Harry"] << endl;

return 0; }

To use map, you declare a variable of class map, supplying two template parameters,
the key class and the value class, which are both strings in the example.
To store a map value, you place a key inside brackets and set it equal to a value:

marriages["Tom"] = "Suzy";

To retrieve that particular item, you supply the key in brackets:

cout << marriages["Tom"] << endl;

When you run this example, you see the following two strings as output:

Suzy
Harriet

Even though the keys can be any type or class, you must specify the type or class
you’re using it when you set up a map. After you do that, you can use only that type for
the particular map. Thus, if you say that the keys will be strings, you cannot then
use an integer for a key, as in marriages[3] = "Suzy";.
Searching If you want to find out whether a certain key is in an associative container,
you can use these:
1. find() : Returns an iterator to the found element (a key-value pair for maps) or
the end iterator if the given key is not found.

2. count() : Returns the number of keys matching the given key. For map or set ,
this can only be 0 or 1, whereas for multimap or multiset , this can be larger than 1.

5. Multisets

The std::multiset available in the STL’s <set> header is an associative container


that contains sorted, non-unique keys. A multiset supports the same
operations as a set, but it will store redundant elements. This has important
ramifications for two methods:

1. The method count can return values other than 0 or 1. The count method
of a multiset will tell you how many elements matched the given key.

2. The method equal_range can return half-open ranges containing more than one
element. The equal_range method of multiset will return a range containing all the
elements matching the given key.

You might want to use a multiset rather than a set if it’s important that you store multiple
elements with the same key.
For example, you could store all of an address’s occupants by treating the address as a key
and each member of the house as an element. If you used a set, you’d be stuck having
only a single occupant.

6. Multimaps

The std::multimap available in the STL’s <map> header is an associative container


that contains key-value pairs with non-unique keys. Because the keys
are not unique, multimap doesn’t support the associative array features that
map does. Namely, operator[] and at aren’t supported. As with multiset,
multimap offers element access primarily through the equal_range method.
7. MAPS VERSUS SETS

It’s important to realize the difference between map and set. map lets you store information
based on a key, through which you can retrieve a value. Listing 6-7, presented
earlier in the “Performing comparisons” section, shows an example in which the key
is an Emp instance and the value is a Salary instance. But with set, you can achieve
something similar: Listing 6-10 could use a single class containing both Emp and Salary
information. Also, you can see in Listing 6-10 that it’s possible to look up the Emp
instance based on nothing but a Social Security number. So in this sense, the Listing 6-
10 example shows a map in which the key is a Social Security number and the value is
the rest of the employee information. The fact is, you can often accomplish associations
with set, as you can with map. The advantage to set is that you need to store only one
instance for each item, whereas with map, you must have two instances, both a key and
a value. The advantage to map is that you can use the nice bracket notation. The choice
is yours.
CHAPTER UNORDERED CONTAINERS 33

1. Order of Elements

The ordered associative containers store their elements in an ordered fashion. By default,
std::less is used for this ordering, which, unless specialized, relies on operator < of the
Key type.

2. Unordered Containers

Unordered associative containers differ from associative containers in that the


elements have no defined order. On the other hand, all the unordered containers depend on
a hash function. They do not order the elements but instead store them in buckets in a hash
map.

3. A hash map or hash table

It is an efficient data structure storing its elements in buckets. Conceptually, the map
contains an array of pointers to buckets, which are in turn arrays or linked lists of elements.
Through a mathematical formula called hashing , a hash integer number is calculated, which
is then transformed into a bucket index. Two elements resulting in the same bucket index
are stored inside the same bucket.

I. Description

A hash map allows for very fast retrieval of elements. To retrieve an element, calculate its
hash value, which results in the bucket number.

Typically, unordered containers are implemented as hash tables. The position in the
array is determined using the hash function, which given a value returns the position
at which it should be stored.
Ideally, most of the elements will be mapped into different positions, but the hash function
can potentially return the same position for different elements. This is called a collision. This
problem is solved by using linked lists to chain elements that map into the same position, so
that multiple elements can be stored in the same position. Because there might be multiple
elements at the same position, the position is often called bucket.

Implementing unordered containers using a hash table allows us to find an element


with a specific value in constant time complexity, which translates to an even faster
lookup when compared to associative containers:

When an element is added to the set, its hash is computed to decide in which bucket the
element should be added. The elements inside a bucket are stored as nodes of a list.
When a key/value pair is added to the map, the hash of the key is computed to decide
in which bucket the key/value pair should be added:
Figure 5.15: Representation of computing the bucket of an element from the key, and storing
the key/ value pair as nodes in a list.

Unordered associative containers and ordered associative containers provide the same
functionalities, and the explanations in the previous section apply to the unordered
associative containers as well. Unordered associative containers can be used to get
better performances when the order of the elements is not important.

4. Template Type Parameters

The unordered associative containers allow you to specify your own hasher and your own
definition of how to decide whether two keys are equal by specifying extra template type
parameters . Here are the template definitions for all unordered associative containers:

5. Hash Functions

The Standard provides the following std::hash template (the base template is defined in but
is included also in the headers):
Specializations are provided for several types, such as bool , char , int , long , double ,
and std::string . If you want to calculate a hash of your own object types, you can
implement your own hashing functor class. However, we recommend that you implement a
specialization of std::hash instead.

The following is an example of how you could implement a std::hash specialization for the
Person class defined in the introduction chapter. It uses the standard std::hash
specialization for string objects to calculate the hash of the first and last name. Both hashes
are then combined by a XOR operation (encrypted operations) . Simply XORing values
generally does not give sufficiently randomly distributed integers, but if both operands are
already hashes, it can be considered acceptable:

1) Complexity of all unordered associative containers

a) Insertion : O(1) on average, O(N) worst case


b) Deletion : O(1) on average, O(N) worst case
c) Access : O(1) on average, O(N) worst case

All unordered associative containers support the same methods as the ordered associative
containers, except reverse iterators, lower_bound() , and upper_bound() . The
following subsections give an overview of all additional operations supported by
unordered_map (UM) , unordered_multimap (UMM), unordered_set (US), and
unordered_multiset (UMS), divided into categories.
5. Using std::unordered_set to create an unordered set

Like the other containers discussed in this chapter, an unordered_set provides a


particular method for storing data in a manner that makes it easy to access later.
In this case, you have access to functions that insert() and erase() elements
from the container. A special function, emplace() enables you to add new elements
only if the element doesn’t exist. Otherwise, the unordered set will allow as
many duplicates as you want (and you can easily count them using count()). You
can also use the find() function to track down elements that you want. Special
functions tell you when you’re at the beginning or end of the set.

I. Manipulating unordered sets

The easiest way to see how an unordered set works is to create one. The UnorderedSet
example, shown in Listing 6-16, demonstrates how to use the various
unordered_set features to maintain a listing of colors.

II. Creating and Using Dynamic Arrays

#include <iostream>
#include <unordered_set>
using namespace std;
int main() {
unordered_set<string> Colors;
Colors.insert("Red");
Colors.insert("Green");
Colors.insert("Blue");
if(Colors.find("Red")!= Colors.end())
cout << "Found Red!" << endl;
auto ReturnValue = Colors.emplace("Red");
if(!ReturnValue.second)
cout << "Red is Already in Set!" << endl;
cout << "There are " << Colors.count("Red")
<< " Red entries." << endl;
ReturnValue = Colors.emplace("Orange");
if(!ReturnValue.second)
cout << "Orange is Already in Set!" << endl;
else
cout << "Orange Added to Set!" << endl;
Colors.erase("Red");
if(Colors.find("Red")!= Colors.end())
cout << "Found Red!" << endl;
else
cout << "Red Missing!" << endl;
return 0;
}

The example begins by creating a new unordered_set, Colors. Notice that this is
a template, so you need to provide a type for the information that the set will hold.
The code uses the insert() function to add three colors to the set.
The find() function enables you to look for a particular value in the set. When the
value is missing, the find() function returns end(), which means that the current
position within the set is at the end.

This example uses the auto data type. ReturnValue is used to detect when a value
that you want to add to the set using emplace() already exists. If the value already
exists, unordered_set refuses to add it when you call emplace(). On the other
hand, if you call insert(), unordered_set will add duplicate entries.
To remove entries from a set, you call erase() with the value you want to remove.
In this case, the example removes the color Red. It then searches for Red using
find().

As you might expect, Red isn’t found this time. The output from this example is as
follows:

1. Found Red!
2. Red is Already in Set!
3. There are 1 Red entries.
4. Orange Added to Set!
5. Red Missing!

III. Unordered Sets

The std::unordered_set available in the STL’s <unordered_set> header is an


associative container that contains unsorted, unique keys. The unordered_set
supports most of the same operations as set and multiset, but its internal
storage model is completely different.

You might want to use an unordered_set in a situation in which there is no natural ordering
among the keys and you don’t need to iterate through the collection in such an order. You
might find that in many situations, you could use either a set or an unordered_set.

a. Storage Model: Hash Tables

A hash function, or a hasher, is a function that accepts a key and returns a


unique size_t value called a hash code. The unordered_set organizes its
elements into a hash table, which associates a hash code with a collection of one or more
elements called a bucket. To find an element, an unordered_set computes its hash code
and then searches through the corresponding bucket in the hash table.

If you’ve never seen a hash table before, this information might be a


lot to take in, so let’s look at an example.
Imagine you had a large group of people that you needed to sort into some kind of sensible
groups to find an individual easily.

You could group people by birthday, which would give you 365 groups (well, 366 if you count
February 29 for leap years).

The birthday is like a hash function that returns one of 365 values for each person.
Each value forms a bucket, and all people in the same bucket have the same
birthday.

In this example, to find a person, you first determine their birthday,


which gives you the correct bucket.

Then you can search through the bucket to find the person you’re looking for.

As long as the hash function is quick and there aren’t too many elements
per bucket, unordered_sets have even more impressive performance than
their ordered counterparts.

When you have a hash collision,


it means that the two keys will reside in the same bucket. In the preceding
birthday example, many people will have the same birthday, so there will
be a lot of hash collisions. The more hash collisions there are, the larger
the buckets will be, and the more time you’ll spend searching through a
bucket for the correct element.

1. It accepts a Key and returns a size_t hash code.


2. It doesn’t throw exceptions.
3. Equal keys yield equal hash codes.
4. Unequal keys yield unequal hash codes with high probability. (There
is a low probability of a hash collision.)

The STL provides the hasher class template std::hash<T> in the


<functional> header, which contains specializations for fundamental
types, enumeration types, pointer types, optional, variant, smart pointers,
and more.
As an example, Listing 13-29 illustrates how std::hash<long>
meets the equivalence criteria.

Constructing

The class template std::unordered_set<T, Hash, KeyEqual, Allocator> takes


four template parameters:

1. Key type T
2. The Hash hash function type, which defaults to std::hash<T>
3. The KeyEqual equality function type, which defaults to std::equal_to<T>
4. The Allocator allocator type, which defaults to std::allocator<T>
5. Braced initialization.

Supported set Operations

IV. Unordered Multisets

The std::unordered_multiset available in the STL’s <unordered_set> header


is an associative container that contains unsorted, non-unique keys. An
unordered_multiset supports all the same constructors and operations as
an unordered_set, but it will store redundant elements. This relationship is
analogous to unordered_sets and sets: both equal_range and count have slightly
different behavior to account for the non-uniqueness of keys.
Chapter Container Adaptors 34

1. Container Adaptors

Additional container classes that are provided by the STL library are container adaptors.
Container adaptors provide constrained access policies on top of the containers we
have looked at in this chapter.

The STL provides three container adapters that encapsulate other STL containers
and expose special interfaces for tailored situations. The adapters
are the stack, the queue, and the priority queue.

A stack is a data structure with two fundamental operations: push and


pop. When you push an element onto a stack, you insert the element onto
the stack’s end. When you pop an element off a stack, you remove the element
from the stack’s end. This arrangement is called last-in, first-out: the
last element to be pushed onto a stack is the first to be popped off.
The STL offers the std::stack in the <stack> header. The class template
stack takes two template parameters. The first is the underlying type of the
wrapped container, such as int, and the second is the type of the wrapped
container, such as deque or vector. This second argument is optional and
defaults to deque.

To construct a stack, you can pass a reference to a deque, a vector, or a


list to encapsulate. This way, the stack translates its operations, such as
push and pop, into methods that the underlying container understands, like
push_back and pop_back. If you provide no constructor argument, the stack
uses a deque by default. The second template parameter must match this
container’s type.
To obtain a reference to the element on top of a stack, you use the top
method.

Container adaptors have a template parameter that the user can provide to specify the
type of container to wrap:

I. Stack

Elements can only be read or removed from the top, so the last inserted element
is the first that gets removed.The access pattern of the stack data structure happens mainly
through three core member functions: push(), top(), and pop(). The push() function is used to
insert an element into the stack, top() used to access the element on top of the stack, and
pop()is used to remove the top element.

The stack class is defined in the <stack> header file.


Here is the template definition of stack : <template> class stack; The complexity for a stack
is as follows:

1. Insertion : O(1) for list as underlying container,


2. Amortized O(1) for vector and deque
3. Deletion : O(1) for list , vector and deque as underlying container
4. Access : Not possible

Stack supported Operations


II. Queue

the elements are enqueued one after the other, so that elements inserted before are ahead
of elements inserted after. Elements are inserted at the end of the queue and removed at
the start.
The interface of the queue data structure is composed of the push(), front(), back(),
and pop() member functions.

The push() function is used to insert an element into the queue(); front() and back()
return the next and last elements of the queue, respectively; the pop() is used to
remove the next element from the queue.

You can compare a queue to a person who arrived before you will be allowed to enter
before you. A queue needs access to the front and the back, so the underlying container
must support back() , front() , push_back() , and pop_front() .

The standard list and deque support these methods and can be used as underlying
containers. The default container is the deque .

Here is the template definition of queue :< template> class queue;

The complexity for a queue is as follows:

1. Insertion : O(1) for list as underlying container;


2. Amortized ; O(1) for deque
3. Deletion : O(1) for list and deque as underlying container
4. Access : Not possible

The queue class is defined in the <queue> header file.

Unlike a stack, a queue is first-in, first-out. When you


push an element into a queue, you insert onto the queue’s end. When
you pop an element off the queue, you remove from the queue’s beginning.
This way, the element that has been in the queue the longest is the
one to get popped off.

The STL offers the std::queue in the <queue> header. queue


takes two template parameters. The first parameter is the underlying type
of the wrapped container, and the optional second parameter is the type of
the wrapped container, which also defaults to deque.

Among STL containers, you can only use deque or list as the underlying
container for a queue, because pushing and popping from the front of a vector.

a. Queue Supported Operations


b. Waiting in line with stacks and queues

Two common programming data structures are in the Standard Library:

Stack: You put items on top of a stack one by one — and you take items off
the top of the stack one by one. You can add several items, one after the
other, before taking an item off the top. This process is sometimes called a
Last In, First Out (LIFO) algorithm.

Queue: A queue is like waiting in line at the post office — the line gets longer
as people arrive. Each new person goes to the back of the line. People leave
from the front of the line. Like the stack, the queue also has an alternate
name: it’s a First In, First Out (FIFO) algorithm.

To use the Standard Library to make a stack, you can use a deque, a list, or a
vector as the underlying storage bin.

Then you declare the stack, as in the following example:

stack<int, vector<int> > MyStack;

Or you can optionally use the default, which is deque:

stack<int> MyStack;

For a queue, you can’t use vector because vectors don’t include operations for
dealing with the front of an item list. So, you can use either deque or list. Here’s
a line of code that uses list:

queue<int, list<int> > MyQueue;

Or here’s a line of code that uses deque by default:

queue<int> MyQueue;

You normally perform three operations with a stack and a queue:

Push: When you add an item to a stack or queue, you push the item. This
action puts the item on top of the stack or at the back of the queue.

Peek: When you look at the top of the stack or the front of the queue, you
peek. The peek operation doesn’t remove the item.

Pop: When you remove an item from the top of a stack or from the front of
the queue, you pop it off.

To peek at the front of a queue, you call the front() method. For a stack, you call
the top() method.

For pushing and popping, the queue and stack each include a push() function and a
pop() function. The StackAndQueue example, shown in Listing 6-13, demonstrates both a
stack and a queue.

// Creating a Stack and a Queue

#include <iostream>
#include <stack>
#include <queue>

using namespace std;

void StackDemo() {

cout << "===Stack Demo===" << endl;

stack<int, vector<int> > MyStack;

MyStack.push(5);
MyStack.push(10);
MyStack.push(15);
cout << MyStack.top() << endl;
MyStack.pop();

cout << MyStack.top() << endl;

MyStack.pop();
MyStack.push(40);

cout << MyStack.top() << endl;

MyStack.pop();
}

void QueueDemo() {
cout << "===Queue Demo===" << endl;

queue<int> MyQueue;
MyQueue.push(5);
MyQueue.push(10);
MyQueue.push(15);

cout << MyQueue.front() << endl;

MyQueue.pop();

cout << MyQueue.front() << endl;

MyQueue.pop();

MyQueue.push(40);

cout << MyQueue.front() << endl;

MyQueue.pop();

int main() {

StackDemo();
QueueDemo();
return 0;

When you specify a container to use inside the stack or queue, remember to put a
space between the closing angle brackets.
Otherwise, the compiler reads it as a single insertion operator, >>, and gets confused.

Here is the output from this example:

===Stack Demo===

15
10
40

===Queue Demo===
5
10
15

III. Priority Queue

Finally, the priority queue is a queue where the elements are accessed according to
their priority, in descending order (highest priority first).

The interface is similar to the normal queue, where push() inserts a new element and
top() and pop() access and remove the next element. The difference is in the way
the next element is determined. Rather than being the first inserted element, it is the
element that has the highest priority.

The priority queue class is also defined in the <queue> header file (std::priority_queue).

A priority_queue is similar to a queue but stores the elements according to a priority. The
element with highest priority is at the front of the queue. In the case of a night club, VIP
members get higher priority and are allowed to enter before non-VIPs. A priority_queue
needs random access on the underlying container and only needs to be able to modify the
container at the back, not the front. Therefore, the underlying container must support random
access, front() , push_back() , and pop_back() .

The vector and deque are available options, with the vector being the default underlying
container.

A Compare instance can optionally be provided to the priority_queue constructor; if not, one
is default-constructed.

a) The complexity for a priority_queue is as follows:

• Insertion : Amortized O(log(N)) for vector or deque as underlying container


• Deletion : O(log(N)) for vector and deque as underlying container
• Access : Not possible

b. Priority Queues (Heaps) :

A priority queue (also called a heap) is a data structure that supports push and
pop operations and keeps elements sorted according to some user-specified
comparator object. The comparator object is a function object invokable with
two parameters, returning true if the first argument is less than the second.
When you pop an element from a priority queue, you remove the element
that is greatest, according to the comparator object.

The STL offers the std::priority_queue in the <queue> header. A priority


_queue has three template parameters:

• The underlying type of the wrapped container


• The type of the wrapped container
• The type of the comparator object

Only the underlying type is mandatory. The wrapped container type


defaults to vector (probably because it’s the most widely used sequential
container), and the comparator object type defaults to std::less.
chapter…. 35

1. Containing instances, pointers, or references

One of the most common discussions you encounter when people start talking
about how to use the container templates is whether to put instances in the containers,
pointers, or references. For example, which of the following should you type?

vector<MyClass>
vector<MyClass *>
vector<MyClass &>

in other words, do you want your container to store the actual instance (whatever
that might mean), a reference to the actual instance, or a pointer to the instance?

To explore this idea, look at the Maps2 example in Listing 6-5. Here, you’re trying
out the different ways of storing things in map: instances, pointers, and references.

I. Making Decisions: Oh, What to Store?

#include <iostream>
#include <map>
using namespace std;

class StoreMe {

public:

int Item;

};

bool operator < (const StoreMe & first, const StoreMe & second) {
return first.Item < second.Item;
}

int main() {

// First try storing the instances

map<StoreMe, StoreMe> instances;

StoreMe key1 = {10}; // braces notation!


StoreMe value1 = {20};
StoreMe key2 = {30};
StoreMe value2 = {40};

instances[key1] = value1;
instances[key2] = value2;

value1.Item = 12345;

cout << instances[key1].Item << endl;


instances[key1].Item = 34567;
cout << instances[key1].Item << endl;
// Next try storing pointers to the instances

map<StoreMe*, StoreMe*> pointers;


StoreMe key10 = {10};
StoreMe value10 = {20};
StoreMe key11 = {30};
StoreMe value11 = {40};

pointers[&key10] = &value10;
pointers[&key11] = &value11;
value10.Item = 12345;
cout << (*pointers[&key10]).Item << endl;

// Finally try storing references to the instances.


// Commented out because it causes an error.)
// map<StoreMe&, StoreMe&> pointers;

return 0;
}

To create the instances of StoreMe, you use the braces notation. You can do that
when you have no constructors. So the line StoreMe key1 = {10}; creates an instance
of StoreMe and puts 10 in the Item property.

To create an individual instance entry, you need both a key and a value instance of
StoreMe. You then use the key to provide a name for the value stored in the map.

Consequently, instances contain two entries consisting of two StoreMe objects each.
Here’s what you see when you run the application:

20
34567
12345

This output doesn’t precisely match expectations because the code changes the
Item property in value1:

value1.Item = 12345;

When the code outputs the value of instances[key1].Item, you see an output of
20, not 12345. That means that the value stored in map is a copy, not the original.

However, when the code changes the value in instances like this:

instances[key1].Item = 34567;
the value portion of the instances entry does change. You see 34567 as output.

Consequently, when working with map instances, you must modify the map entry
directly.

Now that you’ve figured out that map is storing copies of what you put in it, the
idea of storing a pointer should be clear: If you have a pointer variable and then
you make a copy of it, although you have a separate pointer variable, the original
and the copy both point to the same memory location.

That’s the idea behind thesecond part of Listing 6-5. You create pointers like this:

map<StoreMe*, StoreMe*> pointers;

Now this map stores pointer variables. Remember that a pointer variable just holds
a number that represents an address. If two separate pointer variables hold the
same number, it means that they point to the same object at the same address.
Furthermore, because this map is holding pointers, it’s holding numbers, not
instances — something to think about.

To store a value when using pointers, you need to use code like this:

pointers[&key10] = &value10;

Note the use of the ampersand (&) used as a reference operator to store addresses
in map. It’s now possible to change the Item member of one the value objects:

value10.Item = 12345;
that you print using this carefully parenthesized line:
cout << (*pointers[&key10]).Item << endl;
and you see this:
12345

When working with map pointers, you modify the original variable to make a
change because the map entries point to the original variable, rather than make
a copy of it. However, as you can see, using map pointers also makes your code
harder to read.

Don’t worry just now about the bool operator < (const StoreMe & first,
const StoreMe & second)> function.

This function is explained in the “Performing comparisons” section, later in this chapter.

Note that the following line is commented out:


// map<StoreMe&, StoreMe&> pointers;

It attempts to declare a map that holds references, but the code generates a compiler
error instead. Try uncommenting the commented line and see the error message.

Here’s an example of what you might see (make sure to add the comment
back in when you’re done):

error: conflicting declaration 'std::map<StoreMe&, StoreMe&> pointers'


error: 'pointers' has a previous declaration as 'std::map<StoreMe*, StoreMe*> pointers'

References are out of the question because the map is making a copy of everything
you put in it.

2. Working with copies

All C++ containers, not just maps, generally make copies of whatever you stick
inside them as shown in the previous section.

The Vectors3 example, shown in Listing 6-6, replicates the essential functionality of the
Maps2 example shown in Listing 6-5.

LISTING 6-6: The vector Version of the Maps2 Example:

#include <iostream>
#include <vector>
using namespace std;

class StoreMe {

public:
int Item;

};

int main() {

vector<StoreMe> instances;
StoreMe value1 = {20};
StoreMe value2 = {40};
instances.push_back(value1);
instances.push_back(value2);
value1.Item = 12345;
cout << instances[0].Item << endl;
instances[0].Item = 34567;
cout << instances[0].Item << endl;

vector<StoreMe*> pointers;
StoreMe value10 = {20};
StoreMe value11 = {40};
pointers.push_back(& value10);
pointers.push_back(& value11);
value10.Item = 12345;
cout << (*pointers[0]).Item << endl;
return 0;
}

Oddly enough, the output from this example is precisely the same as the Maps2
example and for the same reason. Whether the container is a vector or a map
doesn’t matter; both of them hold copies of the objects or pointers you provide.

Consequently, you can remember these two rules about deleting your original
objects:

»»When the container holds instances: If you’re putting instances in the


container, you can delete the original instances after they’re added. This is
okay because the container has its own copies of the instances.
»»When the container holds pointers: If you’re putting pointers in the
container, you don’t want to delete the original instances because the pointers
in the container still point to these instances.

It’s up to you to decide which method is better. But here are a couple of things to
consider:

»»Keeping instances around: If you don’t want to keep instances lying around,
you can put the instances in the container, and it will make copies.

»»Copy Ability: Some classes, such as classes filled with pointers to other classes
or classes that are enormous, don’t copy well. In that case, you may want to
put pointers in the container.

3. Comparing instances

When you work with classes that contain other classes (such as vector), you need
to provide the class with a way to compare two things. The following sections
describe how to provide comparison capability when working with containers.

Here’s an example;

You create a class called Employee that contains these properties:

1. FirstName
2. LastName
3. SocialSecurityNumber.

Next, you create a Salary class that contains payroll information for an employee. This
class has properties MonthlySalary and Deductions.

With these two objects in place, you create a map instance, where each key/value
pair contains an Employee instance for the key and a Salary instance for the
value.

To look up an employee, you would make an instance of Employee and fill in the
FirstName, LastName, and SocialSecurityNumber properties.

You then retrieve the value based on this key.

There are two issues here:

You’d create an instance and allow map to find the key that matches the
instance. It’s essential to know whether map is looking for the exact same
instance or one identical to it. When looking for the exact same instance, you
need a pointer to the original object, not a new object that you fill in

I. Performing comparisons

The previous section provides details on two issues you must resolve when comparing
objects. Here’s how to resolve these two issues: If you’re dealing with your
own classes, in addition to setting up a container class, you also provide a function
that compares two instances of your own class. Your comparison function can
determine whether two classes are equal, the first is less than the second, or the first is
greater than the second.

At first, how less than and greater than can apply to things like an Employee
class may not seem apparent. But the idea behind less than and greater than is to
give the container class a way to determine a sort order. For example, you might
choose to sort an Employee class in one of these ways:

»»Social Security number


»»Last name, first name
»»First name, last name
»»Employee ID
»»Address
»»Organizational department
The point is that the computer can’t make this decision; you need to choose how
you want the data to appear. After you decide how you want them sorted, you’d
create a function that determines when one record is less than, equal to, or greater
than the other.

If you want the list to sort by name, you would make your function
look strictly at the names. But if you want your list to sort by Social Security
number, you would write your function to compare the Social Security numbers.

The Maps3 example, shown in Listing 6-7, contains a map class with a comparison
function that determines whether two keys are equal.

Containing Instances and Needing Functions That Compare Them:

#include <iostream>
#include <map>
using namespace std;

class Emp {

public:

string Nickname;
string SSN;
Emp(string anickname, string asocial) :
Nickname(anickname),SSN(asocial) {}

Emp() : Nickname(""), SSN("") {}

};

class Salary {

public:

int YearlyInc;
int Taxes;
Salary(int aannual, int adeductions) :
YearlyInc(aannual), Taxes(adeductions) {}

Salary() : YearlyInc(0), Taxes(0) {}

};
bool operator < (const Emp& first, const Emp& second) {
return first.Nickname < second.Nickname;

int main() {

map<Emp, Salary> employees;


Emp emp1("sparky", "123-22-8572");

Salary sal1(135000, 18);


employees[emp1] = sal1;

Emp emp2("buzz", "234-33-5784");


Salary sal2(150000, 23);
employees[emp2] = sal2;

// Now test it out!

Emp emptest("sparky", "");

cout << employees[emptest].YearlyInc << endl;

return 0;
}

When you run this application, you see the YearlyInc member of the Salary value,
where the key is an Employee with the name sparky:

135000

Now notice a couple things about this code. First, to locate the salary for Sparky,
you don’t need the Employee instance for Sparky.

Instead, you create an instance of Employee and set up the Nickname member without
worrying about the SSN member.

Then you retrieve the value by using the bracket notation for map:

cout << employees[emptest].YearlyInc << endl;

The map code uses the less-than function to perform this task. The function
compares only the Nickname members, not the SSN member.

You could change things around a bit by comparing the SSN


members like so:
bool operator < (const Emp& first, const Emp& second) {
return first.SSN < second.SSN;
}

Then you can locate Sparky’s salary based on the SSN:

Employee emptest("", "123-22-8572");


cout << employees[emptest].SSN << endl;

chapter unconventional containers 35

1. Unconventional Containers

Up until now, we've seen containers that are used to store groups of elements of the
same type.

The C++ standard defines some other types that can contain types but offer a different
set of functionalities from the containers we saw previously.

These types are as follows:

I. String
II. Pair and tuple
III. Optional
IV. Variant

I. Strings

A string is a data structure that's used to manipulate mutable sequences of contiguous


characters. The C++ string classes are STL containers. There exist several string
implementations in the standard library that are useful for different lengths of character sets,
such as string, wstring, u16string, and u32string.
The most commonly used type is std::string.

All types and functions for strings are defined in the <string> header file.

A string can be converted into a null-terminating string, which is an array of characters


that terminate with the special null character (represented with '\0') via the use of the
data() or c_str() functions. Null-terminating strings, also called C-strings, are the way
to represent sequences of character in the C language and they are often used when
the program needs to interoperate with a C library; they are represented with the const
char * type and are the type of the literal strings in our programs.

a. Exercise 12: Demonstrating Working Mechanism of the c_str() Function

Let's examine the following code to understand how the c_str() function works:

1. First include the required header files as illustrated:

#include <iostream>
#include <string>

2. Now, in the main function add a constant char variable named charString with
capacity as 8 characters:

int main() {

// Construct a C-string being explicit about the null terminator

const char charString[8] = {'C', '+', '+', ' ', '1', '0', '1', '\
0'};

// Construct a C-string from a literal string. The compiler


//automatically adds the \0 at the end

const char * literalString = "C++ Fundamentals";

// Strings can be constructed from literal strings.

std::string strString = literalString;

3. Use the c_str() function and assign the value of strString to charString2:

const char *charString2 = strString.c_str();

4. Print the charString and charString2 using the print function:

std::cout << charString << std::endl;


std::cout << charString2 << std::endl;

Output:

C++ 101
C++ Fundamentals

As for vectors, strings have size(), empty(), and capacity() member functions, but
there is an additional function called length(), which is just an alias for size().

Strings can be accessed in a character-by-character fashion using operator[] or the


at(), front(), and back() member functions:

std::string chapter = "We are learning about strings";


std::cout << "Length: " << chapter.length() << ", the second
character is "
<< chapter[1] << std::endl;

The usual comparison operators are provided for strings, thus simplifying the way two
string objects can be compared.

Since strings are like vectors, we can add and remove characters from them.
Strings can be made empty by assigning an empty string, by calling the clear(), or
erase() functions.

Let's look at the following code to understand the usage of the clear() and erase()
functions:

#include <iostream>
#include <string>

int main() {
std::string str = "C++ Fundamentals.";
std::cout << str << std::endl;
str.erase(5,10);
std::cout << "Erased: " << str << std::endl;
str.clear();
std::cout << "Cleared: " << str << std::endl;
}

Output:

C++ Fundamentals.
Erased: C++ Fs.

Cleared:

C++ also provides many convenience functions to convert a string into numeric values
or vice versa. For example, the stoi() and stod() functions (which stand for stringto-
int and string-to-double) are used to convert string to int and double, respectively.
Instead, to convert a value into a string, it is possible to use the overloaded function
to_string().

Let's demystify these functions using the following code:

#include <iostream>
#include <string>
using namespace std;

int main() {

std::string str = "55";


std::int strInt = std::stoi(str);
double strDou = std::stod(str);
std::string valToString = std::to_string(strInt);
std::cout << str << std::endl;
std::cout << strInt << std::endl;
std::cout << strDou << std::endl;
std::cout << valToString << std::endl;

Output:

55
55
55
55

II. Pairs and Tuples

The pair and tuple classes are similar to some extent, in the way they can store a
collection of heterogeneous elements.
The pair class can store the values of two types, while the tuple class extended this
concept to any length.
Pair is defined in the <utility> header, while tuple is in the <tuple> header.
The pair constructor takes two types as template parameters, used to specify the types
for the first and second values. Those elements are accessed directly using the first
and second data. Equivalently, these members can be accessed with the get<0>() and
get<1>() functions.

The make_pair() convenience function is used to create a value pair without explicitly
specifying the types:

std::pair<std::string, int> nameAndAge = std::make_pair("John", 32);


std::cout << "Name: " << nameAndAge.first << ", age: " <<
nameAndAge.second
<< std::endl;

The second line is equivalent to the following one:

std::cout << "Name: " << std::get<0>(nameAndAge) << ", age: " <<
std::get<1>(nameAndAge) << std::endl;

Pairs are used by unordered map, unordered multimap, map, and multimap
containers to manage their key/value elements.

Tuples are similar to pairs. The constructor allows you to provide a variable number
of template arguments. Elements are accessed with the get<N>() function only, which
returns the nth element inside the tuple, and there is a convenience function to create
them similar to that for pair, named make_tuple().
Additionally, tuples have another convenience function that's used to extract values
from them. The tie() function allows for the creation of a tuple of references, which is
useful in assigning selected elements from a tuple to specific variables.

III. std::optional

Let's understand how to use the make_tuple() and get() functions to retrieve data from
a tuple:

#include <iostream>
#include <tuple>
#include <string>

int main() {

std::tuple<std::string, int, float> james = std::make_tuple("James",


7,
1.90f);
std::cout << "Name: " << std::get<0>(james) << ". Agent number: " <<
std::get<1>(james) << ". Height: " << std::get<2>(james) <<
std::endl;

Output:

Name: James. Agent number: 7. Height: 1.9

std::optional

optional<T> is used to contain a value that might be present or not.


The class takes a template parameter, T, which represents the type that the
std::optional template class might contain. Value type means that the instance of the
class contains the value. Copying optional will create a new copy of the contained data.
At any point in the execution of the program, optional<T> either contains nothing, when
it's empty, or contains a value of type T.

Optional is defined in the <optional> header.

Let's imagine our application is using a class named User for managing registered users.
We would like to have a function that gets us the information of a user from their email:

User getUserByEmail(Email email);.

But what happens when a user is not registered? That is, when we can determine that
our system does not have the associated User instance?

Some would suggest throwing an exception. In C++, exceptions are used for exceptional
situations, ones that should almost never happen. A user not being registered on our
website is a perfectly normal situation.

In these situations, we can use the optional template class to represent the fact that
wemight not have the data:

std::optional<User> tryGetUserByEmail(Email email);

The optional template provides two easy methods to work with:

• has_value(): This returns true if optional is currently holding a value, and false if
the variant is empty.

• value(): This function returns the value currently held by optional, or throws an
exception if it's not present.
• Additionally, optional can be used as a condition in an if statement: it will evaluate
to true if it contains a value, or false otherwise.

Let's look at the following example to understand how the has_value() and value()
functions work:

#include <iostream>
#include <optional>

int main() {

// We might not know the hour. But if we know it, it's an integer

std::optional<int> currentHour;
if (not currentHour.has_value()) {
std::cout << "We don't know the time" << std::endl;

currentHour = 18;

if (currentHour) {

std::cout << "Current hour is: " << currentHour.value() <<


std::endl;

}
}

Output:

We don't know the time


Current hour is: 18

The optional template comes with additional convenience features. We can assign the
std::nullopt value to optional to make it explicit when we want it empty, and we can
use the make_optional value to create an optional from a value. Additionally, we can
use the dereference operator, *, to access the value of optional without throwing an
exception if the value is not present. In such cases, we will access invalid data, so we
need to be sure that optional contains a value when we use *:

std::optional<std::string> maybeUser = std::nullopt;

if (not maybeUser) {
std::cout << "The user is not present" << std::endl;

maybeUser = std::make_optional<std::string>("[email protected]");

if (maybeUser) {

std::cout << "The user is: " << *maybeUser << std::endl;

Another handy method is value_or(defaultValue). This function takes a default value


and returns the value contained by optional if it currently holds a value, otherwise it
returns the default value.

Let's explore the following example:

#include <iostream>
#include <optional>

int main(){

std::optional<int> x;
std::cout << x.value_or(10) << std::endl;

//Will return value of x as 10

x = 15;
std::cout << x.value_or(10)<< std::endl;

//Will return value of x as 15

Output:

10
15

In addition to return values, optional is useful when accepting it as an argument to


represent arguments that can be present or not.

Let's recall our User class that's composed of an email address, a phone number, and
a physical address. Sometimes, users don't have a phone number and don't want to
provide a physical address, so the only required field we have in User is the email
address:

User::User(Email email, std::optional<PhoneNumber> phoneNumber =


std::nullopt, std::optional<Address> address = std::nullopt){
...
}

This constructor allows us to pass in all the information we have on the user. If,
instead of using optional, we used multiple overloads, we would have had four
overloads:

1. Only email
2. Email and phone number
3. Email and address
4. Email with phone number and address
You can see that the number of overloads grows quickly when there are more
arguments that we might not want to pass.

IV. std::variant

variant is a value type that's used to represent a choice of types. The class takes a list of
types, and the variant will be able to contain one value of any of those types.
It is often referred to as tagged union, because similar to a union, it can store multiple
types, with only one present at a time. It also keeps track of which type is currently
stored.
During the execution of a program, variant will contain exactly one of the possible
types at a time.
Like optional, variant is a value type: when we create a copy of variant, the element
that is currently stored is copied into the new variant

To interact with std::variant, the C++ standard library gives us two main functions:

• holds_alternative<Type>(variant): It returns true if the variant


is currently holding the provided type, if not then false.

• get(variant): There are two versions: get<Type>(variant) and


get<Index>(variant).

get<Type>(variant) gets the value of the type that's currently stored inside
the variant. Before calling this function, the caller needs to be sure that holds_
alternative<Type>(variant) returns true.

get<Index>(variant) gets the value of the index type that's currently stored inside
variant. Like before, the caller needs to be sure that variant is holding the correct type.

For example, with std::variant<string, float> variant, calling get<0>(variant)


will give us the string value, but we need to be sure that variant is currently storing a string
at the moment.
Usually, it is preferable to access the elements with get<Type>() so that we are explicit on
the type that we expect and that if the order of the types in the variant changes, we will still
get the same result:

Exercise 13: Using Variant in the Program

Let's perform the following steps to understand how to use variant in the program:

1. Include the required header files:

#include <iostream>
#include <variant>

2. In the main function, add the variant with the value type as string and integer:

int main()
{
std::variant<std::string, int> variant = 42;

3. Now using the two print statements call the variant in different ways:

std::cout << get<1>(variant) << std::endl;


std::cout << get<int>(variant) << std::endl; }

Output:

42
42

An alternative way to get the content of variant is to use std::visit(visitor,


variant), which takes variant and a callable object. The callable objects need to support
an overload of operator(), taking a type for each of the possible types stored inside
variant. Then, visit will make sure to call the function that accepts the current type
that's stored inside variant:

Exercise 14: Visitor Variant

Let's perform the following steps to understand how to use std::visit(visitor, variant) in
the program:

1. Add the following header files at the start of the program:


#include <iostream>
#include <string>
#include <variant>

2. Now, add the struct Visitor as illustrated:

struct Visitor {

void operator()(const std::string& value) {

std::cout << "a string: " << value << std::endl;

void operator()(const int& value){

std::cout << "an int: " << value << std::endl; }

};

3. Now, in the main function, call the struct Visitor and pass values as illustrated:

int main() {

std::variant<std::string, int> variant = 42;


Visitor visitor;
std::cout << "The variant contains ";
std::visit(visitor, variant);
variant = std::string("Hello world");
std::cout << "The variant contains ";
std::visit(visitor, variant);

The output is as follows:

The variant contains an int: 42


The variant contains a string: Hello world

variant is incredibly valuable when we want to represent a set of values of different


types. Typical examples are as follows:

• A function returning different types depending on the current state of the


program

• A class that represents several states


Let's imagine our std::optional<User> tryGetUserByEmail() function, which we
described earlier.

.
Let's imagine we have struct UserRegistrationForm, which contains the information
that's needed to let the user register.

Our function can now return std::variant<User, UserRegistrationForm>


tryGetUserByEmail(). When the user is registered, we return User, but if the user is not
registered, we can return the registration form.

Additionally, what should we do when there is an error? With variant, we could have
struct GetUserError storing all the information we have so that our application will
be able to recover from the error and add it to the return type: std::variant<User,
UserRegistrationForm, GetUserError>, or tryGetUserByEmail().

getUserByEmail() by just looking at the function signature, and the compiler will help us
make sure that we handle all the cases.

Alternatively, variant can also be used to represent the various states in which a class
can be. Each state contains the data that's required for that state, and the class only
manages the transitions from one state to another.

Activity 22: Airport System Management

Let's write a program to create airport system management:

1. We want to represent the state of an airplane in an airport system. The airplane


can be in three states: at_gate, taxi, or flying. The three states store
different information.

2. With at_gate, the airplane stores the gate number at which it is. With taxi, we
store which lane the airplane is assigned and how many passengers are on board.
With flying, we store the speed:

struct AtGate {
int gate;
};

struct Taxi {
int lane;
int numPassengers;
};
struct Flying {
float speed;
};

3. The airplane should have three methods:

• startTaxi(): This method takes the lane the airplane should go on and the
number of passengers on board. The airplane can start taxi only if it is at the
gate.

• takeOff(): This method takes the speed at which the airplane should fly. The
airplane can start flying only if it is in the taxi state.

• currentStatus(): This method prints the current status of the airplane.

—---------------------------------------------------------

chapter Algorithms Provided by the C++ Standard Template Library 36

1. ALGORITHMS

Algorithms are a way to operate on containers in an abstract way.


Because algorithms accept iterators, they can operate on any container, even user defined
containers, as long as they provide iterators.
This allows us to have a large number of algorithms that work with a large number
of containers, without the need for the algorithm to know how the container is
implemented.
The following are some of the most important and common algorithms that are
provided by the STL.

Note

Algorithms operate on ranges, so they normally take a pair of iterators: first and
last.
2. Read Only Algorithms

Read-only algorithms are algorithms that inspect the elements stored inside a container
but do not modify the order of the elements of the container.
I. Checking for the Presence of Elements

1. bool all_of(InIt first, InIt last, UnaPred predicate)


2. bool none_of(InIt first, InIt last, UnaPred predicate)
3. bool any_of(InIt first, InIt last, UnaPred predicate)

Returns true if all, none, or respectively at least one of the elements in the range [first, last)
satisfies a unary predicate . If the range is empty, all_of() and none_of() return true ,
and any_of() returns false .

DiffType count(InIt first, InIt last, const T& value)


DiffType count_if(InIt first, InIt last, UnaPred predicate)

Returns the number of elements in [first, last] that are equal to a given value , or that satisfy
a unary predicate . [Alternatives: all ordered and unordered associative containers have a
count() member.]

II. Example :

III. Finding Elements


The following example demonstrates the use of all_of() to check whether all elements
are even:

IV. Let's see how we can use these functions:

#include <iostream>
#include <vector>
#include <algorithm>

int main()
{
std::vector<int> vector = {1, 2, 3, 4};
bool allLessThen10 = std::all_of(vector.begin(), vector.end(), []
(int value) { return value < 10; });

std::cout << "All are less than 10: " << allLessThen10 << std::endl;

bool someAreEven = std::any_of(vector.begin(), vector.end(), [](int


value) { return value % 2 == 0; });
std::cout << "Some are even: " << someAreEven << std::endl;
bool noneIsNegative = std::none_of(vector.begin(), vector.end(), []
(int value) { return value < 0; });

std::cout << "None is negative: " << noneIsNegative << std::endl;

end(), [](int value) { return value % 2 == 1; }) << std::endl;


auto position = std::find(vector.begin(), vector.end(), 6);
std::cout << "6 was found: " << (position != vector.end()) <<
std::endl;
}

Output:

All are less than 10: 1


Some are even: 1
None is negative: 1
Odd numbers: 2
6 was found: 0

3. Modifying Algorithms

Modifying algorithms are algorithms that modify the collections they iterate on:
I. Example:

The following example uses transform() to double all the elements in a vector using a
lambda expression, then uses transform() to negate the elements using a standard
function object, and finally outputs all the elements to the console using for_each() . This
code snippet additionally needs :
II. Copy, Move, Swap
III. Removing and Replacing
Let's see these algorithms in action:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

int main()
{
std::vector<std::string> vector = {"Hello", "C++", "Morning",
"Learning"};
std::vector<std::string> longWords;
std::copy_if(vector.begin(), vector.end(),
std::back_inserter(longWords),
[](const std::string& s) { return s.length() > 3; });
std::cout << "Number of longWords: " << longWords.size() <<
std::endl;
std::vector<int> lengths;
std::transform(longWords.begin(), longWords.end(), std::back_
inserter(lengths), [](const std::string& s) { return s.length(); });

std::cout << "Lengths: ";


std::for_each(lengths.begin(), lengths.end(), [](int length)
{ std::cout
<< length << " "; });
std::cout << std::endl;

auto newLast = std::remove_if(lengths.begin(), lengths.end(), [](int


length) { return length < 7; });
std::cout << "No element removed yet: " << lengths.size() <<
std::endl;

// erase all the elements between the two iterators


lengths.erase(newLast, lengths.end());
std::cout << "Elements are removed now. Content: ";
std::for_each(lengths.begin(), lengths.end(), [](int length)
{ std::cout
<< length << " "; });
std::cout << std::endl;
}

Output:

Number of longWords: 3

Lengths: 5 7 8

No element removed yet: 3

Elements are removed now. Content: 7 8

3. Mutating Algorithms
Mutating algorithms are algorithms that change the order of elements:
Let's see how we can use them:

#include <iostream>
#include <random>
#include <vector>
#include <algorithm>
#include <iterator>

int main() {

std::vector<int> vector = {1, 2, 3, 4, 5, 6};


std::random_device randomDevice;
std::mt19937 randomNumberGenerator(randomDevice());
std::shuffle(vector.begin(), vector.end(), randomNumberGenerator);
std::cout << "Values: ";
std::for_each(vector.begin(), vector.end(), [](int value)
{ std::cout <<
value << " "; });

std::cout << std::endl;

Output:

Values: 5 2 6 4 3 1

4. Sorting Algorithms

This class of algorithms rearranges the order of elements within a container in a


specific order:
Here is how to sort a vector:

#include <iostream>
#include <vector>
#include <algorithm>

int main(){

std::vector<int> vector = {5, 2, 6, 4, 3, 1};

std::sort(vector.begin(), vector.end());

std::cout << "Values: ";

std::for_each(vector.begin(), vector.end(), [](int value)


{ std::cout << value << " "; });
std::cout << std::endl; }

Output:

Values: 1 2 3 4 5 6

a. Binary Search Algorithms

The following table explains the use of binary_search:


int main(){

std::vector<int> vector = {1, 2, 3, 4, 5, 6};


bool found = std::binary_search(vector.begin(), vector.end(), 2);
std::cout << "Found: " << found << std::endl;

Output:

Found: 1

5. Numeric Algorithms
This class of algorithms combines numeric elements using a linear operation in
different ways:
Let's see how we can use accumulate in the following program:

#include <iostream>
#include <vector>
#include <algorithm>

int main(){

std::vector<int> costs = {1, 2, 3};


int budget = 10;

int margin = std::accumulate(costs.begin(), costs.end(), budget, []


(int a, int b) { return a - b; });

std::cout << "Margin: " << margin << std::endl;


}

Output:

Margin: 4

Exercise 19: Customer Analytics

We have the information of many customers of our application and we want to compute
analytics data on that.
Given a map that has a username as a key and a user account as a value, we would like
to print the balances of the new users in descending order.
A user is considered new if they registered no more than 15 days ago. The struct
representing the user's account is provided and is as follows:
struct UserAccount {

int balance;
int daysSinceRegistered;

};

Write the void computeAnalytics (std::map<std::string, UserAccount>& accounts)


function, which prints the desired balances.

1. Make sure to include all the required headers for the solution:

#include <iostream>
#include <vector>
#include <iterator>
#include <map>
#include <algorithm>

2. First, we need to extract UserAccount from the map. Remember that the element
map stores a pair containing a key and value. Since we need to transform the
type into UserAccount, we can use std::transform, by passing a lambda that only
returns the user account from the pair. To insert this into vector, we can use
std::back_inserter. Make sure to use a const reference when accepting pair in
the lambda that's passed to transform:

void computeAnalytics(std::map<std::string, UserAccount>& accounts)


{

// Balance of accounts newer than 15 days, in descending order

std::vector<UserAccount> newAccounts;

std::transform(accounts.begin(), accounts.end(),
std::back_inserter(newAccounts), [](const std::pair<std::string,
UserAccount>& user) {

return user.second; });

3. After we have extracted the accounts in vector, we can use remove_if to remove all
accounts that are older than 15 days:

auto newEnd = std::remove_if(newAccounts.begin(), newAccounts.end(),


[](const UserAccount& account) { return account.daysSinceRegistered
> 15; } );
newAccounts.erase(newEnd, newAccounts.end());

4. After removing the old accounts, we need to sort the balances in descending
order. By default, std::sort uses an ascending order, so we need to provide a
lambda to change the order:

std::sort (newAccounts.begin(), newAccounts.end(), [](const


UserAccount& lhs, const UserAccount& rhs) { return lhs.balance >
rhs.balance; } );

Now that the data is sorted, we can print it:

for(const UserAccount& account : newAccounts) {

std::cout << account.balance << std::endl;

}
}

5. We can now invoke our function with the following test data:

int main() {

std::map<std::string, UserAccount> users = {


{"Alice", UserAccount{500, 15}},
{"Bob", UserAccount{1000, 50}},
{"Charlie", UserAccount{600, 17}},
{"Donald", UserAccount{1500, 4}}

};

computeAnalytics(users);

We divided the most common algorithms into various categories, and we looked at the
most important algorithms in those categories, including find, remove, and sort.
In the next chapter, you will learn how to use the advanced features of C++ to create
dynamic programs.
chapter

STREAMS CRASH COURSE

1. Streams

A stream models a stream of data. In a stream, data flows between objects,


and those objects can perform arbitrary processing on the data. When
you’re working with streams, output is data goino in the stream and input
is data coming out of the stream. These terms reflect the streams as viewed
from the user’s perspective.

output (I/O). Regardless of the source or destination, you can use streams
as the common language to connect inputs to outputs. The STL uses class
inheritance to encode the relationships between various stream types.

I. The primary types in this hierarchy are:

• The std::basic_ostream class template in the <ostream> header that represents


an output device.

• The std::basic_istream class template in the <istream> header that represents


an input device.

• The std::basic_iostream class template in the <iostream> header for


devices that are input and output

All three stream types require two template parameters. The first corresponds
to the stream’s underlying data type and the second to a traits type.

This section covers streams from a user’s perspective rather than from
a library implementer’s perspective.

II. Stream Classes

All STL stream classes that users interact with derive from basic_istream,
basic_ostream, or both via basic_iostream. The headers that declare each type
also provide char and wchar_t specializations for those templates, as outlined
in Table 16-1. These heavily used specializations are particularly useful when
you’re working with human-language data input and output.
The objects in Table 16-1 are abstractions that you can use in your
programs to write generic code. Do you want to write a function that logs
output to an arbitrary source? If so, you can accept an ostream reference
parameter and not deal with all the nasty implementation details.

Often, you’ll want to perform I/O with the user (or the program’s
environment). Global stream objects provide a convenient, stream-based
wrapper for you to work against.

III. Global Stream Objects

The STL provides several global stream objects in the <iostream> header that
wrap the input, output, and error streams stdin, stdout, and stderr. These
implementation-defined standard streams are preconnected channels between your program
and its executing environment. For example, in a desktop environment, stdin typically
binds to the keyboard and stdout and stderr bind to the console.

Table 16-2 lists the global stream objects, all of which reside in the std
namespace.

So how do you use these objects? Well, stream classes support operations
that you can partition into two categories:
a) Formatted operations Might perform some preprocessing on
their input parameters before performing I/O

b) Unformatted operations Perform I/O directly

The following sections explain each of these categories in turn.

a) Formatted Operations

All formatted I/O passes through two functions: the standard stream operators,
operator<< and operator>>.

Output streams overload operator<<, which is referred to as the output


operator or the inserter.

The basic_ostream class template overloads the output


operator for all fundamental types (except void and nullptr_t) and some
STL containers, such as basic_string, complex, and bitset.

Listing 16-1 illustrates how to use the output operator to write various
types into cout.

#include <iostream>
#include <string>
#include <bitset>

using namespace std;

int main() {

bitset<8> s{ "01110011" };
string str("Crying zeros and I'm hearing ");
size_t num{ 111 };
cout << s;
cout << '\n';
cout << str;
cout << num;
cout << "s\n";

Output :

01110011
Crying zeros and I'm hearing 111s

One very nice feature of the standard stream operators is that they generally
return a reference to the stream. Conceptually, overloads are typically
defined along the following lines:

ostream& operator<<(ostream&, char);

This means you can chain output operators together. Using this
technique, you can refactor Listing 16-1 so cout appears only once, as
Listing 16-2 illustrates.

#include <iostream>
#include <string>
#include <bitset>

using namespace std;

int main() {
bitset<8> s{ "01110011" };
string str("Crying zeros and I'm hearing ");
size_t num{ 111 };
cout << s << '\n' << str << num << "s\n"; u
}

01110011
Crying zeros and I'm hearing 111

Because each invocation of operator<< returns a reference to the output


stream (here, cout), you simply chain the calls together to obtain identical
output.

Input streams overload operator>>, which is referred to as the input


operator or the extractor. The basic_istream class has corresponding overloads
for the input operator for all the same types as basic_ostream, and again as
a user, you can largely ignore the deserialization details.

Listing 16-3 illustrates how to use the input operator to read two double
objects and a string from cin, then print the implied mathematical operation’s
result to stdout.

#include <iostream>
#include <string>
using namespace std;
int main() {

double x, y;
cout << "X: ";
cin >> x; u
cout << "Y: ";
cin >> y; v
string op;
cout << "Operation: ";
cin >> op; w

if (op == "+") {
cout << x + y; x
} else if (op == "-") {
cout << x - y; y
} else if (op == "*") {
cout << x * y; z
} else if (op == "/") {
cout << x / y; {
} else {
cout << "Unknown operation " << op; |
}
}

Here, you collect two doubles and followed by the string op ,


which encodes the desired operation. Using an if statement, you can output
the specified operation’s result for addition x, subtraction y, multiplication z,
and division or indicate to the user that op is unknown .

To use the program, you type the requested values into the console
when directed. A newline will send the input (as stdin) to cin, as Listing 16-4
illustrates.

X: 3959
Y: 6.283185
Operation: *
24875.1

You input the two double objects:

the radius of Earth in miles, 3959


and 2π, 6.283185 , and you specify multiplication * .

The result is Earth’s circumference in miles x. Note that you don’t need to provide a decimal
point for an integral value ; the stream is smart enough to know that
there’s an implicit decimal.
You might wonder what happens in Listing 16-4 if you input a non-numeric string
for X or Y . In an error state, the stream ceases to accept input, and the program won’t
accept any more input.

b. Unformatted Operations

When you’re working with text-based streams, you’ll usually want to use
formatted operators; however, if you’re working with binary data or if you’re
writing code that needs low-level access to streams, you’ll want to know
about the unformatted operations.

The istream class has many unformatted input methods. These methods
manipulate streams at the byte level and are summarized in Table 16-3. In
this table, is is of type std::istream <T>, s is a char*, n is a stream size, pos is a
position type, and d is a delimiter of type T.

Output streams have corollary unformatted write operations, which


manipulate streams at a very low level, as summarized in Table 16-4. In this
table, os is of type std::ostream <T>, s is a char*, and n is a stream size.
c. Special Formatting for Fundamental Types

All fundamental types, in addition to void and nullptr, have input and output
operator overloads, but some have special rules:

char and wchar_t The input operator skips whitespace when assigning
character types.

char* and wchar_t* The input operator first skips whitespace and then
reads the string until it encounters another whitespace or an end-of-file
(EOF). You must reserve enough space for the input.

Void* Address formats are implementation dependent for input and


output operators.
On desktop systems, addresses take hexadecimal literalform, such as 0x01234567 for 32-
bit or 0x0123456789abcdef for 64-bit.

Bool The input and output operators treat Boolean values as numbers:
1 for true and 0 for false.

Numeric types The input operator requires that input begin with at
least one digit. Badly formed input numbers yield a zero-valued result.
These rules might seem a bit strange at first, but they’re fairly straightforward
once you get used to them.

Avoid reading into C-style strings, because it’s up to you to ensure that you’ve allocated
enough space for the input data. Failure to perform adequate checking results
in undefined behavior and possibly major security vulnerabilities. Use std::string
instead.

V. Stream State

A stream’s state indicates whether I/O failed. Each stream type exposes the
constant static members referred to collectively as its bits, which indicate
a possible stream state: goodbit, badbit, eofbit, and failbit.
To determine whether a stream is in a particular state, you invoke member functions that
return a bool indicating whether the stream is in the corresponding state.

Table 16-5 lists these member functions, the stream state corresponding to
a true result, and the state’s meaning.

To reset a stream’s status to indicate a good working state, you can invoke its clear()
method.

Streams implement an implicit bool conversion (operator bool), so you


can check whether a stream is in a good working state simply and directly.
For example, you can read input from stdin word by word until it encounters
an EOF (or some other failure condition) using a simple while loop.

Listing 16-5 illustrates a simple program that uses this technique to generate
word counts from stdin

#include <iostream>
#include <string>

int main() {

std::string word;
size_t count{};
while (std::cin >> word)
count++;
std::cout << "Discovered " << count << " words.\n";

Chapter Manipulators

1. Manipulators
Manipulators are special objects that modify how streams interpret input or
format output. Manipulators exist to perform many kinds of stream alterations.
For example, std::ws modifies an istream to skip over whitespace. Here
are some other manipulators that work on ostreams:

• std::flush empties any buffered output directly to an ostream.

• std::ends sends a null byte.

• std::endl is like std::flush except it sends a newline before flushing.

Table 16-6 summarizes the manipulators in the <istream> and <ostream>

For example, you could replace x in Listing 16-7 with the following:

cout << "Discovered " << count << " words." << endl;

This will print a newline and also flush output.

NOTE

As a general rule, use std::endl when your program has finished outputting text
to the stream for a while and \n when you know your program will output more
text soon.

It can, for example, determine whether an ostream will represent Boolean


values textually (boolalpha) or numerically (noboolalpha); integral values as
octal (oct), decimal (dec), or hexadecimal (hex); and floating-point numbers
as decimal notation (fixed) or scientific notation (scientific). Simply
pass one of these manipulators to an ostream using operator<< and all subsequent
insertions of the corresponding type will be manipulated (not just an
immediately preceding operand).

You can also set a stream’s width parameter using the setw manipulator.
A stream’s width parameter has varied effects, depending on the stream.
For example, with std::cout, setw will fix the number of output characters
allocated to the next output object. Additionally, for floating-point output,
setprecision will set the following numbers’ precision.

Listing 16-8 illustrates how these manipulators perform functions similar


to those of the various printf format specifiers.

#include <iostream>
#include <iomanip>
using namespace std;

int main() {

cout << "Gotham needs its " << boolalpha << true << " hero.";
cout << "\nMark it " << noboolalpha << false << "!";
cout << "\nThere are " << 69 << "," << oct << 105 << " leaves in
here."; w
cout << "\nYabba " << hex << 3669732608 << "!";
cout << "\nAvogadro's number: " << scientific << 6.0221415e-23;
cout << "\nthe Hogwarts platform: " << fixed << setprecision(2) <<
9.750123;

cout << "\nAlways eliminate " << 3735929054; {


cout << setw(4) << "\n"
<< 0x1 << "\n"
<< 0x10 << "\n"
<< 0x100 << "\n"
<< 0x1000 << endl;
}

Output :

Gotham needs its true hero.


Mark it 0!
There are 69,151 leaves in here.
Yabba dabbad00!
Avogadro's Number: 6.022142e-23
the Hogwarts platform: 9.75
Output :

Always eliminate deadc0de


1
10
100
1000

2. String Streams Supporting Input and Output

Additionally, if you want a string stream that supports input and output
operations, you can use the basic_stringstream, which has the following
specializations:

using stringstream = basic_stringstream<char>;

using wstringstream = basic_stringstream<wchar_t>;


3. File Streams

The file stream classes provide facilities for reading from and writing to
character sequences. The file stream class structure follows that of the
string stream classes. File stream class templates are available for input,
output, and both.

Opening Files with Streams

You have two options for opening a file with any file stream. The first option
is the open method, which accepts a const char* filename and an optional
std::ios_base::openmode bitmask argument. The openmode argument can be
one of the many possible combinations of values listed in Table 16-9.
a. Put the file in binary mode.

In binary mode, the stream won’t convert special character sequences, like end of line (for
example, a carriage return plus a line feed on Windows) or EOF.

The second option for specifying a file to open is to use the stream’s
constructor. Each file stream provides a constructor taking the same arguments
as the open method. All file stream classes are RAII wrappers around
the file handles they own, so the files will be automatically cleaned up
when the file stream destructs. You can also manually invoke the close
method, which takes no arguments. You might want to do this if you know
you’re done with the file but your code is written in such a way that the file
stream class object won’t destruct for a while.

File streams also have default constructors, which don’t open any files.
To check whether a file is open, invoke the is_open method, which takes no
arguments and returns a Boolean.

1. Output File Streams

Output file streams provide output stream semantics for character sequences,
and they all derive from the class template std::basic_ofstream in the <fstream>
header, which provides the following specializations:

using ofstream = basic_ofstream<char>;


using wofstream = basic_ofstream<wchar_t>;

The default basic_ofstream constructor doesn’t open a file, and the nondefault
constructor’s second optional argument defaults to ios::out.
Whenever you send input to the file stream, the stream writes the data
to the corresponding file. Listing 16-14 illustrates how to use ofstream to
write a simple message to a text file.

#include <fstream>

using namespace std;

int main() {

ofstream file{ "lunchtime.txt", ios::out|ios::app };

file << "Time is an illusion." << endl;

file << "Lunch time, " << 2 << "x so." << endl;

lunchtime.txt:

Time is an illusion.
Lunch time, 2x so.

flags out and app

Because this combination of flags appends output, any data you send through the output
operator into this file stream gets appended to the end of the file. As expected, the file
contains the message you passed to the output operator .

Thanks to the ios::app flag, the program will append output to lunchtime.txt if it
exists. For example, if you run the program again, you’ll get the following:

output:

Time is an illusion.
Lunch time, 2x so.
Time is an illusion.
Lunch time, 2x so.

The second iteration of the program added the same phrase to the end
of the file.

Input File Streams


Input file streams provide input stream semantics for character sequences, and
they all derive from the class template std::basic_ifstream in the <fstream>
header, which provides the following specializations:

using ifstream = basic_ifstream<char>;


using wifstream = basic_ifstream<wchar_t>;

The default basic_ifstream constructor doesn’t open a file, and the nondefault
constructor’s second optional argument defaults to ios::in.

Whenever you read from the file stream, the stream reads data from
the corresponding file. Consider the following sample file, numbers.txt:

-54
203
9000
0
99
-789
400

Listing 16-15 contains a program that uses an ifstream to read from a text
file containing integers and return the maximum. The output corresponds
with invoking the program and passing the path of the file numbers.txt.

#include <iostream>
#include <fstream>
#include <limits>

using namespace std;

int main() {

ifstream file { "numbers.txt" };

auto maximum = numeric_limits<int>::min();

int value;

while (file >> value)

maximum = maximum < value ? value : maximum;

cout << "Maximum found was " << maximum << endl; }

output :
Maximum found was 9000

You first initialize an istream to open the numbers.txt text file . Next,
you initialize the maximum variable with the minimum value an int can
take .

Using the idiomatic input stream and while-loop combination ,you cycle through each integer
in the file, updating the maximum as you find higher values x. Once the file stream cannot
parse any more integers, you print the result to stdout y.

Handling Failure

As with other streams, file streams fail silently. If you use a file stream constructor
to open a file, you must check the is_open method to determine
whether the stream successfully opened the file. This design differs from
most other stdlib objects where invariants are enforced by exceptions. It’s
hard to say why the library implementors chose this approach, but the fact
is that you can opt into an exception-based approach fairly easily.

You can make your own factory functions to handle file-opening failures
with exceptions. Listing 16-16 illustrates how to implement an ifstream factory
called open.

#include <fstream>
#include <string>
using namespace std;

ifstream open(const char* pathv, ios_base::openmode mode =


ios_base::inw) {

ifstream file{ path, mode };

if(!file.is_open()) {

string err{ "Unable to open file " };

err.append(path);

throw runtime_error{ err }; }

file.exceptions(ifstream::badbit);

return file; {

}
Your factory function returns an ifstream and accepts the same arguments
as a file stream’s constructor (and open method): a file path and an
openmode . You pass these two arguments into the constructor of ifstream
and then determine whether the file opened successfully y. If it didn’t, you
throw a runtime_error . If it did, you tell the resulting ifstream to throw an
exception whenever its badbit gets set in the future {.
}

Referencias

1. C programming second edition for dummies, Dan Booking, 2021, wiley


2. Learn to program with C Noel Kalicharan ,2015 , apress
3. head first C, david griffith and dawn griffith , 2012, o’reilly
4. geekforgeeks c++ tutorial
5. geek for geeks c tutorial
6. c++ for dummies All-in-one 4th edition ,John Paul Mueller 2021,wiley
7. c++ crash course a fast paced introduction , josh lospinoso ,2019, no starch press
8. c++ fundamentals ,Antonio Mallia and Francesco Zoffoli,2019, packt
9. A complete guide to programming in c++,Ulla Kirch-Prinz -Peter Prinz

You might also like