2020 Full-time Interview CCJ’s Preparation (1): Commonly Asked C++ Interview Questions

1: What are the differences between C and C++?

see link: https://www.geeksforgeeks.org/commonly-asked-c-interview-questions-set-1/

2. What are the differences between references and pointers?

see link https://www.geeksforgeeks.org/commonly-asked-c-interview-questions-set-1/

Their similarities:

Their differences:

3. What are virtual functions – Write an example?

A virtual function is a member function which is declared in the base class using the keyword virtual and is re-defined (overridden) by the derived class.

Virtual functions are used with inheritance, they are called according to the type of object pointed or referred, not according to the type of pointer or reference. In other words, virtual functions are resolved late, at runtime. Virtual keyword is used to make a function virtual.

Following things are necessary to write a C++ program with runtime polymorphism (use of virtual functions, i.e., 多态性)

For example, in the following program bp is a pointer of type Base, but a call to bp->show() calls show() function of Derived class, because bp points to an object of Derived class.

#include<iostream>
using namespace std;
class Base {
public:
virtual void show() { cout<<" In Base \n"; }
};
class Derived: public Base {
public:
void show() { cout<<"In Derived \n"; }
};
int main(void) {
Base *bp = new Derived;
bp->show(); // RUN-TIME POLYMORPHISM
return 0;
}

Alt text

4. What is this pointer?

The ‘this’ pointer is passed as a hidden argument to all nonstatic member function calls and is available as a local variable within the body of all nonstatic functions.

class A {
public:
void fun(){
cout << "try to run 'delete this'\n";
delete this;
}
};
int main() {
/* Following is Valid */
A *ptr = new A;
ptr->fun();
ptr = NULL; // make ptr=NULL to make sure that things are not accessed using ptr;
/* And following is Invalid: Undefined Behavior */
A a;
a.fun();
getchar();
return 0;
}

Alt text

5. What exactly does an #if 0 … #endif block do?

#if 0
//Code goes here
#endif

Answer:

#if is a preprocessor command, which gets evaluated before the actual compilation step. The code inside that block doesn’t appear in the compiled binary.

It’s often used for temporarily removing segments of code with the intention of turning them back on later.

6. C++的数据: Variables, Literals and Constants

see: https://www.tutorialspoint.com/cplusplus/cpp_variable_types.htm

In this section, we will learn about variables, literals, and constants in C++ with the help of examples.

C++的数据包括常量(literal)与变量(variable), 常量与变量都具有类型。

1) Variable:

There are following basic types of variable in C++: bool, char, int, float, double, void, wchar_t (A wide character type). C++ also allows to define various other types of variables, like Enumeration(enum), Pointer (*), Array, Reference (&), Structures (struct), and Classes (class).

a) Variable Definition in C++:

int i, j, k;
int d = 3, f = 5; // definition and initializing d and f.
float f, salary;
double d;

b) Variable Declaration in C++:

#include <iostream>
using namespace std;
// Variable declaration:
extern int a, b;
extern int c;
extern float f;
int main () {
// Variable definition:
int a, b;
int c;
float f;
// actual initialization
a = 10;
b = 20;
c = a + b;
cout << c << endl ;
f = 70.0/3.0;
cout << f << endl ;
return 0;
}
// function declaration
int func(int);
int main(){
// function call
int i = func();
}
// function definition
int func(int a) {
return a;
}

c) Variable Scope in C++:

2) C++ Constants/Literals:

Constants refer to fixed values that the program may not alter and they are called literals.

Constants can be of any of the basic data types and can be divided into into Integer Numerals, Floating-Point Numerals, Characters, Strings and Boolean Values. Again, constants are treated just like regular variables except that their values cannot be modified after their definition.

a) Integer Literals:

212 // Legal
215u // Legal, unsigned int
0xFeeL // Legal, L for long int
078 // Illegal: 8 is not an octal digit
032UU // Illegal: cannot repeat a suffix
//Following are other examples of various types of Integer literals;
85 // decimal
0213 // octal
0x4b // hexadecimal
30 // int
30u // unsigned int
30l // long
30ul // unsigned long

b) Floating-point Literals:

3.14159 // Legal
314159E-5L // Legal
510E // Illegal: incomplete exponent
210f // Illegal: no decimal or exponent
.e55 // Illegal: missing integer or fraction

c) Boolean Literals:

d) Character Literals:

e) String Literals:

//All the three forms are identical strings.
"hello, dear"
"hello, \
dear"
"hello, " "d" "ear"

f) Defining Constants

see: https://www.learncpp.com/cpp-tutorial/const-constexpr-and-symbolic-constants/

- To make a variable constant, simply put the `const` keyword either before (which is recommend) or after the variable type, like so:
const double gravity { 9.8 }; // preferred use of const before type
int const sidesInSquare { 4 }; // okay, but not preferred

Alt text

Live Demo
#include <iostream>
using namespace std;
//#define LENGTH 10
//#define WIDTH 5
//#define NEWLINE '\n'
int main() {
const int LENGTH = 10;
const int WIDTH = 5;
const char NEWLINE = '\n';
int area;
area = LENGTH * WIDTH;
cout << area;
cout << NEWLINE;
return 0;
}

7. C++ Modifier Types

The data type modifiers are listed here:

The modifiers signed, unsigned, long, and short can be applied to integer base types. In addition, signed and unsigned can be applied to char, and long can be applied to double.

The modifiers signed and unsigned can also be used as prefix to long or short modifiers. For example, unsigned long int.

C++ allows a shorthand notation for declaring unsigned, short, or long integers. You can simply use the word unsigned, short, or long, without int. It automatically implies int. For example, the following two statements both declare unsigned integer variables.

unsigned x;
unsigned int y;

8. The static Storage Class

#include <iostream>
// Function declaration
void func(void);
static int count = 10; /* Global variable */
main() {
while(count--) {
func();
}
return 0;
}
// Function definition
void func( void ) {
static int i = 5; // local static variable
i++;
std::cout << "i is " << i ;
std::cout << " and count is " << count << std::endl;
}
i is 6 and count is 9
i is 7 and count is 8
i is 8 and count is 7
i is 9 and count is 6
i is 10 and count is 5
i is 11 and count is 4
i is 12 and count is 3
i is 13 and count is 2
i is 14 and count is 1
i is 15 and count is 0

9. The extern Storage Class

#include <iostream>
int count ;
extern void write_extern();
main() {
count = 5;
write_extern();
}
#include <iostream>
extern int count;
void write_extern(void) {
std::cout << "Count is " << count << std::endl;
}
$g++ main.cpp support.cpp -o my_exam1

This will produce my_exam1 executable program, try to execute it and check the result as follows −

$./my_exam1
>>> 5

10. In C: What is the difference between ++i and i++?

i = 1;
j = ++i;
(i is 2, j is 2)
i = 1;
j = i++;
(i is 2, j is 1)

11. Bitwise Operator

C++ Python Note Examples
& & Binary AND Operator x & y: Does a “bitwise and”. Each bit of the output is 1 if the corresponding bit of AND of is 1, otherwise it’s 0.
| | Binary OR Operator x | y: Does a “bitwise or”. Each bit of the output is 0 if the corresponding bit of AND of is 0, otherwise it’s 1
^ ^ Binary XOR Operator x ^ y: Does a “bitwise exclusive or”. Each bit of the output is the same as the corresponding bit in x if that bit in y is 0, and it’s the complement of the bit in x if that bit in y is 1
~ ~ Binary Ones Complement Operator ~ x: Returns the complement of x. i.e., the number you get by switching each 1 for a 0 and each 0 for a 1. This is the same as -x - 1
<< << Binary Left Shift Operator. x << y: Returns with the bits shifted to the left by places (and new bits on the right-hand-side are zeros). This is the same as multiplying by
>> >> Binary Right Shift Operator. x >> y: Returns with the bits shifted to the right by places. This is the same as //’ing by

12. Logical Operators

C++ Python Note Examples
&& and Called Logical AND operator x && y(C++) or x and y(Python): Returns True if both statements are true
|| or Called Logical OR operator x || y(C++) or x or y(Python): Returns True if one of the statements is true
! not Called Logical NOT operator !x(C++) or not x(Python): Reverse the result, returns False if the result is true

13. Misc Operators

The following table lists some other operators that C++ supports.

Sr.No Operator & Description
sizeof sizeof operator returns the size of a variable. For example, sizeof(a), where ‘a’ is integer, and will return 4.
Condition ? X : Y Conditional operator (?). If Condition is true then it returns value of X otherwise returns value of Y.
, Comma operator Comma operator causes a sequence of operations to be performed. The value of the entire comma expression is the value of the last expression of the comma-separated list.
. (dot) and -> (arrow) Member operators are used to reference individual members of classes, structures, and unions.
Cast, e.g., int(x) Casting operators convert one data type to another. For example, int(2.2000) would return 2. This syntax works in both C++ and Python.
& Pointer operator Pointer operator & returns the address of a variable. For example &a; will give actual address of the variable.
* Pointer operator * is pointer to a variable. For example *var; will pointer to a variable var.

14. Loop Types in C++ and Python

1) while loop:

Alt text

Alt text

Alt text

2) for loop:

// for loop execution
for( int i = 10; i < 20; ++i){
cout << "value of i: " << i << endl;
}
for x in range(6):
print(x)

3) do … while loop in C++

Alt text

15. Loop Control Statements in C++ and Python

C++ supports the following control statements.

16. C++ Pointers

1) What are Pointers?

int var1;
char var2[10];
cout << "Address of var1 variable: " << &var1 << endl;
// Address of var1 variable: 0xbfebd5c0
cout << "Address of var2 variable: " << &var2 << endl;
// Address of var2 variable: 0xbfebd5b6
int *ip = NULL; // pointer to an integer
double *dp; // pointer to a double
float *fp; // pointer to a float
char *ch // pointer to character

2) Pointers vs Arrays

int var[MAX] = {10, 100, 200};
int *ptr;
// let us have array address in pointer.
ptr = var;
for (int i = 0; i < MAX; i++) {
cout << "Address of var[" << i << "] = " << ptr << endl;
cout << "Value of var[" << i << "] = " << *ptr << endl;
// point to the next location
ptr++;
}

When the above code is compiled and executed, it produces result something as follows −

Address of var[0] = 0xbfa088b0
Value of var[0] = 10
Address of var[1] = 0xbfa088b4
Value of var[1] = 100
Address of var[2] = 0xbfa088b8
Value of var[2] = 200

b) pointers and arrays are not completely interchangeable

#include <iostream>
using namespace std;
const int MAX = 3;
int main () {
int var[MAX] = {10, 100, 200};
for (int i = 0; i < MAX; i++) {
*var = i; // This is a correct syntax
var++; // This is incorrect.
}
return 0;
}

17. C++ References

1) What are References?

A reference variable is an alias, that is, another name for an already existing variable. Once a reference is initialized with a variable, either the variable name or the reference name may be used to refer to the variable.

2) References vs Pointers

References are often confused with pointers but three major differences between references and pointers are −

3) Creating References in C++

int i = 17;

We can declare reference variables for i as follows.

int& r = i;

Read the & in these declarations as reference. Thus, read the first declaration as "r is an integer reference initialized to i" and read the second declaration as "s is a double reference initialized to d.". Following example makes use of references on int and double −

Live Demo
#include <iostream>
using namespace std;
int main () {
// declare simple variables
int i = 5;
double d = 11.7;
// declare reference variables
int& r = i;
double& s = d;
cout << "Value of i : " << i << endl;
cout << "Value of i reference : " << r << endl;
cout << "Value of d : " << d << endl;
cout << "Value of d reference : " << s << endl;
return 0;
}

When the above code is compiled together and executed, it produces the following result −

Value of i : 5
Value of i reference : 5
Value of d : 11.7
Value of d reference : 11.7

4) References for function argument lists

References are usually used for function argument lists and function return values. So following are two important subjects related to C++ references which should be clear to a C++ programmer −

a) References as Parameters

#include <iostream>
using namespace std;
// function declaration
void swap(int& x, int& y);
int main () {
// local variable declaration:
int a = 100;
int b = 200;
cout << "Before swap, value of a :" << a << endl;
cout << "Before swap, value of b :" << b << endl;
/* calling a function to swap the values.*/
swap(a, b);
cout << "After swap, value of a :" << a << endl;
cout << "After swap, value of b :" << b << endl;
return 0;
}
// function definition to swap the values.
void swap(int& x, int& y) {
int temp;
temp = x; /* save the value at address x */
x = y; /* put y into x */
y = temp; /* put x into y */
return;
}

b) Reference as Return Value

#include <iostream>
#include <ctime>
using namespace std;
double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
double& setValues( int i ) {
return vals[i]; // return a reference to the ith element
}
// main function to call above defined function.
int main () {
cout << "Value before change" << endl;
for ( int i = 0; i < 5; i++ ) {
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
setValues(1) = 20.23; // change 2nd element
setValues(3) = 70.8; // change 4th element
cout << "Value after change" << endl;
for ( int i = 0; i < 5; i++ ) {
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
return 0;
}
Value before change
vals[0] = 10.1
vals[1] = 12.6
vals[2] = 33.1
vals[3] = 24.1
vals[4] = 50
Value after change
vals[0] = 10.1
vals[1] = 20.23
vals[2] = 33.1
vals[3] = 70.8
vals[4] = 50
int& func() {
int q;
//! return q; // Compile time error
static int x;
return x; // Safe, x lives outside this scope
}

18. C++ Functions

A function is a group of statements that together perform a task. Every C++ program has at least one function, which is main(), and all the most trivial programs can define additional functions.

1) Defining a Function

Alt text

2) Function Declarations

3) Function Arguments

4) Calling a Function in C++

a) call by value:

The call by value method of passing arguments to a function copies the actual value of an argument into the formal parameter of the function. In this case, changes made to the parameter inside the function have no effect on the argument.
By default, C++ uses call by value to pass arguments. In general, this means that code within a function cannot alter the arguments used to call the function.

b) call by pointer:

The call by pointer method of passing arguments to a function copies the address of an argument into the formal parameter. Inside the function, the address is used to access the actual argument used in the call. This means that changes made to the parameter affect the passed argument.

To pass the value by pointer, argument pointers are passed to the functions just like any other value. So accordingly you need to declare the function parameters as pointer types as in the following function swap(), which exchanges the values of the two integer variables pointed to by its arguments.

// function definition to swap the values.
void swap(int *x, int *y) {
int temp;
temp = *x; /* save the value at address x */
*x = *y; /* put y into x */
*y = temp; /* put x into y */
return;
}

c) call by reference:

The call by reference method of passing arguments to a function copies the reference of an argument into the formal parameter. Inside the function, the reference is used to access the actual argument used in the call. This means that changes made to the parameter affect the passed argument.

To pass the value by reference, argument reference is passed to the functions just like any other value. So accordingly you need to declare the function parameters as reference types as in the following function swap(), which exchanges the values of the two integer variables pointed to by its arguments.

// function definition to swap the values.
void swap(int &x, int &y) {
int temp;
temp = x; /* save the value at address x */
x = y; /* put y into x */
y = temp; /* put x into y */
return;
}

19: C++ Data Structures: struct

C/C++ arrays allow you to define variables that combine several data items of the same kind, but structure is another user defined data type which allows you to combine data items of different kinds.

1) Defining a Structure

To define a structure, you must use the struct statement. The struct statement defines a new data type, with more than one member, for your program. The format of the struct statement is this −

struct [structure tag] {
member definition;
member definition;
...
member definition;
} [one or more structure variables];

The structure tag is optional and each member definition is a normal variable definition, such as int i; or float f; or any other valid variable definition. At the end of the structure’s definition, before the final semicolon, you can specify one or more structure variables but it is optional. Here is the way you would declare the Book structure −

struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
} book;

2) Accessing Structure Members

To access any member of a structure, we use the member access operator (.). The member access operator is coded as a period between the structure variable name and the structure member that we wish to access. You would use struct keyword to define variables of structure type. Following is the example to explain usage of structure −

struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
};
struct Books Book1; // Declare Book1 of type Book
// book 1 specification
strcpy( Book1.title, "Learn C++ Programming");
strcpy( Book1.author, "Chand Miyan");
strcpy( Book1.subject, "C++ Programming");
Book1.book_id = 6495407;

3) Structures as Function Arguments

You can pass a structure as a function argument in very similar way as you pass any other variable or pointer. You would access structure variables in the similar way as you have accessed in the above example −

void printBook( struct Books book ) {
cout << "Book title : " << book.title <<endl;
cout << "Book author : " << book.author <<endl;
cout << "Book subject : " << book.subject <<endl;
cout << "Book id : " << book.book_id <<endl;
}
printBook( Book1 );

4) Pointers to Structures

You can define pointers to structures in very similar way as you define pointer to any other variable as follows −

Now, you can store the address of a structure variable in the above defined pointer variable. To find the address of a structure variable, place the & operator before the structure’s name as follows −

To access the members of a structure using a pointer to that structure, you must use the -> operator as follows −

5) The typedef Keyword

There is an easier way to define structs or you could “alias” types you create. For example −

typedef struct {
char title[50];
char author[50];
char subject[100];
int book_id;
} Books;

Now, you can use Books directly to define variables of Books type without using struct keyword. Following is the example −

You can use typedef keyword for non-structs as well as follows −

Then x, y and z are all pointers to long ints.

20. C++ Classes and Objects

1) C++ Class Definitions

A class definition starts with the keyword class followed by the class name; and the class body, enclosed by a pair of curly braces. A class definition must be followed either by a semicolon or a list of declarations. For example, we defined the Box data type using the keyword class as follows −

class Box {
public:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};

The keyword public determines the access attributes of the members of the class that follows it. A public member can be accessed from outside the class anywhere within the scope of the class object. You can also specify the members of a class as private or protected which we will discuss in a sub-section.

2) Define C++ Objects

A class provides the blueprints for objects, so basically an object is created from a class. We declare objects of a class with exactly the same sort of declaration that we declare variables of basic types. Following statements declare two objects of class Box −

Box Box1; // Declare Box1 of type Box
Box Box2; // Declare Box2 of type Box

Both of the objects Box1 and Box2 will have their own copy of data members.

3) Accessing the Data Members

The public data members of objects of a class can be accessed using the direct member access operator (.).

4) C++ Class Member Functions

class Box {
public:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
double getVolume(void) {
return length * breadth * height;
}
};
double Box::getVolume(void) {
return length * breadth * height;
}
Box myBox; // Create an object
myBox.getVolume(); // Call member function for the object

5) C++ Class Access Modifiers: public, private, and protected

Data hiding is one of the important features of Object Oriented Programming (OOP) which allows preventing the functions of a program to access directly the internal representation of a class type. The access restriction to the class members is specified by the labeled public, private, and protected sections within the class body.

The keywords public, private, and protected are called access specifiers.

A class can have multiple public, protected, or private labeled sections. Each section remains in effect until either another section label or the closing right brace of the class body is seen.

The default access for members and classes is private.

a) The public Members:

b) The private Members:

class Box {
double width;
public:
double length;
void setWidth( double wid );
double getWidth( void );
};

Practically, we define data in private section and related functions in public section so that they can be called from outside of the class as shown in the following program.

#include <iostream>
using namespace std;
class Box {
public:
double length;
void setWidth( double wid );
double getWidth( void );
private:
double width;
};
// Member functions definitions
double Box::getWidth(void) {
return width ;
}
void Box::setWidth( double wid ) {
width = wid;
}
// Main function for the program
int main() {
Box box;
// set box length without member function
box.length = 10.0; // OK: because length is public
cout << "Length of box : " << box.length <<endl;
// set box width without member function
// box.width = 10.0; // Error: because width is private
box.setWidth(10.0); // Use member function to set it.
cout << "Width of box : " << box.getWidth() <<endl;
return 0;
}

When the above code is compiled and executed, it produces the following result −

Length of box : 10
Width of box : 10

c) The protected Members

A protected member variable or function is very similar to a private member but it provided one additional benefit that they can be accessed in child classes which are called derived classes.

You can check following example where I have derived one child class SmallBox from a parent class Box.

Following example is similar to above example and here width member will be accessible by any member function of its derived class SmallBox.

include <iostream>
using namespace std;
class Box {
protected:
double width;
};
class SmallBox:Box { // SmallBox is the derived class.
public:
void setSmallWidth( double wid );
double getSmallWidth( void );
};
// Member functions of child class
double SmallBox::getSmallWidth(void) {
return width ;
}
void SmallBox::setSmallWidth( double wid ) {
width = wid;
}
// Main function for the program
int main() {
SmallBox box;
// set box width using member function
box.setSmallWidth(5.0);
cout << "Width of box : "<< box.getSmallWidth() << endl;
return 0;
}
Width of box : 5

6) The Class Constructor and Destructor

a) Class Constructor:

#include <iostream>
using namespace std;
class Line {
public:
void setLength( double len );
double getLength( void );
Line ();// This is a default constructor;
Line(double len); // This is a parameterized constructor;
private:
double length;
};
// Member functions definitions including constructor
Line::Line(void) {
cout << "Object is being created" << endl;
}
// Member functions definitions including constructor
Line::Line( double len) {
cout << "Object is being created, length = " << len << endl;
length = len;
}
void Line::setLength( double len ) {
length = len;
}
double Line::getLength( void ) {
return length;
}
// Main function for the program
int main() {
Line line(10.0);
// get initially set length.
cout << "Length of line : " << line.getLength() <<endl;
// set line length again
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
return 0;
}
Line::Line( double len): length(len) {
cout << "Object is being created, length = " << len << endl;
}

If for a class A, you have multiple fields X, Y, Z, etc., to be initialized, then use can use same syntax and separate the fields by comma as follows −

A::A( double a, double b, double c): X(a), Y(b), Z(c) {
....
}

b) Class Destructor:

// Member functions definitions including constructor
Line::Line(void) {
cout << "Object is being created" << endl;
}
Line::~Line(void) {
cout << "Object is being deleted" << endl;
}

c) Copy Constructor:

classname (const classname &obj) {
// body of constructor
}

For example:

Live Demo
#include <iostream>
using namespace std;
class Line {
public:
int getLength( void );
Line( int len ); // simple constructor
Line( const Line &obj); // copy constructor
~Line(); // destructor
private:
int *ptr;
};
// Member functions definitions including constructor
Line::Line(int len) {
cout << "Normal constructor allocating ptr" << endl;
// allocate memory for the pointer;
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj) {
cout << "Copy constructor allocating ptr." << endl;
ptr = new int;
*ptr = *obj.ptr; // copy the value
}
Line::~Line(void) {
cout << "Freeing memory!" << endl;
delete ptr;
}
int Line::getLength( void ) {
return *ptr;
}
void display(Line obj) {
cout << "Length of line : " << obj.getLength() <<endl;
}
void display2(Line & obj) {
cout << "Length of line : " << obj.getLength() <<endl;
}
// Main function for the program
int main() {
cout << "creating object line1" << endl;
Line line1(10);
cout << "\ncreating object line2" << endl;
Line line2 = line1; // This also calls copy constructor
cout << "\ncalling display(line1)" << endl;
display(line1);
cout << "\ncalling display(line2)" << endl;
display(line2);
cout << "\ncalling display2(line2)" << endl;
display2(line2);
return 0;
}

Alt text

7) C++ Friend Functions

class Box {
double width;
public:
double length;
friend void printWidth( Box box );
void setWidth( double wid );
};

To declare all member functions of class ClassTwo as friends of class ClassOne, place a following declaration in the definition of class ClassOne

friend class ClassTwo;

#include <iostream>
using namespace std;
class Box {
double width;
public:
friend void printWidth( Box box );
void setWidth( double wid );
};
// Member function definition
void Box::setWidth( double wid ) {
width = wid;
}
// Note: printWidth() is not a member function of any class.
void printWidth( Box box ) {
/* Because printWidth() is a friend of Box, it can
directly access any member of this class */
cout << "Width of box : " << box.width <<endl;
}
// Main function for the program
int main() {
Box box;
// set box width without member function
box.setWidth(10.0);
// Use friend function to print the wdith.
printWidth( box );
return 0;
}

When the above code is compiled and executed, it produces the following result −

Width of box : 10

8) C++ Inline Functions

inline int Max(int x, int y) {
return (x > y)? x : y;
}

9) C++ this Pointer

#include <iostream>
using namespace std;
class Box {
public:
// Constructor definition
Box(double l = 2.0, double b = 2.0, double h = 2.0) {
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume() {
return length * breadth * height;
}
int compare(Box box) {
return this->Volume() > box.Volume();
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main(void) {
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
if(Box1.compare(Box2)) {
cout << "Box2 is smaller than Box1" <<endl;
} else {
cout << "Box2 is equal to or larger than Box1" <<endl;
}
return 0;
}

When the above code is compiled and executed, it produces the following result −

Constructor called.
Constructor called.
Box2 is equal to or larger than Box1

10) Static Members of a C++ Class

a) Static Data Members:

#include <iostream>
using namespace std;
class Box {
public:
static int objectCount;
// Constructor definition
Box(double l = 2.0, double b = 2.0, double h = 2.0) {
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
// Increase every time object is created
objectCount++;
}
double Volume() {
return length * breadth * height;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
// Initialize static member of class Box
int Box::objectCount = 0;
int main(void) {
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
// Print total number of objects.
cout << "Total objects: " << Box::objectCount << endl;
// Or we can access the static data via an object;
cout << "Total objects: " << Box2.objectCount << endl;
return 0;
}

When the above code is compiled and executed, it produces the following result −

Constructor called.
Constructor called.
Total objects: 2
// Print total number of objects.
cout << "Total objects: " << Box::objectCount << endl;
// Or we can access the static data via an object;
cout << "Total objects: " << Box2.objectCount << endl;

b) Static Function Members:

#include <iostream>
using namespace std;
class Box {
public:
static int objectCount;
// Constructor definition
Box(double l = 2.0, double b = 2.0, double h = 2.0) {
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
// Increase every time object is created
objectCount++;
}
double Volume() {
return length * breadth * height;
}
// static member function
static int getCount() {
return objectCount;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
// Initialize static member of class Box
int Box::objectCount = 0;
int main(void) {
// Print total number of objects before creating object.
cout << "Inital Stage Count: " << Box::getCount() << endl;
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
// Print total number of objects after creating object.
cout << "Final Stage Count: " << Box::getCount() << endl;
return 0;
}
Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2

21. C++ Inheritance

One of the most important concepts in object-oriented programming is that of inheritance. Inheritance allows us to define a class in terms of another class, which makes it easier to create and maintain an application. This also provides an opportunity to reuse the code functionality and fast implementation time.

When creating a class, instead of writing completely new data members and member functions, the programmer can designate that the new class should inherit the members of an existing class. This existing class is called the base class, and the new class is referred to as the derived class.

The idea of inheritance implements is a relationship. For example, mammal IS-A animal, dog IS-A mammal hence dog IS-A animal as well and so on.

1) Base and Derived Classes

A class can be derived from more than one classes, which means it can inherit data and functions from multiple base classes. To define a derived class, we use a class derivation list to specify the base class(es). A class derivation list names one or more base classes and has the form −

class derived-class: access-specifier base-class

Consider a base class Shape and its derived class Rectangle as follows −

#include <iostream>
using namespace std;
// Base class
class Shape {
public:
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
protected:
int width;
int height;
};
// Derived class
class Rectangle: public Shape {
public:
int getArea() {
return (width * height);
}
};
int main(void) {
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
return 0;
}
Total area: 35

b) Access Control and Inheritance

Access public protected private
Same class yes yes yes
Derived classes yes yes no
Outside classes yes no no

c) Type of Inheritance

When deriving a class from a base class, the base class may be inherited through public, protected or private inheritance. The type of inheritance is specified by the access-specifier as explained above.

We hardly use protected or private inheritance, but public inheritance is commonly used. While using different type of inheritance, following rules are applied −

d) Multiple Inheritance

#include <iostream>
using namespace std;
// Base class Shape
class Shape {
public:
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
protected:
int width;
int height;
};
// Base class PaintCost
class PaintCost {
public:
int getCost(int area) {
return area * 70;
}
};
// Derived class
class Rectangle: public Shape, public PaintCost {
public:
int getArea() {
return (width * height);
}
};
int main(void) {
Rectangle Rect;
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
// Print the total cost of painting
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0;
}
Total area: 35
Total paint cost: $2450

22. C++ Overloading (Operator and Function)

C++ allows you to specify more than one definition for a function name or an operator in the same scope, which is called function overloading and operator overloading respectively.

An overloaded declaration is a declaration that is declared with the same name as a previously declared declaration in the same scope, except that both declarations have different arguments and obviously different definition (implementation).

When you call an overloaded function or operator, the compiler determines the most appropriate definition to use, by comparing the argument types you have used to call the function or operator with the parameter types specified in the definitions. The process of selecting the most appropriate overloaded function or operator is called overload resolution.

1) Function Overloading in C++

void print(int i) {
cout << "Printing int: " << i << endl;
}
void print(double f) {
cout << "Printing float: " << f << endl;
}
void print(char* c) {
cout << "Printing character: " << c << endl;
}
// Call print to print integer
print(5);
// Call print to print float
print(500.263);
// Call print to print character
print("Hello C++");

2) Operators Overloading in C++

You can redefine or overload most of the built-in operators available in C++. Thus, a programmer can use operators with user-defined types as well.

Overloaded operators are functions with special names: the keyword "operator" followed by the symbol for the operator being defined. Like any other function, an overloaded operator has a return type and a parameter list.

Box operator+(const Box&);

declares the addition operator that can be used to add two Box objects and returns final Box object. Most overloaded operators may be defined as ordinary non-member functions or as class member functions. In case we define above function as non-member function of a class then we would have to pass two arguments for each operand as follows −

Box operator+(const Box&, const Box&);

Following is the example to show the concept of operator overloading using a member function. Here an object is passed as an argument whose properties will be accessed using this object, the object which will call this operator can be accessed using this operator as explained below −

class Box {
public:
double getVolume(void) {
return length * breadth * height;
}
void setLength( double len ) {
length = len;
}
void setBreadth( double bre ) {
breadth = bre;
}
void setHeight( double hei ) {
height = hei;
}
// Overload + operator to add two Box objects.
Box operator+(const Box& b) {
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};

Then we can call it:

Box Box1; // Declare Box1 of type Box
Box Box2; // Declare Box2 of type Box
Box Box3; // Declare Box3 of type Box
...
// Add two object as follows:
Box3 = Box1 + Box2;

23. Polymorphism (多态性) in C++: virtual function

1) A Negative Example: static linkage or early binding

include <iostream>
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a = 0, int b = 0){
width = a;
height = b;
}
int area() {
cout << "Parent class area :" <<endl;
return 0;
}
};
class Rectangle: public Shape {
public:
Rectangle( int a = 0, int b = 0):Shape(a, b) { }
int area () {
cout << "Rectangle class area :" <<endl;
return (width * height);
}
};
class Triangle: public Shape {
public:
Triangle( int a = 0, int b = 0):Shape(a, b) { }
int area () {
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
};
// Main function for the program
int main() {
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// store the address of Rectangle
shape = &rec;
// call rectangle area.
shape->area();
// store the address of Triangle
shape = &tri;
// call triangle area.
shape->area();
return 0;
}
Parent class area :
Parent class area :
class Shape {
protected:
int width, height;
public:
Shape( int a = 0, int b = 0) {
width = a;
height = b;
}
virtual int area() {
cout << "Parent class area :" <<endl;
return 0;
}
};
Rectangle class area
Triangle class area

2) Virtual Function: dynamic linkage or late binding

3) Pure Virtual Functions

class Shape {
protected:
int width, height;
public:
Shape(int a = 0, int b = 0) {
width = a;
height = b;
}
// pure virtual function
virtual int area() = 0;
};

24. Data Abstraction in C++

1) What is Data Abstraction?

2) Access Labels Enforce Abstraction

In C++, we use access labels to define the abstract interface to the class. A class may contain zero or more access labels −

3) Benefits of Data Abstraction

4) Designing Strategy

25. Data Encapsulation in C++

All C++ programs are composed of the following two fundamental elements −

Encapsulation is an Object Oriented Programming concept that binds together the data and functions that manipulate the data, and that keeps both safe from outside interference and misuse. Data encapsulation led to the important OOP concept of data hiding.

Data encapsulation is a mechanism of bundling the data, and the functions that use them and data abstraction is a mechanism of exposing only the interfaces and hiding the implementation details from the user.

C++ supports the properties of encapsulation and data hiding through the creation of user-defined types, called classes. We already have studied that a class can contain private, protected and public members. By default, all items defined in a class are private. For example −

class Box {
public:
double getVolume(void) {
return length * breadth * height;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};

The variables length, breadth, and height are private. This means that they can be accessed only by other members of the Box class, and not by any other part of your program. This is one way encapsulation is achieved.

To make parts of a class public (i.e., accessible to other parts of your program), you must declare them after the public keyword. All variables or functions defined after the public specifier are accessible by all other functions in your program.

Making one class a friend of another exposes the implementation details and reduces encapsulation. The ideal is to keep as many of the details of each class hidden from all other classes as possible.

Designing Strategy: Most of us have learnt to make class members private by default unless we really need to expose them. That’s just good encapsulation. This is applied most frequently to data members, but it applies equally to all members, including virtual functions.

26. Interfaces in C++ (Abstract Classes VS Concrete Classes)

An interface describes the behavior or capabilities of a C++ class without committing to a particular implementation of that class.

The C++ interfaces are implemented using abstract classes and these abstract classes should not be confused with data abstraction which is a concept of keeping implementation details separate from associated data.

A class is made abstract by declaring at least one of its functions as pure virtual function. A pure virtual function is specified by placing "= 0" in its declaration as follows −

class Box {
public:
// pure virtual function
virtual double getVolume() = 0;
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};

The purpose of an abstract class (often referred to as an ABC) is to provide an appropriate base class from which other classes can inherit. Abstract classes cannot be used to instantiate objects and serves only as an interface. Attempting to instantiate an object of an abstract class causes a compilation error.

Thus, if a subclass of an ABC needs to be instantiated, it has to implement each of the virtual functions, which means that it supports the interface declared by the ABC. Failure to override a pure virtual function in a derived class, then attempting to instantiate objects of that class, is a compilation error.

Classes that can be used to instantiate objects are called concrete classes.

27. C++ Exception Handling

An exception is a problem that arises during the execution of a program. A C++ exception is a response to an exceptional circumstance that arises while a program is running, such as an attempt to divide by zero.

Exceptions provide a way to transfer control from one part of a program to another. C++ exception handling is built upon three keywords: try, catch, and throw.

1) try/catch block

Assuming a block will raise an exception, a method catches an exception using a combination of the try and catch keywords. A try/catch block is placed around the code that might generate an exception. Code within a try/catch block is referred to as protected code, and the syntax for using try/catch as follows −

try {
// protected code
} catch( ExceptionName e1 ) {
// catch block
} catch( ExceptionName e2 ) {
// catch block
} catch( ExceptionName eN ) {
// catch block
}

You can list down multiple catch statements to catch different type of exceptions in case your try block raises more than one exception in different situations.

#include <iostream>
#include <vector>
int main() {
try {
std::cout << "Throwing an integer exception...\n";
throw 42;
} catch (int i) {
std::cout << " the integer exception was caught, with value: " << i << '\n';
}
try {
std::cout << "Creating a vector of size 5... \n";
std::vector<int> v(5);
std::cout << "Accessing the 11th element of the vector...\n";
std::cout << v.at(10); // vector::at() throws std::out_of_range
} catch (const std::exception& e) { // caught by reference to base
std::cout << " a standard exception was caught, with message '"
<< e.what() << "'\n";
}
}

Alt text

2) Define New Exceptions

You can define your own exceptions by inheriting and overriding exception class functionality. Following is the example, which shows how you can use std::exception class to implement your own exception in standard way −

#include <iostream>
#include <exception>
using namespace std;
class MyException : public exception {
public:
const char * what () const throw () {
return "C++ Exception";
}
};
int main() {
try {
throw MyException();
} catch(MyException& e) {
std::cout << "MyException caught" << std::endl;
std::cout << e.what() << std::endl;
} catch(std::exception& e) {
//Other errors
}
}
MyException caught
C++ Exception

28. C++ Dynamic Memory

A good understanding of how dynamic memory really works in C++ is essential to becoming a good C++ programmer. Memory in your C++ program is divided into two parts −

Many times, you are not aware in advance how much memory you will need to store particular information in a defined variable and the size of required memory can be determined at run time.

You can allocate memory at run time within the heap for the variable of a given type using a special operator in C++ which returns the address of the space allocated. This operator is called new operator.

If you are not in need of dynamically allocated memory anymore, you can use delete operator, which de-allocates memory that was previously allocated by new operator.

1) new and delete Operators

There is following generic syntax to use new operator to allocate memory dynamically for any data-type.

new data-type;

Here, data-type could be any built-in data type including an array or any user defined data types include class or structure. Let us start with built-in data types. For example we can define a pointer to type double and then request that the memory be allocated at execution time. We can do this using the new operator with the following statements −

double* pvalue = NULL; // Pointer initialized with null
pvalue = new double; // Request memory for the variable

The memory may not have been allocated successfully, if the free store had been used up. So it is good practice to check if new operator is returning NULL pointer and take appropriate action as below −

double* pvalue = NULL;
if( !(pvalue = new double )) {
cout << "Error: out of memory." <<endl;
exit(1);
}

The malloc() function from C, still exists in C++, but it is recommended to avoid using malloc() function. The main advantage of new over malloc() is that new doesn’t just allocate memory, it constructs objects which is prime purpose of C++.

At any point, when you feel a variable that has been dynamically allocated is not anymore required, you can free up the memory that it occupies in the free store with the ‘delete’ operator as follows −

delete pvalue; // Release memory pointed to by pvalue

2) Dynamic Memory Allocation for 1D Arrays

char* pvalue = new char[20];
double * pVal = new double [30];
delete [] pvalue; // Delete array pointed to by pvalue
delete [] pVal;

3) Dynamic Memory Allocation for multi-dimensional array:

1) Using Single Pointer:

#include <iostream>
// M x N matrix
#define M 4
#define N 5
// Dynamically Allocate Memory for 2D Array in C++
int main(){
// dynamically allocate memory of size M*N
int * A = new int[M * N];
// assign values to allocated memory
for (int i = 0; i < M; i++)
for (int j = 0; j < N; j++)
*(A + i*N + j) = rand() % 100;
// print the 2D array
for (int i = 0; i < M; i++){
for (int j = 0; j < N; j++)
std::cout << *(A + i*N + j) << " "; // or (A + i*N)[j])
std::cout << std::endl;
}
// deallocate memory
delete[] A;
return 0;
}

2) Using double pointer:

see: https://stackoverflow.com/questions/936687/how-do-i-declare-a-2d-array-in-c-using-new

int ** A = new int [ROW_NUM][COL_NUM]; // Wrong!!!
int ** ary = new int*[ROW_NUM];
for(int i = 0; i < ROW_NUM; ++i) {
ary[i] = new int[COL_NUM];
}

Alt text

for(int i = 0; i < ROW_NUM; ++i) {
delete [] ary[i];
}
delete [] ary;
int *ary = new int[ROW_NUM*COL_NUM];
// ary[i][j] is then rewritten as
ary[i*ROW_NUM + j]

3) Dynamic Memory Allocation for Objects

Objects are no different from simple data types. For example, consider the following code where we are going to use an array of objects to clarify the concept −

#include <iostream>
using namespace std;
class Box {
public:
Box() {
cout << "Constructor called!" <<endl;
}
~Box() {
cout << "Destructor called!" <<endl;
}
};
int main() {
Box* myBoxArray = new Box[4];
delete [] myBoxArray; // Delete array
return 0;
}

If you were to allocate an array of four Box objects, the Simple constructor would be called four times and similarly while deleting these objects, destructor will also be called same number of times.

Alt text

29. C++ Templates

Templates are the foundation of generic programming, which involves writing code in a way that is independent of any particular type.

A template is a blueprint or formula for creating a generic class or a function. The library containers like iterators and algorithms are examples of generic programming and have been developed using template concept.

There is a single definition of each container, such as vector, but we can define many different kinds of vectors for example, vector <int> or vector <string>.

You can use templates to define functions as well as classes, let us see how they work −

1) Function Template

The general form of a template function definition is shown here −

template <class type> ret-type func-name(parameter list) {
// body of function
}

Here, type is a placeholder name for a data type used by the function. This name can be used within the function definition.

The following is the example of a function template that returns the maximum of two values −

template <typename T>
inline T const& Max (T const& a, T const& b) {
return a < b ? b:a;
}
int i = 39;
int j = 20;
cout << "Max(i, j): " << Max(i, j) << endl;
double f1 = 13.5;
double f2 = 20.7;
cout << "Max(f1, f2): " << Max(f1, f2) << endl;

2) Class Template

Just as we can define function templates, we can also define class templates. The general form of a generic class declaration is shown here −

template <class type> class class-name {
.
.
.
}

Here, type is the placeholder type name, which will be specified when a class is instantiated. You can define more than one generic data type by using a comma-separated list.

Following is the example to define class Stack<> and implement generic methods to push and pop the elements from the stack −

#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>
using namespace std;
template <class T>
class Stack {
private:
vector<T> elems; // elements
public:
void push(T const&); // push element
void pop(); // pop element
T top() const; // return top element
bool empty() const { // return true if empty.
return elems.empty();
}
};
template <class T>
void Stack<T>::push (T const& elem) {
// append copy of passed element
elems.push_back(elem);
}
template <class T>
void Stack<T>::pop () {
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// remove last element
elems.pop_back();
}
template <class T>
T Stack<T>::top () const {
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// return copy of last element
return elems.back();
}
int main() {
try {
Stack<int> intStack; // stack of ints
Stack<string> stringStack; // stack of strings
// manipulate int stack
intStack.push(7);
cout << intStack.top() <<endl;
// manipulate string stack
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
} catch (exception const& ex) {
cerr << "Exception: " << ex.what() <<endl;
return -1;
}
}
7
hello
Exception: Stack<>::pop(): empty stack

30. C++ Preprocessor

There are number of preprocessor directives supported by C++ like #include, #define, #if, #else, #line, etc. Let us see important directives −

1) The #define Preprocessor

#define PI 3.14159

2) Function-Like Macros

#include <iostream>
using namespace std;
#define MIN(a,b) (((a)<(b)) ? a : b)
int main () {
int i, j;
i = 100;
j = 30;
cout <<"The minimum is " << MIN(i, j) << endl;
return 0;
}

3) Conditional Compilation

#ifndef NULL
#define NULL 0
#endif
#ifdef DEBUG
cerr <<"Variable x = " << x << endl;
#endif
#if 0
code prevented from compiling
#endif

4) Predefined C++ Macros

Alt text

Alt text

Alt text

31. List the types of inheritance supported in C++

1) Single Inheritance

Alt text

// sub class derived from two base classes
class Car: public Vehicle{
...
};

2) Multiple Inheritance

Alt text

// sub class derived from two base classes
class Car: public Vehicle, public FourWheeler {
...
};

3) Multilevel Inheritance

Alt text

4) Hierarchical Inheritance

Alt text

5) Hybrid (Virtual) Inheritance

Alt text

31. How to catch all the exceptions in C++?

#include <iostream>
using namespace std;
void func(int a) {
try {
if(a==0) throw 23.33;
if(a==1) throw 's';
} catch(...) {
cout << "Caught Exception!\n";
}
}
int main() {
func(0);
func(1);
return 0;
}
Caught Exception!
Caught Exception!

32. Basic Concepts

#include <iostream> //包含头文件iostream
using namespace std; //使用命名空间std
int main( ){
cout << "This is a C++ program.";
return 0;
}

33. OOPs Concepts in C++

34. Three dots ... in C++ function

https://en.cppreference.com/w/cpp/language/variadic_arguments
https://en.cppreference.com/w/cpp/utility/variadic

In C++, we can write functions with three dots (…) as parameter. This will make that function to support variable number of arguments. The va_arg, va_end, and va_start macros (defined in cstdarg.h file) provide access to function arguments when the number of arguments is variable.

Here the three dots is called a trailing ...;

Alt text

35. Memory layout of C programs

A typical memory representation of C program consists of following sections.

  1. Text segment

  2. Initialized data segment

  3. Uninitialized data segment

  4. Stack

  5. Heap

Alt text

1) Text segment (aka code segment)

2) Initialized data segment (aka Data Segment):

3) Uninitialized data segment

Uninitialized data segment, often called the “bss” segment, named after an ancient assembler operator that stood for “block started by symbol”. Data in this segment is initialized by the kernel to arithmetic 0 before the program starts executing.

uninitialized data starts at the end of the data segment and contains all global variables and static variables that are initialized to zero or do not have explicit initialization in source code.

4) Stack

5) Heap

6) Examples

a) Check the following simple C program:

Alt text

Alt text

b) add one global variable in program:

Alt text

Alt text

c) add one static variable which is also stored in bss:

Alt text

Alt text

d) initialize the static variable which will then be stored in Data Segment (DS):

Alt text

Alt text

e) initialize the global variable which will then be stored in Data Segment (DS):

Alt text

Alt text

36. Static Variables in C

Static variables have a property of preserving their value even after they are out of their scope! Hence, static variables preserve their previous value in their previous scope and are not initialized again in the new scope.

Syntax: static data_type var_name = var_value;
E.g., static int count = 0;

Following are some interesting facts about static variables in C.

1) The life of a static variable:

#include<stdio.h>
int fun_static() {
static int count = 0;
count++;
return count;
}
int fun() {
int count = 0;
count++;
return count;
}
int main() {
// prints “1 2”
printf("%d ", fun_static());
printf("%d ", fun_static());
// prints “1 1”
printf("%d ", fun());
printf("%d ", fun());
return 0;
}

2) Static variables are allocated memory in data segment

Alt text

3) Static variables initialized as 0

4) static variables can only be initialized using constant literals in C

Alt text

Alt text

Alt text

Alt text

Alt text

Alt text

5) Static global variables and functions in C/C++

static int fun(void)
{
printf("I am a static function ");
}
/* Inside file1.c */
static void fun1(void)
{
puts("fun1 called");
}
/* Inside file2.c */
int main(void)
{
fun1();
getchar();
return 0;
}

Now, if we compile the above code with command “gcc file2.c file1.c”, we get the error “undefined reference to fun1” . This is because fun1() is declared static in file1.c and cannot be used in file2.c.

6) Static variables should not be declared inside structure

37 Static Keyword in C++: Static Variables and Static Variables of Class

Static keyword has different meanings when used with different types. We can use static keyword with:

1) Static Variables

a) Static variables in a Function:

When a variable is declared as static, space for it gets allocated for the lifetime of the program. Even if the function is called multiple times, space for the static variable is allocated only once and the value of variable in the previous call gets carried through the next function call.

b) Static variables in a class:

2) Static Members of Class

a) Class objects as static:

Just like variables, objects also when declared as static have a scope till the lifetime of program.

// CPP program to illustrate
// class objects as static
#include<iostream>
using namespace std;
class GfG {
int i = 0;
public:
GfG(){
i = 0;
cout << "Inside Constructor\n";
}
~GfG(){
cout << "Inside Destructor\n";
}
};
int main(){
int x = 0;
if (x==0){
static GfG obj;
}
cout << "End of main\n";
}
Inside Constructor
End of main
Inside Destructor

You can see the destructor is invoked after the end of main. This happened because the scope of static object is through out the life time of the program.

b) Static functions in a class:

// C++ program to demonstrate static
// member function in a class
#include<iostream>
using namespace std;
class GfG {
public:
// static member function
static void printMsg(){
cout<<"Welcome to GfG!";
}
};
// main function
int main(){
// invoking a static member function
GfG::printMsg();
}
Welcome to GfG!
#include<iostream>
using namespace std;
class Test {
public:
// Error: Virtual member functions cannot be static
virtual static void fun() { }
};
#include<iostream>
using namespace std;
class Test {
public:
// Error: Static member function cannot be const
static void fun() const { }
};

38 C++ keywords: const

1) Const, constexpr, and symbolic constants

a) make a variable constant:

Alt text

const int value1 = 5; // copy initialization
const int value2(7); // direct initialization
const int value3 { 9 }; // uniform initialization (C++11)
void printInteger(const int myValue){
std::cout << myValue;
}

b) make a function parameter const

c) Runtime vs compile time constants

C++ actually has two different kinds of constants.

const double gravity { 9.8 };
std::cout << "Enter your age: ";
int age{};
std::cin >> age;
const int usersAge { age }; // usersAge can not be changed

When you declare a const variable, the compiler will implicitly keep track of whether it’s a runtime or compile-time constant.

d) constexpr (c++ 11)

To help provide more specificity, C++11 introduced the keyword constexpr, which ensures that a constant must be a compile-time constant:

constexpr double gravity { 9.8 }; // ok, the value of 9.8 can be resolved at compile-time
constexpr int sum { 4 + 5 }; // ok, the value of 4 + 5 can be resolved at compile-time
std::cout << "Enter your age: ";
int age{};
std::cin >> age;
constexpr int myAge { age }; // NOT okay, age can not be resolved at compile-time

Alt text

e) Symbolic constants:

A symbolic constant is a name given to a constant literal value. There are two ways to declare symbolic constants in C++. One of them is good, and one of them is not. We’ll show you both.

Alt text

Alt text

f) Using symbolic constants throughout a multi-file program:

In many applications, a given symbolic constant needs to be used throughout your code (not just in one location). Instead of redefining these every time they are needed, it’s better to declare them once in a central location and use them wherever needed. That way, if you ever need to change them, you only need to change them in one place.

There are multiple ways to facilitate this within C++, but the following is probably easiest:

For example:

#ifndef CONSTANTS_H
#define CONSTANTS_H
// define your own namespace to hold constants
namespace constants {
constexpr double pi { 3.14159 };
constexpr double avogadro { 6.0221413e23 };
constexpr double my_gravity { 9.2 }; // m/s^2 -- gravity is light on this planet
// ... other related constants
}
#endif
#ifndef CONSTANTS_H
#define CONSTANTS_H
// define your own namespace to hold constants
namespace constants{
inline constexpr double pi { 3.14159 }; // inline constexpr is C++17 or newer only
inline constexpr double avogadro { 6.0221413e23 };
inline constexpr double my_gravity { 9.2 }; // m/s^2 -- gravity is light on this planet
// ... other related constants
}
#endif
#include "constants.h"
#include <iostream>
int main(){
std::cout << "Enter a radius: ";
int radius{};
std::cin >> radius;
double circumference { 2.0 * radius * constants::pi };
std::cout << "The circumference is: " << circumference << '\n';
return 0;
}

2) Const class objects and member functions

a) Const classes:

Instantiated class objects can also be made const by using the const keyword. Initialization is done via class constructors:

const Date date1; // initialize using default constructor
const Date date2(2020, 10, 16); // initialize using parameterized constructor
const Date date3 { 2020, 10, 16 }; // initialize using parameterized constructor (C++11)

Once a const class object has been initialized via constructor, any attempt to modify the member variables of the object is disallowed, as it would violate the constness of the object. This includes both changing member variables directly (if they are public), or calling member functions that set the value of member variables. Consider the following class:

class Something
{
public:
int m_value;
Something(): m_value{0} { }
void setValue(int value) { m_value = value; }
int getValue() { return m_value ; }
};
int main()
{
const Something something{}; // calls default constructor
something.m_value = 5; // compiler error: violates const
something.setValue(5); // compiler error: violates const
return 0;
}

Both of the above lines involving variable something are illegal because they violate the constness of something by either attempting to change a member variable directly, or by calling a member function that attempts to change a member variable.

Just like with normal variables, you’ll generally want to make your class objects const when you need to ensure they aren’t modified after creation.

b) Const member functions

Now, consider the following line of code:

std::cout << something.getValue();

Perhaps surprisingly, this will also cause a compile error, even though getValue() doesn’t do anything to change a member variable! It turns out that const class objects can only explicitly call const member functions, and getValue() has not been marked as a const member function.

A const member function is a member function that guarantees it will not modify the object or call any non-const member functions (as they may somehow modify the object).

To make getValue() a const member function, we simply append the const keyword to the function prototype, after the parameter list, but before the function body:

class Something {
public:
int m_value;
Something(): m_value{0} { }
void resetValue() { m_value = 0; }
void setValue(int value) { m_value = value; }
int getValue() const { return m_value; } // note addition of const keyword after parameter list, but before function body
};

Now getValue() has been made a const member function, which means we can call it on any const objects.

For member functions defined outside of the class definition, the const keyword must be used on both the function prototype in the class definition and on the function definition:

class Something
{
public:
int m_value;
Something(): m_value{0} { }
void resetValue() { m_value = 0; }
void setValue(int value) { m_value = value; }
int getValue() const; // note addition of const keyword here
};
int Something::getValue() const // and here
{
return m_value;
}

Furthermore, any const member function that attempts to change a member variable or call a non-const member function will cause a compiler error to occur. For example:

class Something
{
public:
int m_value ;
void resetValue() const { m_value = 0; } // compile error, const functions can't change member variables.
};

In this example, resetValue() has been marked as a const member function, but it attempts to change m_value. This will cause a compiler error.

Note that constructors cannot be marked as const. This is because constructors need to be able to initialize their member variables, and a const constructor would definitely not be able to do so. Consequently, the language disallows const constructors.

Alt text

c) Const references

Although instantiating const class objects is one way to create const objects, a more common way is by passing an object to a function by const reference.

In the lesson on passing arguments by reference, we covered the merits of passing class arguments by const reference instead of by value.

To recap, passing a class argument by value causes a copy of the class to be made (which is slow, and will call the class constructor) – most of the time, we don’t need a copy, a reference to the original argument works just fine, and is more performant because it avoids the needless copy. We typically make the reference const in order to ensure the function does not inadvertently change the argument, and to allow the function to work with R-values (e.g. literals), which can be passed as const references, but not non-const references.

Can you figure out what’s wrong with the following code?

#include <iostream>
class Date
{
private:
int m_year;
int m_month;
int m_day;
public:
Date(int year, int month, int day)
{
setDate(year, month, day);
}
void setDate(int year, int month, int day)
{
m_year = year;
m_month = month;
m_day = day;
}
int getYear() { return m_year; }
int getMonth() { return m_month; }
int getDay() { return m_day; }
};
// note: We're passing date by const reference here to avoid making a copy of date
void printDate(const Date &date)
{
std::cout << date.getYear() << '/' << date.getMonth() << '/' << date.getDay() << '\n';
}
int main()
{
Date date{2016, 10, 16};
printDate(date);
return 0;
}

The answer is that inside of the printDate function, date is treated as a const object. And with that const date, we’re calling functions getYear(), getMonth(), and getDay(), which are all non-const. Since we can’t call non-const member functions on const objects, this will cause a compile error.

The fix is simple: make getYear(), getMonth(), and getDay() const:

class Date
{
private:
int m_year;
int m_month;
int m_day;
public:
Date(int year, int month, int day)
{
setDate(year, month, day);
}
// setDate() cannot be const, modifies member variables
void setDate(int year, int month, int day)
{
m_year = year;
m_month = month;
m_day = day;
}
// The following getters can all be made const
int getYear() const { return m_year; }
int getMonth() const { return m_month; }
int getDay() const { return m_day; }
};

Now in function printDate(), const object date will be able to successfully call getYear(), getMonth(), and getDay().

d) Overloading const and non-const function

Finally, although it is not done very often, it is possible to overload a function in such a way to have a const and non-const version of the same function:

#include <string>
class Something
{
private:
std::string m_value;
public:
Something(const std::string &value=""): m_value{ value } {}
const std::string& getValue() const { return m_value; } // getValue() for const objects
std::string& getValue() { return m_value; } // getValue() for non-const objects
};

The const version of the function will be called on any const objects, and the non-const version will be called on any non-const objects:

int main()
{
Something something{};
something.getValue() = "Hi"; // calls non-const getValue();
const Something something2{};
something2.getValue(); // calls const getValue();
return 0;
}

Overloading a function with a const and non-const version is typically done when the return value needs to differ in constness. In the example above, the non-const version of getValue() will only work with non-const objects, but is more flexible in that we can use it to both read and write m_value (which we do by assigning the string “Hi”).

The const version of getValue() will work with either const or non-const objects, but returns a const reference, to ensure we can’t modify the const object’s data.

This works because the const-ness of the function is considered part of the function’s signature, so a const and non-const function which differ only in const-ness are considered distinct.

39 When do we use Initializer List in C++?

Syntax: e.g., Point(int i = 0, int j = 0):x(i), y(j) {} and Point(int i = 0, int j = 0):x{i}, y{j} {} // C++11

#include<iostream>
using namespace std;
class Point {
private:
int x;
int y;
public:
Point(int i = 0, int j = 0):x(i), y(j) {}
/* The above use of Initializer list is optional as the
constructor can also be written as:
Point(int i = 0, int j = 0) {
x = i;
y = j;
}
*/
int getX() const {return x;}
int getY() const {return y;}
};
int main() {
Point t1(10, 15);
cout<<"x = "<<t1.getX()<<", ";
cout<<"y = "<<t1.getY();
return 0;
}
/* OUTPUT:
x = 10, y = 15
*/

Let’s talk about the details for each of them.

1) For initialization of non-static const data members

#include<iostream>
using namespace std;
class Test {
const int t;
public:
Test(int tt):t(tt) {} //Initializer list must be used
int getT() { return t; }
};
int main() {
Test t1(10);
cout<<t1.getT();
return 0;
}
/* OUTPUT:
10
*/

2) For initialization of reference members:

// Initialization of reference data members
#include<iostream>
using namespace std;
class Test {
int &t;
public:
Test(int &tt):t(tt) {} //Initializer list must be used
int getT() { return t; }
};
int main() {
int x = 20;
Test t1(x);
cout<<t1.getT()<<endl;
x = 30;
cout<<t1.getT()<<endl;
return 0;
}
/* OUTPUT:
20
30
*/

3) For initialization of member objects which do not have default constructor:

#include <iostream>
using namespace std;
class A {
int i;
public:
A(int );
};
A::A(int arg) {
i = arg;
cout << "A's Constructor called: Value of i: " << i << endl;
}
// Class B contains object of A
class B {
A a;
public:
B(int );
};
B::B(int x):a(x) { //Initializer list must be used
cout << "B's Constructor called";
}
int main() {
B obj(10);
return 0;
}
/* OUTPUT:
A's Constructor called: Value of i: 10
B's Constructor called
*/

If class A had both default and parameterized constructors, then Initializer List is not must if we want to initialize “a” using default constructor, but it is must to initialize “a” using parameterized constructor.

4) For initialization of base class members : Like point 3, the parameterized constructor of the base class can only be called using Initializer List.

#include <iostream>
using namespace std;
class A {
int i;
public:
A(int );
};
A::A(int arg) {
i = arg;
cout << "A's Constructor called: Value of i: " << i << endl;
}
// Class B is derived from A
// The default inheritance model in C++ is private inheritance for classes and public for structs.
class B: A {
public:
B(int );
};
B::B(int x):A(x) { //Initializer list must be used
cout << "B's Constructor called";
}
int main() {
B obj(10);
return 0;
}

5) When constructor’s parameter name is same as data member

#include <iostream>
using namespace std;
class A {
int i;
public:
A(int );
int getI() const { return i; }
};
A::A(int i):i(i) { } // Either Initializer list or this pointer must be used
/* The above constructor can also be written as
A::A(int i) {
this->i = i;
}
*/
int main() {
A a(10);
cout<<a.getI();
return 0;
}
/* OUTPUT:
10
*/

6) For Performance reasons

// Without Initializer List
class MyClass {
Type variable;
public:
MyClass(Type a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
variable = a;
}
};

Here compiler follows following steps to create an object of type MyClass

Now consider the same code with MyClass() constructor with Initializer List

// With Initializer List
class MyClass {
Type variable;
public:
MyClass(Type a):variable(a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
}
};

40. Passing arguments by reference

While pass by value is suitable in many cases, it has a couple of limitations.

1) Pass by reference

To pass a variable by reference, we simply declare the function parameters as references rather than as normal variables:

void addOne(int &ref) // ref is a reference variable
{
ref = ref + 1;
}

When the function is called, ref will become a reference to the argument. Since a reference to a variable is treated exactly the same as the variable itself, any changes made to the reference are passed through to the argument!

The following example shows this in action:

void addOne(int &ref)
{
ref = ref + 1;
}
int main()
{
int value = 5;
cout << "value = " << value << '\n';
addOne(value);
cout << "value = " << value << '\n';
return 0;
}

This program is the same as the one we used for the pass by value example, except foo’s parameter is now a reference instead of a normal variable. When we call addOne(value), ref becomes a reference to main’s value variable. This snippet produces the output:

value = 5
value = 6

As you can see, the function changed the value of the argument from 5 to 6!

2) Returning multiple values via out parameters

Sometimes we need a function to return multiple values. However, functions can only have one return value. One way to return multiple values is using reference parameters:

#include <iostream>
#include <cmath> // for std::sin() and std::cos()
void getSinCos(double degrees, double &sinOut, double &cosOut)
{
// sin() and cos() take radians, not degrees, so we need to convert
static constexpr double pi { 3.14159265358979323846 }; // the value of pi
double radians = degrees * pi / 180.0;
sinOut = std::sin(radians);
cosOut = std::cos(radians);
}
int main()
{
double sin(0.0);// C++ 11 <==> double sin = 0.0
double cos(0.0);
// getSinCos will return the sin and cos in variables sin and cos
getSinCos(30.0, sin, cos);
std::cout << "The sin is " << sin << '\n';
std::cout << "The cos is " << cos << '\n';
return 0;
}

This function takes one parameter (by value) as input, and “returns” two parameters (by reference) as output. Parameters that are only used for returning values back to the caller are called out parameters. We’ve named these out parameters with the suffix “out” to denote that they’re out parameters. This helps remind the caller that the initial value passed to these parameters doesn’t matter, and that we should expect them to be rewritten. By convention, output parameters are typically the rightmost parameters.

This method, while functional, has a few minor downsides. First, the caller must pass in arguments to hold the updated outputs even if it doesn’t intend to use them. More importantly, the syntax is a bit unnatural, with both the input and output parameters being put together in the function call. It’s not obvious from the caller’s end that sin and cos are out parameters and will be changed. This is probably the most dangerous part of this method (as it can lead to mistakes being made). Some programmers and companies feel this is a big enough problem to advise avoiding output parameters altogether, or using pass by address for out parameters instead (which has a clearer syntax indicating whether a parameter is modifiable or not).

Personally, we recommend avoiding out parameters altogether if possible. If you do use them, naming out parameters (and output arguments) with an “out” suffix (or prefix) can help make it clear that the value might be modified.

3) Limitations of pass by reference

Non-const references can only reference non-const l-values (e.g. non-const variables), so a reference parameter cannot accept an argument that is a const l-value or an r-value (e.g. literals and the results of expressions).

4) Pass by const reference

As mentioned in the introduction, one of the major disadvantages of pass by value is that all arguments passed by value are copied into the function parameters. When the arguments are large structs or classes, this can take a lot of time. References provide a way to avoid this penalty. When an argument is passed by reference, a reference is created to the actual argument (which takes minimal time) and no copying of values takes place. This allows us to pass large structs and classes with a minimum performance penalty.

However, this also opens us up to potential trouble. References allow the function to change the value of the argument, which is undesirable when we want an argument be read-only. If we know that a function should not change the value of an argument, but don’t want to pass by value, the best solution is to pass by const reference.

You already know that a const reference is a reference that does not allow the variable being referenced to be changed through the reference. Consequently, if we use a const reference as a parameter, we guarantee to the caller that the function will not change the argument!

The following function will produce a compiler error:

void foo(const std::string &x){ // x is a const reference
x = "hello";//compile error: a const reference cannot have its value changed!
}

Using const is useful for several reasons:

Alt text

#include <string>
void foo(std::string& text) {}
int main(){
std::string text{ "hello" };
foo(text); // ok
foo(text + " world"); // illegal, non-const references can't bind to r-values.
return 0;
}

5) References to pointers

It’s possible to pass a pointer by reference, and have the function change the address of the pointer entirely:

#include <iostream>
void foo(int *&ptr) // pass pointer by reference
{
ptr = nullptr; // this changes the actual ptr argument passed in, not a copy
}
int main()
{
int x = 5;
int *ptr = &x;
std::cout << "ptr is: " << (ptr ? "non-null" : "null") << '\n'; // prints non-null
foo(ptr);
std::cout << "ptr is: " << (ptr ? "non-null" : "null") << '\n'; // prints null
return 0;
}

As a reminder, you can pass a C-style array by reference. This is useful if you need the ability for the function to change the array (e.g. for a sort function) or you need access to the array’s type information of a fixed array (to do sizeof() or a for-each loop). However, note that in order for this to work, you explicitly need to define the array size in the parameter:

#include <iostream>
// Note: You need to specify the array size in the function declaration
void printElements(int (&arr)[4])
{
int length{ sizeof(arr) / sizeof(arr[0]) }; // we can now do this since the array won't decay
for (int i{ 0 }; i < length; ++i)
{
std::cout << arr[i] << '\n';
}
}
int main()
{
int arr[]{ 99, 20, 14, 80 };
printElements(arr);
return 0;
}

This means this only works with fixed arrays of one particular length. If you want this to work with fixed arrays of any length, you can make the array length a template parameter.

6) Pros and cons of pass by reference

Alt text

41. Difference between const reference and normal parameter

void DoWork(int n);
void DoWork(const int &n);

The difference is more prominent when you are passing a big struct/class.

struct MyData {
int a,b,c,d,e,f,g,h;
long array[1234];
};
void DoWork(MyData md);
void DoWork(const MyData& md);

when you use use ‘normal’ parameter, you pass the parameter by value and hence creating a copy of the parameter you pass. if you are using const reference, you pass it by reference and the original data is not copied.

In both cases, the original data cannot be modified from inside the function.


End of the file
Nov. 28, 2020, by CCJ at Secaucus, NJ, US.