0% found this document useful (0 votes)
18 views80 pages

C and C++ Programming Guide

Systems Programming in Linux With C and C++

Uploaded by

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

C and C++ Programming Guide

Systems Programming in Linux With C and C++

Uploaded by

Adolfo Alvarez
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Contents

Programming in C and C++ 3


Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Audience . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

C programming 7
Basic Hello World program . . . . . . . . . . . . . . . . . . . . . . . . 8
Basic Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
sizeof operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Format Specifier . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
const keyword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
type definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
typecasting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Scope and Lifetime of the variables . . . . . . . . . . . . . . . . . . . . 22
Control statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Goto statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Function returning local data . . . . . . . . . . . . . . . . . . . . 65
Variadic functions . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Function like macros . . . . . . . . . . . . . . . . . . . . . . . . . 77
inline functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
String manipulation operations . . . . . . . . . . . . . . . . . . . 80
Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Pass by value and Pass by reference in functions . . . . . . . . . 96
Dynamic Memory Allocation . . . . . . . . . . . . . . . . . . . . . . . 102
Double pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
Recap about variables and scope . . . . . . . . . . . . . . . . . . 120
Function Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Bit fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Structure padding and packing . . . . . . . . . . . . . . . . . . . 141
Enumeration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
Unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
Appendix A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
Significance of header files . . . . . . . . . . . . . . . . . . . . . . 149
Header description . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Compilation of C program . . . . . . . . . . . . . . . . . . . . . . 151
GCC compilation options . . . . . . . . . . . . . . . . . . . . . . 152
Valgrind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
Command line arguments (argc, argv) . . . . . . . . . . . . . . . 154

1
File I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
Operating with the binary files . . . . . . . . . . . . . . . . . . . 163
I/O operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
Useful macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
Useful helper functions . . . . . . . . . . . . . . . . . . . . . . . . 173

C++ programming 174


Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
cout, cerr and cin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
New operators in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
New keywords in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
Typecasting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
Constructors and Destructors . . . . . . . . . . . . . . . . . . . . 194
namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
Operator Overloading . . . . . . . . . . . . . . . . . . . . . . . . 200
Function Overloading . . . . . . . . . . . . . . . . . . . . . . . . 218
Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . 219
noexcept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
Standard library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
std::pair . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
std::initializer_list . . . . . . . . . . . . . . . . . . . . . . . . . . 226
std::bitset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
File streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
Queues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
Dequeue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
shared_ptr, unique_ptr . . . . . . . . . . . . . . . . . . . . . . . 250
File systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
Mutexes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
Conditional Variables . . . . . . . . . . . . . . . . . . . . . . . . 259
Derived Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
Abstract Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
Template overloading . . . . . . . . . . . . . . . . . . . . . . . . . 269
Appendix B . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
Use cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
Design Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
Factory Design pattern . . . . . . . . . . . . . . . . . . . . . . . . 292

2
Loops
Loop statements are another programming construct that allow executing some-
thing for a certain number of times.
1. While loop
The while loop allows to loop over a certain condition until it fails. An example
of the while is as follows.
while (condition) {
// statements
}
Example.22 While condition
An example use of while loop is as follows.
#include <stdio.h>

int main()
{
int i = 0;

while (i < 10) {


printf("i %d\n", i);
i ++;
}

return 0;
}
Example.23 While loop example
Sometimes it doesn’t have to be a condition, a variable should be enough.
The following statements are valid in this regard.
bool is_set = true;
while (is_set) { } // valid

int i = 1;
while (i) { } // valid
In the above program the loop repeats until i reaches 10. Upon reaching 10, the
while condition fails breaking the loop.
The break statement can be used in the while loop as well.
int main()
{
int i = 0;

39
while (1) {
if (i >= 10) {
break;
}
printf("%d\n", i);
i ++;
}

return 0;
}
Example.24 while loop and break statement
Above program shows the use of while (1). Generally this means that the
condition in the while loop is never false. It is an infinite loop.
Generally infinite loops are not preferable in programming without any condi-
tional checks in the while statement.
The infinite loops generally do nothing but increase in CPU load on the process
the program runs and consumes the CPU cycles unnecessarily. However, some
programs written for the operating systems do need to run infinitely (such as
graphics, display, editors etc). To do this, operating systems employ certain
event based mechanisms supported by the hardware. This ensures that the
program executes only based on certain events.
Sometimes, infinite loops are required but with OS supported waiting mechanism
that does not cause much of a CPU load and provides better resource utilization
between other processes.

40
2. For loop
The for loop is similar to the while loop. The syntax is as follows,
for (initialization; condition; increment / decrement operation)
Below is an example of the use of for loop.
#include <stdio.h>

int main()
{
int i;

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


printf("i %d\n", i);
}

return 0;
}
Example.25 For loop
the i = 0 statement in for executes only once. The i < 10 statement exe-
cutes everytime the loop repeats. The i ++ statement executes everytime the
statements in the for loop executes.
Another way to do is the following:
#include <stdio.h>

int main()
{
int i = 0;

for (;i < 10; i ++) {


printf("i %d\n", i);
}

return 0;
}
Example.26 For iteration
The initializer statement can be left aside.
The above while (1) can be re-written with for as follows.
#include <stdio.h>

int main()
{

41
int i = 0;

for (;;) {
if (i >= 10) {
break;
}
printf("i %d\n", i);
i ++;
}

return 0;
}
Example.27 Infinite for loop
The for(;;) is also an infinite for loop. As mentioned, the infinite loops must
be used with caution.
To stop an infinite loop program, press Ctrl + C key combination on the terminal
within Linux.

42
3. do while loop
The do..while loop is similar to the while. The statement within the do..while
executes first and the condition is checked for validity. Below is an example.
#include <stdio.h>

int main()
{
int i = 0;

do {
printf("Hello World\n");
} while (i != 0);

return 0;
}
Example.28 do..while loop
Once run, it prints Hello World. This means that the statements execute and
the checks happen later.

43
Goto statement
The statement goto is similar to a jump instruction in assembly. The above
loop can be rewritten with goto as follows.
#include <stdio.h>

int main()
{
int i = 0;

begin:
if (i < 10) {
printf("i %d\n", i);
i ++;
goto begin;
}

return 0;
}
Example.29 goto loop
We do not use goto in most of the programs for the following reasons:
1. Readability reduces with many gotos with in a function or within a C file.
2. Incorrectly written gotos can cause loops in program.
Gotos are not bad when used correctly in a program. For example in usecases
when certain conditions fail during a program initialization, the deinitialization
sequence must do the opposite. In such cases a jump required on the failure
case.
Here’s a pseudo code example,
int init_1()
{
...
return 0;
}

int init_2()
{
...
return 0;
}

void deinit_1()
{

44
...
}

int init_main()
{
int ret;

ret = init_1();
if (ret != 0) {
return -1;
}

ret = init_2();
if (ret != 0) {
goto deinit;
}

deinit:
deinit_1();
return -1;
}
Example.30 goto usecase
More about functions in the functions section.
In areas such as Automotive and Aerospace software application, goto statement
is seldom used. It is treated as a bad practise. So avoiding this is a good step
when writing software for such applications.

45
Arrays

46
1. One Dimensional Arrays
One dimensional array are the base type in arrays.
An array of integers is defined as,
int a[10];
Above statement defines an array a of 10 integers. Each element in the array is
an element of type integer.
Array indexes start from 0. Each item in the array is indexed with regular
numbers ranging from 0 to 9.
Maximum elements in the above array are 10 but the last index of the 10th
element is 9 (since indexing starts at 0), not 10. Accessing the array beyond
its maximum range is also called out of bounds access. Out of bounds accesses
are major security problem as the element is accessing an address beyond the
allocated range.
int a[];
is invalid because array without number of elements defined is invalid syntax.
However,
int a[] = {1, 2, 3, 4};
is still valid and compiler assumes that the array contains 4 elements.
Below program assigns the elements in the array.
#include <stdio.h>

int main()
{
int a[10];
int i = 0;

for (i = 0; i < sizeof(a) / sizeof(a[0]); i ++) {


a[i] = i;
}

printf("array elements:\n");
for (i = 0; i < sizeof(a) / sizeof(a[0]); i ++) {
printf("\ta[%d] = %d\n", i, a[i]);
}
printf("\n");

return 0;
}
Example.31 array iteration

47
The size of an array is calculated the same way.
#include <stdio.h>

int main()
{
int a[10];

printf("size of array %lu\n", sizeof(a));

return 0;
}
Example.32 sizeof array
With the sizeof, one can also find out the number of elements in the array as
follows.
#include <stdio.h>

int main()
{
int a[10];

printf("number of elements %d\n", sizeof(a) / sizeof(a[0]));

return 0;
}
Example.33 number of elements in an array
Initializing array elements
The below statement generally initializes the array.
int a[10] = {0};
However, this initializes the first element to 0. Since only one element is initialized
then by default all elements are initialized to 0.
So if we have initialized it,
int a[10] = {10};
the first element of the array is initialized to 10 and the rest of the elements are
initialized as 0s. Below is one example:
#include <stdio.h>

int main()
{
int a[10] = {10};

48
int i;

for (i = 0; i < sizeof(a) / sizeof(a[0]); i ++) {


printf("a[%d] = %d\n", i, a[i]);
}

return 0;
}
Example.34 Initializing array
This example prints the first element as 10 and rest as 0.
General way sometimes tend to be the use of memset which is discussed in below
sections. But the below example shows how to initialize an array.
int a[10];

memset(a, 0, sizeof(a));
Sets all the elements of the array a to 0.
Another way to set array elements is as follows:
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int i;

for (i = 0; i < sizeof(a) / sizeof(a[0]); i ++) {


printf("a[%d] = %d\n", i, a[i]);
}
Example.35 iterating array
But this means that all array elements must be initialized which is impractical
for a large set of arrays.
copying array elements
Copying one array to another is simple as iterating over each element and copying
one element to another.
Below is one example,
#include <stdio.h>

int main()
{
int a1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int a2[10];
int i;

for (i = 0; i < sizeof(a1) / sizeof(a1[0]); i ++) {

49
if (err && (err[0] != '\0')) {
return -1;
}

printf("val %f\n", val);

return 0;
}
4. strtoul
Converts string to unsigned long.
Below is the prototype.
unsigned long strtoul(const char *nptr, char **err_ptr, int base);
Below is one example:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

int main()
{
char *str = "4294967295";
uint32_t val;
char *err = NULL;

val = strtoul(str, &err, 10);


if (err && (err[0] != '\0')) {
return -1;
}

printf("val %u\n", val);

return 0;
}

94
Pointers
Strings can also be initialized with a pointer.
The below statement is a string that is allocated at compile time and the str is
a pointer to the beginning of the string “Hello”.
char *str = "Hello";
A pointer of any type is possible.
int *p;
declares an integer pointer.
A pointer can be assigned NULL stating that it points to no address. This NULL
is different from the null terminating character \0.
The null terminating character can be applied only to the strings. While the
NULL pointer is applied to the pointers.
In Standard library, the definition of the NULL is a macro. Something like the
following:
#define NULL (void *)0
This is more generically, a value 0 type casted to a void pointer.
The NULL pointer is used to inform that the pointer points to nothing. Also
deferencing the NULL pointer results in a abrupt program stop or a segmentation
fault.
The below statement,
int val = 4;
int *v = &val;
declares an integer val and a pointer v holding the address of the variable val.
The & denotes the address when placed before the variable.
Pointers can be printed with %p format specifier.
#include <stdio.h>

int main()
{
int val = 4;
int *v = &val;

printf("%d %p\n", val, v);


}
A size of a pointer can be evaluated as following.

95
int *v;
int size = sizeof(v);
On a 64-bit machine, the size results in 8 bytes.

Pass by value and Pass by reference in functions


Consider the below example,
void add(int a, int b, int r)
{
r = a+ b;
}

int main()
{
int a = 3;
int b = 3;
int r = 0;

add(a, b, r);

printf("r %d\n", r);


}
Here the function add takes a, b and r as inputs. The add function perform the
addition operation and writes the result in r.
Once the function executes and returns, the value of r is still 0. This is because
the variable r when passed is local to the function add. So the result value of r
in function add is not passed back.
One way to pass back the value is to return it. For example,
int add(int a, int b)
{
return a + b;
}
And then capture the return value in the caller.
This method of passing arguments is generally called as Pass by value. In this
approach, the value of the passed arguments do not change in the caller.
There is another approach to do this by using pointers. Refer to the pointers
section on using pointers.
void add(int a, int b, int *r)
{
*r = a + b;
}

96
This method is called as Pass by Reference. The r variable above is passed as a
pointer. So in the caller we need to pass the address of the variable.
...
add(a, b, &r);
...
Here we passed the address of the variable r so the actual address that the add
function is writing is in the original address of r.
This is particularly useful when functions want to change some information
about the variables that they take as inputs instead of returning.
The void pointer
The void pointer is a generic pointer that can be assigned as an address to any
structure, pointer or a variable. Below is one example:
int a[10];
void *p;

p = &a[0];
The void pointer cannot be dereferenced because dereferencing involve deducing
the type it points, since its void the compiler wouldn’t know which type it has to
decode. So a typecast is required or in some cases assignment back to its type.
To typecast back to the type generally, the typecast need to be used.
void *p;
int *a;

a = (int *)p;
printf("val %d\n", *a);
Typecasting can be used for the structure pointer as well.
struct S {
int p;
};

struct S s = {
.p = 3,
};

void *p = &s;

struct S *r = (struct S *)p; // typecast back to struct S


The below implementation of program results in compiler error because of the
de-reference of void pointer.

97
#include <stdio.h>

int main()
{
int p = 3;
void *a;

a = &p;

printf("%d\n", *a); // compiler error.. de-referencing void *

return 0;
}
Another way to do is the following.
#include <stdio.h>

int main()
{
int p = 3;
void *a;

a = &p;

printf("%d\n", *(int *)a); // typecasting implictly and then de-referencing the pointer

return 0;
}
Pointers and Arrays
A Pointer to an array can be simply assigned as follows.
int a[10];
int *p;

p = a;
or p = &a[0].
The pointer p assigned as the pointer to the first element of the array.
#include <stdio.h>

int main()
{
int a[10];
int *p;
int i;

98
for (i = 0; i < sizeof(a) / sizeof(a[0]); i ++) {
a[i] = i;
}

p = a;

for (i = 0; i < sizeof(a) / sizeof(a[0]); i ++) {


printf("a[%d] = %d\n", i, p[i]);
}
printf("\n");

return 0;
}
Below is another example of accessing array elements with a pointer. The
elements of array are updated with the pointer.
#include <stdio.h>

int main()
{
int a[10];
int i;
int *p;

for (i = 0; i < sizeof(a) / sizeof(a[0]); i ++) {


p = &a[i];
*p = i;
}

for (i = 0; i < sizeof(a) / sizeof(a[0]); i ++) {


printf("%d\n", a[i]);
}

return 0;
}
When passing an array to a function, the caller takes it as a pointer input. This
is called as array decaying into a pointer. When such thing happens, calling
sizeof on the pointer gives you 4 or 8 bytes that is the size of the pointer on
the architecture. In general it is wise to pass the number of elements of the array
as argument to the function call.
For example,
int f(int *a, int a_len)
{

99
}

int main()
{
int a[10];

f(a, 10);
}
Pointer Arithmetic
Arithmetic operations are allowed on pointers, but they generally are dangerous
if not done correct. The danger is that a non-allocated / non-reserved address
being accessed during an arithmetic operation results in either unknown code
execution (resulting to using this portion of code for viruses or exploits) or if
lucky leads to a program crash.
#include <stdio.h>

int main()
{
char *p = "hello";

while (*p != '\0') {


printf("'%c'", *p);
p ++;
}
printf("\n");

return 0;
}
The above code checks for the \0 character and iterates through each character
in the string p. The operator ++ allows us to move to the next character.
When a ++ is performed on a character pointer, since the type the pointer is
pointing to, is char the next address is the next byte.
When the same pointer points to an integer, the next address will be the next 4
bytes. Below is an example,
#include <stdio.h>

int main()
{
int a[10] = {1, 2, 3, 4, 5, 6};
int *p;

p = &a[0];

100
while (*p != 6) {
printf("p %d\n", *p);
p ++;
}

printf("\n");

return 0;
}
The value &a above would mean the base address of the array a. Shifting it by
1, moves it to the end of the array. Updated the above program with the change
to retrieve the end of the array a.
#include <stdio.h>

int main()
{
int a[10] = {1, 2, 3, 4, 5, 6};
int *p;

p = &a[0];

while (*p != 6) {
printf("p %d\n", *p);
p ++;
}

p = a;

printf("%p %p %p\n", a, &a + 1, a + 10);

return 0;
}

101
Dynamic Memory Allocation
Dynamic memory is the runtime memory required by the program. Some
programs cannot estimate the amount of memory they need at compilation
time. This has let the OS developers let the program to allocate the memory
at runtime by exposing APIs that return allocated memory from the operating
system. The standard C / C++ library then abstracts it with the libc API for
the programmer.
Dynamic memory is always allocated in the heap section of the program.
The Operating System provides a system call layer that allows the C library to
make a call to the operating system to get the memory allocated for the program.
Generally the memory returned is not contiguous but is sparsed. This behavior
will be different for different operating systems.
Though the memory specific system calls return the memory, the C library
functions keeps a note of memory asked for book keeping or for efficiency.
Below are some of the functions exposed by the Standard Library.

[Link] Function Name Description


1 malloc Allocate memory
2 calloc Allocate and clear the memory area
3 realloc Re-allocate memory or expand the allocated memory
4 free Free the allocated memory

102
1. malloc
The malloc function is a C function that is used to allocate memory dynamically.
It is declared in stdlib.h. The memory allocated by malloc may contain the
old data that is used by other programs.
The prototype is as follows:
void *malloc(int size);
The example code is as follows.
#include <stdio.h>
#include <stdlib.h>

int main()
{
int *a;

a = malloc(sizeof(int));
*a = 4;

printf("a %p *a %d\n", a, *a);

return 0;
}
malloc makes a call to the underlying operating system call to allocate the heap
memory.
Once the memory is allocated, it can only be freed with the free.
The call to the function malloc can fail and it results in a NULL pointer. In the
example above, we are directly dereferencing a without checking if it can be a
NULL pointer.
Dereferencing a NULL pointer results in faults and the program results
into a crash!
The reason of returning NULL is that if the operating system is under high
memory pressure (that is most of the memory is being used), in such cases, the
operating system cannot allocate anymore memory available. This results in
being returning a NULL pointer.
So, the best practises of using allocated pointers is to always check for NULL.
Below is another example of malloc:
#include <stdio.h>
#include <stdlib.h>

int main()

103
{
int *a;
int i;

a = malloc(sizeof(int) * 10); // allocate space for 10 integers


if (!a) { // if a is null pointer, return
return -1;
}

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


a[i] = i;
}

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


printf("a[%d] = %d\n", i, a[i]);
}

return 0;
}
In the above example, the pointer a is allocated with 10 elements and is being
accessed just like an array.
Or the elements can be accessed by incrementing the pointer. Like so,
#include <stdio.h>
#include <stdlib.h>

int main()
{
int *a;
int *p;
int i;

a = malloc(sizeof(int) * 10);
if (!a) {
return -1;
}

p = a;

printf("a %p\n", a);

i = 0;
while (i < 10) {
*a = i;
i ++;

104
Array of function pointers
The array of function pointers have the below syntax.
int (*fptr[4])(void); // defines array of 4 function pointers
To be much simpler, one can typedef the function pointer and define the arrays.
typedef int (*fptr)(void); // defines a function pointer

fptr f[4]; // defines 4 function pointers


Below is an example of array of function pointers,
#include <stdio.h>

int f()
{
static int count = 0;

printf("F called %d\n", count);

return ++ count;
}

typedef int (*fptr_f)(void);

int main()
{
fptr_f fptr[4];
int i;

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


fptr[i] = f;
}

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


fptr[i]();
}

return 0;
}

124
Structures
Data structure is a group of variables of different types. The struct word is
used as an identifier to the compiler to make it recognize the structure.
An example of data structure looks as follows.
struct shelf {
char book_name[10];
int n_papers;
};
Above structure defines a shelf that contains a list of books and papers, one is a
string and another is an integer.
Defining the structure variable is similar to defining the base type.
struct shelf s;
here s is of type structure shelf.
Accessing the elements in the structure is via the . operator.
#include <stdio.h>
#include <string.h>

struct shelf {
char book_name[10];
int n_papers;
};

int main()
{
struct shelf s;

strcpy(s.book_name, "Witcher");
s.n_papers = 2000;

printf("book_name: %s papers: %d\n", s.book_name, s.n_papers);

return 0;
}
A structure can be inside another structure as well.
struct book {
char book_name[10];
char book_author[10];
};

struct shelf {

125
struct book book;
int n_papers;
}
We can apply the typedefs to structures as well. such as,
typedef struct book {
...
} book_t; // define book typedef

typedef struct book book_t; // define typedef in a new line

typedef struct { // define typedef without naming


} book_t;
Now book_t can be used to define structure variables. Generally _t prefix is
used to differentiate the typedefs. But its only a choice of programmer and not
defined by the C standard.
One can also use macros for better sounding names.
#define Book_Info struct book
But doing this generally avoided although its possible.
Macros though can be used this way, their whole purpose is for naming constants
or writing small function like macros.
The elements are accessed as follows.
struct shelf s;

void set_book(struct book *b, char *book_name, char *book_author)


{
strcpy(b->book_name, book_name);
strcpy(b->book_author, book_author);
}

void set_shelf(char *book_name, char *book_author, int n_papers)


{
s.n_papers = n_papers;
set_book(&s.b, book_name, book_author);
}
The variable b of the type struct book is passed as a pointer to the set_book.
The function set_book sets the book_name and book_author.
An array of structures is possible too.
struct book {
char book_name[10];
char book_author[10];

126
};

struct shelf {
struct book books[10];
int n_papers;
}
Pointers in Structures
Structures can contain pointers as well.
struct book {
char *book_name;
char *book_author;
};
These are allocated just the way pointers are allocated.
book->book_name = calloc(1, 40);
book->book_author = calloc(1, 20);
There can be cases when there are structures in a structure which contain pointers
to another structures or variables. In such cases freeing all the structures becomes
a real problem. Doing it in the same order of allocation guarantees no crashes
but leaks memory. To do the right way, one must free in the reverse order of
allocation.
Below example shown provides such a method.
struct S1 {
int v;
};

struct S2 {
struct S1 s1;
};

...
struct S2 s2;

s2.s1.v = 3;

printf("v: %d\n", s2.s1.v);


Structure Initialization
Structures initialization can be done in many ways. One way of initialization is
as follows.
struct A {
int val;

127
double val_d;
};

struct A a = { .val = 3, .val_d = 3.1 };


Is one way to initialize the structure.
Below is an example,
#include <stdio.h>

struct A {
int val;
double val_d;
};

int main()
{
struct A a = { .val = 3, .val_d = 3.1 };

fprintf(stderr, "val: %d\n", [Link]);


fprintf(stderr, "val_d: %f\n", a.val_d);

return 0;
}
To initialize an array of structures, one can use the following approach.
struct A {
int val;
double val_d;
} a[] = {
{
.val = 3,
.val_d = 3.1,
},
{
.val = 4,
.val_d = 3.2,
},
{
.val = 5,
.val_d = 3.3,
}
};
When initializing an array statically the array subscript is not required.
In general, if the array never changes during the program lifetime, then the

128
variable can be set const.
const struct A a[] = { ... };
Array of structures
Arrays of structures is possible as well.
struct A {
int val;
double val_d;
};

struct A a[10]; // array of structures of type `A`.


They can be iterated just like arrays.
struct A {
int val;
double val_d;
};

void set(struct A *a, int size)


{
int i;

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


a[i].val = i;
a[i].val_d = i + 0.1 * i;
}
}
Allocating structures
Structures are allocated in the same way as any variable.
struct S {
int a;
int b;
};

struct S *s;

s = calloc(1, sizeof(struct S));


the access to the member variables can then be done using the -> operator.
s->a = 3;
s->b = s->a;
The freeing is same as well. A call to free(s) would free up the allocated
memory.

129
Function pointers in structures
The below structure declares two function pointers get and set which are
accessible from the structure variable.
struct S {
int (*get)();
void (*set)(int);
};

struct S s;

[Link](3); // set the variable


int var = [Link](); // get the variable
But in general the pointers [Link] and [Link] contain garbage pointers. So
accessing them generally results in a segmentation fault or in bad situation
results in abnormal program execution.
One way to assign the addresses is the following:
int a;
int my_get() { return a; }
void my_set(int A) { a = A; }

struct S s;

memset(&s, 0, sizeof(struct S));

[Link] = my_get;
[Link] = my_set;
Now accessing the function pointers [Link] and [Link] will indirectly call my_get
and my_set functions.
While we can have function pointers in structures, but we cannot have functions
in structure. This is not allowed in C. However, C++ allow this grouping of
data and operations.
The below structure is incorrect and results in error.
#include <stdio.h>

struct S {
int f(void);
};

int main()
{
}

130
The error,
c/struct_func.c:4:9: error: field ‘f’ declared as a function
4 | int f(void);
| ˆ
Structure with function pointers
Below program shows a structure with function pointers.
#include <stdio.h>
#include <stdlib.h>

struct S {
int (*init)(void);
void (*run)(int);
void (*deinit)(void);
};

int driver_init(void)
{
printf("driver_init called\n");

return 0;
}

void driver_run(int a)
{
printf("driver_run called with %d\n", a);
}

void driver_deinit(void)
{
printf("driver_deinit called\n");
}

struct S *get_driver_callbacks()
{
struct S *s;

s = calloc(1, sizeof(struct S));


if (!s) {
return NULL;
}

s->init = driver_init;
s->run = driver_run;
s->deinit = driver_deinit;

131
return s;
}

void free_driver_callbacks(struct S *s)


{
if (s) {
free(s);
}
}

int main()
{
struct S *s;
int ret;

s = get_driver_callbacks();
if (!s) {
return -1;
}

ret = s->init();
if (ret != 0) {
return -1;
}

s->run(3);

s->deinit();

free_driver_callbacks(s);

return 0;
}
A structre with function pointers looks as follows.
struct S {
int (*init)(void);
void (*run)(int);
void (*deinit)(void);
};
Another way to do is to typedef’ the function pointers.
typedef int (*init)(void);
typedef void (*run)(int);
typedef void (*deinit)(void);

132
struct S {
init init;
run run;
deinit deinit;
};
Below, the structure S is allocated as follows,
struct S *s;

s = calloc(1, sizeof(struct S));


if (!s) {
return -1;
}

s->init = driver_init;
s->run = driver_run;
s->deinit = driver_deinit;
Another way to do is to use a static variable local to the file.
struct S s = {
.init = driver_init,
.run = driver_run,
.deinit = driver_deinit,
};
The indirect calls to the functions driver_init, driver_run and
driver_deinit can be made with the structure pointer.
int ret;

ret = [Link]();
if (ret != 0) {
return -1;
}

[Link](3);
[Link]();
In general, the demonstration is that, the type is known to the caller. Here the
type is struct S. But the way to get the pointer to a specific driver function is
not its job. It is the job of another layer that can identify which driver must be
used and how to get the driver callbacks of the type struct S.
Calling a specific function based on a type
This is another usecase where based on a specific type, the corresponding function
needs to be called. This can be implemented by using normal if conditional check

133
and then calling the function. Another way to do is to use function pointers.
Sometime it might be overhead about the use of function pointers. It depends
on when to use the function pointers depending on the usecase. For example,
if there are too many functions to be called for many types, maintaining code
modular requires the use of function pointers.
Below is one example,
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void buy_apples(int number);


void buy_oranges(int number);
void buy_mangoes(int number);

struct S {
char *ptr;
void (*buy)(int number);
};

static const struct S s[] = {


{
.ptr = "apples",
.buy = buy_apples,
},
{
.ptr = "oranges",
.buy = buy_oranges,
},
{
.ptr = "mangoes",
.buy = buy_mangoes,
},
};

const struct S *find_fruit(const char *fruit)


{
int i;

for (i = 0; i < sizeof(s) / sizeof(s[0]); i ++) {


if (!strcmp(s[i].ptr, fruit)) {
return &s[i];
}
}

return NULL;

134
static_cast
dynamic_cast
reinterpret_cast

190
Classes
Classes in C++ are similar to the structures in C. The Class is enclosure for
data and operations on the data.
A class would generally look like this.
class <name> {
public:
<variable_type > variable;
<return_type> function_prototype(parameters..);

protected:
<variable_type > variable;
<return_type> function_prototype(parameters..);

private:
<variable_type > variable;
<return_type> function_prototype(parameters..);
};
The below example provides a simple class definition.

class S {
public:
S() { a = 0; }
~S() { }
void set(int a) { a_ = a; }
void get() { return a_; }

private:
int a_;
};
The functions S() and ~S() are constructor and destructor respectively. The
constructor gets called when the class object is instantiated. The destructor is
called when the class object goes out of scope. Lifecycle of the class object is
similar to that of the C variable.
The functions set and get within S are called public member functions. The
variable a_ is a private member variable. In general the private members are
prefixed or postfixed with something that differentiates between a local variable
and a class member. Without it, it gets really hard to understand the variable’s
lifetime.
The sizeof on classes would give the size of the variables (excluding member
functions).
#include <iostream>

191
class S {
public:
int get() { return a; }

private:
int a;
int p;
double r;
};

int main()
{
std::cout << "size: " << sizeof(S) << std::endl;

return 0;
}
Public members can be accessed by the users of the class while private members
are not accessible.
The below declares the class object of S.
S s;
The member functions set and get are accessible as,
S s;

[Link](3);
int val = [Link]();
Accessing a_ directly as below results in a compiler error that the variable is
part of the private section of the class.
S s;

int val = s.a_; // results in compiler error


The variable a_ can only be accessible via the get method.
If the public keyword is not mentioned then the scope is by default private.
class S {
S() { a = 0; }
~S() { }
void set(int a) { a_ = a; }
void get() { return a_; }

int a_;
};

192
The above code shows class members are all by default, private. Class with all
members private is legal and compiles until it is instantiated by creating an
object of the class.

193
Constructors and Destructors
The constructor is called when an object of it is created. For example,
class S {
public:
S() { a = 3; }
~S() { }

int get() { return a; }

private:
int a;
};

int main()
{
S s;
}
The declaration S s calls the constructor S(). Constructors and Destructors
will have the same name as the class. The destructor has ~ prefix attached to it.
The destructor gets called soon after the object loses its scope.
Below is an example,
#include <iostream>

class P {
public:
P()
{
std::cout << "default constructor" << std::endl;
a = 3;
}
~P()
{
std::cout << "destructor called" << std::endl;
}

int get() { return a; }

private:
int a;
};

int main()
{

194
P p;

std::cout << "p.a: " << [Link]() << std::endl;

return 0;
}
Copy constructor
Copy Assignment operator
Move constructor
this pointer
The this pointer is nothing but self referencing the class member function.
The below program shows the use of this pointer. Download it here
#include <iostream>

class S {
public:
S() { a_ = 0; }
~S() { }

int get() { return this->a_; }


void set(int a) { this->a_ = a; }

private:
int a_;
};

int main()
{
S s;

[Link](3);
std::cout << "val " << [Link]() << std::endl;

return 0;
}
The most important use case is that when the input variable to the member
function and the class variables / functions are same, then to inhibit confusion
and to assist compiler, this can be used.
Below is the example. Download it here
#include <iostream>

195
struct S {
public:
S() { a = 0; }
~S() { }

int get() { return a; }


void set(int a) { this->a = a; }

private:
int a;
};

int main()
{
S s;

[Link](3);
std::cout << "val " << [Link]() << std::endl;

return 0;
}
Virtual functions

196
namespaces
Namespace is a concept to allocate a particular name for one or more classes or
functions.
The using namespace is used to include a particular namespace without includ-
ing its name directly when calling its classes or functions defined in it.
#include <iostream>

using namespace std;

int main()
{
cout << "test\n";

return 0;
}
Which could’ve been the reference of cout with std::cout without the names-
pace.
We can define our own namespaces as well.
namespace math
{

double square(double number) { return number * number; }

}
Defines the function square in namespace math. Below is one way to use it.
Download it here
#include <iostream>

namespace math
{

double square(double number) { return number * number; }

using namespace math;


using namespace std;

int main()
{
double n;

197
n = square(3);
cout << "number " << n << endl;

return 0;
}
Another way of using it as follows. Download it here
#include <iostream>

namespace math
{

double square(double number) { return number * number; }

int main()
{
double n;

n = math::square(3);
std::cout << "number " << n << std::endl;

return 0;
}
In order to access the function, we prefix the members with the namespace
followed by the :: operator.

198
Overloading
Overloading is a concept of having many signatures. In general C++ provides
operator overloading and the function overloading.
Overloading is also refered as compile time polymorphism in C++. This means
that the compiler sees the function / operator prototypes and inserts the corre-
sponding function / operator during the compile time.

199
Operator Overloading
Operators such as +, -, *, >, <, <<, >>, <=, >= and / and many others can be
overloaded.

200
void f() noexcept { std::cout << "in f()" << std::endl; }
};

int main()
{
class G g;

g.f();
}

223
Standard library
Standard library or STL in short is a group of helper function that ease up
programming. Nowadays, they are more focussed towards helping programmers
write OS independent software using C++.

224
std::pair
std::pair defines a pair of values. The usecases are when returning more than
one variable from a function or passing to a function as key-value pair.
std::pair can be used to construct a pair or std::make_pair can be used as
well.
auto p = std::pair<std::string, int>("test", 1); // constructs std::pair type in p
auto p = std::make_pair<std::string, int>("test", 1); // makes a pair of type std::pair
Below is one example of the usecase for std::pair:
#include <iostream>

void f(std::pair<const std::string, int> p)


{
std::cout << "first: " << [Link] << " second: " << [Link] << std::endl;
}

int main()
{
f(std::make_pair("test", 1));
f(std::make_pair<std::string, int>("test", 2));
f(std::pair("test", 1));
}
std::pair also exposes operators ==, !=, < and >. One can use certain operations
for comparison. Here’s one example: Download it here
#include <iostream>

int main()
{
auto r = std::pair<std::string, int>("test", 1);
auto r1 = std::pair<std::string, int>("test", 1);

std::cout << "r == r1: " << (r == r1) << std::endl;

return 0;
}
std::pair can be implemented with templates. See usecases section for the
implementation of std::pair.

225
std::initializer_list

226
std::bitset

227
File streams
std::istream
std::fstream

228
Arrays
std::array defines an array type. The std::array contains the following
methods:
The std::array template looks as follows,
template <typename T>
std::array<T, n>
Most usual way of declaring an array of integers is,
std::array<int, 10> a;
This is similar to declaring int a[10].
1. at Returns the value at the index.
2. operator[] Used to access the element stored at the index.
3. size Returns the size of the array.
Below is an example of std::array.
#include <iostream>
#include <array>

int main()
{
std::array<int, 10> a;
int i;

for (i = 0; i < [Link](); i ++) {


a[i] = i + i;
}

for (i = 0; i < [Link](); i ++) {


std::cout << "i : " << i << " " << "a[i] : " << [Link](i) << std::endl;
}

return 0;
}

229
Strings
std::string defines a string type. Requires <string> but it seems <iostream>
seem to implictly include it.

S. No Method Description
1 c_str() get the C style string
2 front() access the front character
3 back() access the last character
4 size() length of the string
5 clear() clear the string
6 operator[] array operator to index string elements
7 at get character of the string at a position
8 operator+= strcat for std::string
9 operator= assign the value to the string

Below is one of the example,


#include <iostream>

int main()
{
std::string str = "tests";

std::cout << "str: " << str


<< " c_str(): " << str.c_str()
<< " front(): " << [Link]()
<< " back(): " << [Link]() << std::endl;

return 0;
}
The + operator on the strings is similar to strcat for C strings.
#include <iostream>
#include <string>

int main()
{
std::string r1 = "r1";
std::string r2 = "r2";
std::string r3 = r1 + r2;

printf("r1: %s r2: %s r3: %s\n", r1.c_str(), r2.c_str(), r3.c_str());

return 0;
}

230
Strings can be iterated with the indices just like the C strings.
#include <iostream>
#include <string>

int main()
{
std::string r = "hello world";

for (uint32_t i = 0; i < [Link](); i ++) {


printf("'%c'", r[i]);
}
printf("\n");

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


printf("'%c'", r[i]);
}
printf("\n");

return 0;
}
Clear the strings with clear member function.
#include <iostream>
#include <string>

int main()
{
std::string r = "hello world";

printf("%s\n", r.c_str());

[Link]();

printf("%s\n", r.c_str());

return 0;
}

231
Vectors
std::vector defines a vector type. This is similar to the doubly linked list. It
is declared in <vector>.
Below are the supported methods.

[Link] Method Description


1 push_back insert an element to the end
2 at write / access an element at the index
3 begin start of the iteration
4 end end of the iteration
5 cbegin start of constant iteration
6 cend end of constant iteration
7 rbegin start of reverse iteration
8 rend end of reverse iteration
9 erase erase one or more elements
10 insert insert one or more elements
11 resize resize the vector
12 clear erase all the elements from the vector

1. push_back
Push an element at the end of the vector.
std::vector<int> v;

v.push_back(1);
v.push_back(2);
2. at
Get the index pointer.
std::vector<int> v;

v.push_back(1);
v.push_back(2);

for (int i = 0; i < [Link](); i ++) {


std::cout << "val: " << [Link](i) << std::endl;
}
std::vector<int> v;

v.push_back(1);
v.push_back(2);

232
[Link](3); // resize the elements of the vector
[Link](2) = 3; // set an element at index 2
Without the resize call the at assignment will fail resulting in out of bounds
exception.
3. begin
Get the beginning of the vector.
std::vector<int> v;

v.push_back(1);
v.push_back(2);

auto i = [Link]();

std::cout << "val: " << i << std::endl;


4. end
Get the end of the vector.
std::vector<int> v;

v.push_back(1);
v.push_back(2);

auto t = [Link]() - 1; // last element of the vector

std::cout << "val: " << t << std::endl;


std::vector<int> v;

v.push_back(1);
v.push_back(2);

for (auto it : v) {
std::cout << "val: " << it << std::endl;
}

std::vector<int>::iterator it;

// iterate from begin to end


for (it = [Link](); it != [Link](); it ++) {
std::cout << "val: " << *it << std::endl;
}
5. cbegin
Get the beginning of the vector iteration using const.

233
};

int main()
{
R r;

std::cout << "R: " << [Link]() << std::endl;

return 0;
}
Though the above program instantiates R directly, it may not be very useful to
instantiate R. In general abstract classes can be instantiated via other means.
See Design patterns for Factory method.
Much of the uses of inheritance lie in the designs and abstractions. They can be
used to represent the designs in the form of C++.

263
Templates
Templates allow to write software generically. Below is an example of a template.
template <typename T>
class calculator {
};
Where T is the type. The instantiation of the class object for this would be,
class calculator<int> cal;
Defines the class calculator as a template.
There can be more than one template types.
For example, the following is valid.
template <typename T, typename R, typename P>
class calculator {
};
The member functions of the class can be written as follows.
template <typename T>
class calculator {
public:
T add(T a, T b);
T sub(T a, T b);
T mul(T a, T b);
T div(T a, T b);
T mod(T a, T b);
};
The member functions describe that the inputs to the member functions are all
of type T and returns type T.
For example,
calculator<int> cal;
declaration of object means that the member functions also are of same type.
For example, passing other type instead of the same results in compiler error.
We can write the calculator program that is written in C with macros, in C++
with templates as follows:
#include <iostream>

template <typename T>


class calculator {
public:
T add(T a, T b) { return a + b; }

264
T sub(T a, T b) { return a - b; }
T mul(T a, T b) { return a * b; }
T div(T a, T b) { return a / b; }
T mod(T a, T b) { return a % b; }
};

int main()
{
calculator<int> cal;

std::cout << "Add: " << [Link](3, 3) << std::endl;


std::cout << "Sub: " << [Link](3, 3) << std::endl;
std::cout << "Mul: " << [Link](3, 3) << std::endl;
std::cout << "Div: " << [Link](3, 3) << std::endl;
std::cout << "Mod: " << [Link](3, 3) << std::endl;

return 0;
}
Here we used calculator<int> for the cal object. This means all the oper-
ations / member functions of the calculator will accept integers. If we used
calculator<double> it would be the double that is being used in all the opera-
tions.
For example, there can be a chance that string could’ve been used such as
calculator<std::string> cal. In this case the compilation results in failure
because the arguments given are integers. Certain overloaded string operations
such as +, - may work, but the other operations which does not have the
overloaded types will result in compilation failure.
A normal function can be overloaded with the templates such as the following
example,
#include <iostream>

template <typename T>


void print(T val)
{
std::cout << "template: val: " << val << std::endl;
}

void print(int v)
{
std::cout << "int: val: " << v << std::endl;
}

int main()
{

265
print(3);
print<std::string>("hello");
print<int>(3);

return 0;
}
Here the print(3) is called directly which by the explicit function and variable
declaration the print(int v) gets called. When the print is called with < and
>, the templatized version gets invoked.
If in case print(int v) is not available, the first call to print(3) actually
results as an implicit call to print<int>(3). The compiler deduces the type
implicitly.
Template with default type
Templates can have default type representing the type to use if in case not given.
For example,
template <typename T, typename R = int>
void f(T a, R b);
Shows the typename R defaults to int. So when the template parameter is not
given for identification, the default is used instead.
For example,
f<double>(1.1, 1); // uses <double, int> instead
f<double, double>(1.1, 2.2): // explicitly specified double, so default does not matter here
Below is one example,
#include <iostream>

template <typename T, typename R = int>


void f(T a, R b)
{
std::cout << "a: " << a << " "
<< "b: " << b << std::endl;
}

int main()
{
f<double>(1.1, 1);
f<double, double>(1.1, 2.2);

return 0;
}
Usecase 1: Implementing std::array

266
Templates can as well have a normal types such as int, float etc.
For example,
template<typename T, int n>
class p {
};

p<int, 10> a;
This means that the passed number 10 is a constant through out the object
lifecycle.
For example, these can be used to define static array.
template<typename T, int n>
class p {
T array_[n]; // define an array of constant size n
};

p<int, 10> a; // here we defined array size as 10


This approach can be used as a method for writing implementation of
std::array.
Array supposed to have the following functionalities.

[Link] Name Description


1 operator[] Indexing into an array
2 at Access an element at given position
3 size Get the size of the array
4 clear Clear the array with a given value

Below is an implementation.
#include <iostream>

template <typename T, int n>


class array {
public:
explicit array() = default;
~array() = default;

T &operator[](int index) { return array_[index]; }

T at(int index) { return array_[index]; }

267
int size() { return n; }

void clear(const T val) {


for (auto i = 0; i < n; i ++) {
array_[i] = val;
}
}
private:
T array_[n];
};

int main()
{
array<int, 10> a;

a[1] = 4;

std::cout << "array " << a[1] << std::endl;


}

268
Template overloading
Template overloading is possible just like the function overloading that is dis-
cussed.
Below is one example of template overloading.
template <typename T>
int print(T &val);

template <typename T, typename R>


int print(T &val, R &val2);

int print(int val);


Below is an example usage of the overloaded templates.
#include <iostream>

template <typename T>


int print(T val)
{
std::cout << "val: " << val << std::endl;

return 0;
}

template <typename T, typename R>


int print(T val1, R val2)
{
std::cout << "val1: " << val1 << " " << "val2: " << val2 << std::endl;

return 0;
}

int print(int val)


{
std::cout << "normal val: " << val << std::endl;

return 0;
}

int main()
{
print(3);
print(3, 6);
print(3u);
print<int>(3);

269
}
There are 2 types of calling conventions to invoke the template <typename
T>print.
One is to explicitly use the <int> to indicate the compiler that call the template
version.
Another is to append the type to the integer 3 to inform that its not a signed
integer which inturn inform the compiler to invoke the template version.

270
Appendix B

271
Use cases

272
Useful functions 1. Template min
template <typename T>
T min(T a, T b)
{
return a < b ? a: b;
}
2. Template max
template <typename T>
T max(T a, T b)
{
return a > b ? a: b;
}

273
std::cout << "p != p1: " << (p != p1) << std::endl;

return 0;
}

290
Event Driven System

Design Patterns

291
Factory Design pattern

292
Singleton Design pattern
The singleton pattern is used when an object is being used by many other classes.
One way to do is to instantiate it statically and return that instance.
Since its been used by many other classes, the instantiation happens statically
within the class itself. For this one generally defines instance member function
that returns the statically declared class object. The constructor is hidden to
prevent any more instantiations by the class declarations.
An example singleton class looks as follows.
class singleton {
public:
static singleton *instance() {
static singleton s;
return &s;
}
~singleton() = default;
singleton(const singleton &) = delete;
const singleton &operator=(const singleton &) = delete;
singleton(const singleton &&) = delete;
const singleton &&operator=(const singleton &&) = delete;

int member(...);

private:
explicit singleton();
}
We delete the copy and move constructors so that only one instance that is
created during the call to the static member function instance is the only
instance that is available.

293
usecase.1: Logging utility
Singleton can be used when writing a logging utility that logs the message
/ debug message to something like console or to a file, but does not require
instantiation everytime when we want to use the object.
An example of it looks as follows:
class log {
public:
static log *instance() {
static log l;
return &l;
}
~log() { }
log(const log &) = delete;
const log &operator=(const log &) = delete;
log(const log &&) = delete;
const log &&operator=(const log &&) = delete;

int info(const char *msg, ...);


int verbose(const char *msg, ...);
int debug(const char *msg, ...);
int warn(const char *msg, ...);
int error(const char *msg, ...);
int fatal(const char *msg, ...);
private:
explicit log() { }
};
The above class is a singleton that has many member functions for logging such
as,
1. info
2. verbose
3. debug
4. warning
5. error
6. fatal
The member functions of this singleton can be accessed from anywhere as long
as they include the header file that this class belongs.
The call can be simply made as :
log *l = log::instance();

l->info("info message\n");
or

294
log::instance()->info("info message\n");
usecase.2: Datastore
Data store is another use of singleton class. Lets see the below class:
class key_val_datastore {
public:
static key_val_datastore *instance() {
static key_val_datastore ds;
return &ds;
}
~key_val_datastore() { }
key_val_datastore(const key_val_datastore &) = delete;
const key_val_datastore &operator=(const key_val_datastore &) = delete;
key_val_datastore(const key_val_datastore &&) = delete;
const key_val_datastore &&operator=(const key_val_datastore &&) = delete;

int write(uint32_t val);


int write(std::string val);
int read(uint32_t &val);
int read(std::string &val);
private:
explicit key_val_datastore() { }
};
Just as in the usecase 1, the data store can be read and written with the member
functions.
Ofcourse there will be parallel accesses, which can be sequentialized with the
use of mutexes.

295
Builder Design pattern
Builder pattern generally involve a class object being returned by every call to
the member function of the returned class object. This allows repeated calls to
build or initialize certain contents within the class object.
For example consider building a server.
1. components
2. assemble of components - this can be done all at once or if the assembly
is different, it can be done one member function at a time specific to the
component.
An example of such a class that have the properties of a builder is as follows.
class pc_assembly {
public:
explicit pc_assembly() { }
~pc_assembly() { }

...
pc_assembly &acquire_components() {
// .. manufacture components..
return *this;
}
pc_assembly &assemble_components() {
// .. manufacture components..
return *this;
}
}
The calling now becomes,
pc_assembly p;

p.acquire_components().assemble_components();
As shown above, such pattern can be repeated many times over until all the
items are built.
Below is an example.
#include <iostream>

class pc_builder {
public:
explicit pc_builder() { }
~pc_builder() { }

pc_builder &assemble_cpu() {

296
std::cout << "cpu aseembling done" << std::endl;
cpu_ = true;
return *this;
}

pc_builder &assemble_gpu() {
std::cout << "gpu assembling done" << std::endl;
gpu_ = true;
return *this;
}

pc_builder &assemble_fan() {
std::cout << "fan assemble done" << std::endl;
fan_ = true;
return *this;
}

pc_builder &assemble_ram() {
std::cout << "ram assemble done" << std::endl;
ram_ = true;
return *this;
}

pc_builder &assemble_components() {
std::cout << "aseembled rest of the components" << std::endl;
components_ = true;
return *this;
}

bool power_on() {
std::cout << "power on ok" << std::endl;
return true;
}

private:
bool cpu_;
bool gpu_;
bool fan_;
bool ram_;
bool components_;
};

int main()
{
pc_builder b;

297
b.assemble_cpu()
.assemble_gpu()
.assemble_fan()
.assemble_ram()
.assemble_components();

b.power_on();
}

298
Adapter Design pattern

299
Appendix C

300

You might also like