C++ Interview Questions for Beginner and Experienced Programmers - Interview Preparation Guide
24 min read

C++ Interview Questions for Beginner and Experienced Programmers - Interview Preparation Guide

In this article, I have sourced almost all the major topics in C++. My friend and I have personally used these questions to crack an initial round for a senior software engineer role.

I hope this detailed article will help you to prepare for your C++ interview.

What is the difference between C and C++?

  1. C is a procedural programming language, but C++ supports both procedural and Object-Oriented programming.
  2. C++ supports features like function overloading, templates, inheritance, virtual functions, and friend functions. These features are absent in C.
  3. C++ supports exception handling at a language level, in C exception handling, is done in the traditional if-else style.
  4. C++ supports references and C doesn’t.

Which is faster i++ and ++i?

  • ++i is sometimes faster (and never slower) than i++.
  • Post increment requires a local copy of the value before it gets incremented.

What are the access specifiers?

  1. public - members are accessible from outside the class
  2. private - members can not be accessed/viewed from outside the class. They can only be accessed using either getters and setters or friend functions.
  3. protected - members can not be accessed from outside the class. However, they can be accessed in the inherited classes.

What are class and objects?

  • class is a user-defined data type and an object is an instance of the class.
#include <iostream>
using namespace std;

// class declaration
class Base {
	// instance variables
	int x;
public:
	void add(int a, int b) { x = a + b; }
	void print() { cout << x << endl; }
};

int main() {
	Base b; // object of the class
	b.add(1, 2);
	b.print();
	
	return 0;
}

What is this pointer?

This pointer is passed as a hidden argument to non-static member functions.

#include <iostream>
using namespace std;

class Base {
	int x;
public:
	void set(int a) { x = a; }
	int get() { return x; }
	// compiler will internally calls it in the following way:
	// void setComp(Base* const this, int a) { this->x = a; }
	// int get(cont Base* const this) const { return this->x; }
};

int main() {
	Base b;
	b.set(1);
	int val = b.get();
	return 0;
}

What is the difference between pointer and reference?

  • Reference can't be NULL.
  • The memory address of a reference will be identical to the variable it is referring to. But, the pointer holds its own memory address.
  • A pointer can be re-assigned
  • A pointer can perform arithmetic operations. i.e p++
  • Indirections are allowed in the case of the pointer. i.e we can have pointers to pointers.
#include <iostream>
using namespace std;

int main() {
	int i = 10;
	
	int *p = &i; // pointer
	int &ref = i; // reference

	// memory address of i and ref will be equal
	// But the pointer will hold its own memory address
	cout << &i << endl;
	cout << &ref << endl;
	cout << &p << endl;
	
	// indirection 
	int *q = NULL;
	int **qq  = &q;
	int **qqq  = &qq;
}

What is Smart Pointer?

Whenever a user forgot to deallocate a pointer that will cause a memory leak. Smart pointer solves this issue by freeing the memory when the object gets destroyed.

1. unique_ptr:

unique_ptr stores only one pointer.

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

class Base {
	int x, y;
public:
	Base(int a, int b) { x = a; y = b; }

	int add() { return x + y; }
};

int main(){

	unique_ptr<Base> p1(new Base(1, 2));
	cout << p1->add() << endl; // 3

	unique_ptr<Base> p2;
	p2 = move(p1);
	cout << p2->add() << endl; // 3

	return 0;
}

2. shared_ptr:

A shared_ptr can share ownership of an object while storing a pointer to another object.

#include <iostream>
using namespace std;
#include <memory>
 
class Base {
    int x, y;
public:
    Base(int a, int b) { x = a; y = b; }
 
    int add() { return x + y; }
};
 
int main()
{
    shared_ptr<Base> p1(new Base(1, 2));
    cout << p1->add() << endl; // 3
 
    shared_ptr<Base> p2;
    p2 = p1;
 
    cout << p2->add() << endl; // 3
 
    cout << p1->add() << endl; // 3
 
    cout << p1.use_count() << endl; // Reference Counter is 2
    return 0;
}

3. weak_ptr

A weak pointer is similar to a shared pointer except it won't maintain a reference counter.

What is a dangling pointer?

  • A dangling pointer is a pointer that points to invalid data or to data which is not valid anymore.
  • The smart pointer can be used to fix the issue.
#include <iostream>
using namespace std;

void func(int *p) {
	cout << *p << endl;
	delete p;
}

int main() {
	int *p1 = new int(10);
	int *p2 = p1;
	func(p2); // at this point, p2 is deleted but p1 still exists.
	
	cout << *p1 << endl; // here p1 is a dangling pointer and will return 0
	return 0;
}

What is a function pointer?

  • Function pointer store the address of another function.
#include <iostream>
using namespace std;

int add(int x, int y) {
	return x + y;
}

int main() {
	// function pointer
	int (*addition)(int, int) = &add;
	cout << addition(1, 2) << endl;
	
	return 0;
}
  • Example: pass function pointer as an argument
#include <iostream>
using namespace std;

int add(int x, int y) {
	return x + y;
}

int total(int (*addition)(int, int)) {
	int result = addition(1, 2);
	return result;
}

int main() {
	cout << total(&add) << endl;
	return 0;
}
  • Example: how to return a function pointer
#include <iostream>
using namespace std;

int add(int x, int y) {
	return x + y;
}

int sub(int x, int y) {
	return x - y;
}

int (*total (int type))(int, int) {
	if (type == 1) {
		return add;
	}
	if (type == 2) {
		return sub;
	}
}

int main() {
	int (*useFunc)(int, int) = &add;
	
	useFunc = total(1);
	int result = useFunc(1, 2);
	cout << result << endl; // 3

	useFunc = total(2);
	result = useFunc(1, 2);
	cout << result << endl; // -1
	
	return 0;
}

Vector vs ArrayList

  • ArrayList is a resizable array and its size can be increased dynamically.
  • Vector is similar to ArrayList, but it is synchronized. It also consumes more memory

How and when to use Enum?

  • Enums are lists of constants.
  • When you need a predefined list of values which do represent some kind of numeric or textual data, you should use an enum.
#include <iostream>
using namespace std;

enum week { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };

int main()
{
    week today;
    today = Monday;
    
    cout << today << endl; // 1
    cout << today + 1 << endl; // 2
    cout << today + 4 << endl; // 5
    
    return 0;
}

What is a friend function and class?

  1. In c++, the "friend" keyword can be used to make a function or class a friend of your class.
  2. Friend function/class can access the private and protected members of another class.
  • Example of friend function:
class Base {
	int x;
public:
	friend int getX(Base&);
	friend void setX(Base&, int);
};

int getX(Base& obj) { return obj.x; }
void setX(Base& obj, int _val) {
	obj.x = _val;
}

int main() {
    Base b;
    setX(b, 10);
    std::cout << getX(b);
    return 0;
}
  • Example of friend class:
class Base {
	int x;
public:
	friend class FriendClass;
};

class FriendClass {
	int x;
public:
	int getX(Base& obj) { return obj.x; }
	void setX(Base& obj, int _val) {
		obj.x = _val;
	}
};

int main() {
    Base b;
    FriendClass fc;
    fc.setX(b, 10);
    std::cout << fc.getX(b);
    return 0;
}

What is the const member function?

Restrict modification of data members inside the function.

class Base {
	int x;
public:
	int getX() const { x = 1; return x; } // compiler won't allow this assignment
	void setX(int _val) { x = _val; }
};

To fix this issue, we can use a mutable keyword.

class Base {
	mutable int x;
public:
	int getX() const { x = 1; return x; } // x will be changed to 1
	void setX(int _val) { x = _val; }
};

Malloc() vs new vs delete vs free

  • Malloc, New, delete, and free operators are used to dynamically allocate memory.
  • Constructors and destructors calls differ between them.
  • Malloc is a C function whereas new is specific to c++.
  • New calls the constructor of a class but malloc doesn't.
  • Free is a C function whereas delete is specific to c++.
  • Delete calls the destructor of a class but free doesn't.
#include "bits/stdc++.h"
using namespace std;

class A {
    int a;
public:
    int* ptr;
    
    A() { cout << "constructor" << endl; }
 
    ~A() { cout << "destructor" << endl; }
};
 
int main()
{
 
    A* a = new A; // constructor will be called.
    delete (a); // destructor will be called.
    
    A* b = (A*)malloc(sizeof(A));
    free(b);
    
    return 0;
}

What are the main features of oops?

  • Encapsulation
  • Polymorphism
  • Inheritance

What is Inheritance?

Inheritance allows us to define a class in terms of another class, which makes it easier to create and maintain an application.

#include <iostream>
using namespace std;
 
class Parent {
public:
    int x;
};

// Derived class from Parent (Base)
class Child: public Parent {
public:
  int y;
};

int main() {
	Child c;
	c.x = 1;
	c.y = 2;
	cout << c.x << " " << c.y << endl;
}

What is the diamond problem?

In the following example,

  • Class A is inherited into class B. At that point, we will have variables a and b.
  • Class A is inherited into class C. At that point, we will have variables a and c.
  • When we will inherit classes B and C into D. we will have access to the a, b and a, c variables.
  • Until we touch variable a with class D everything will work fine.

To solve this issue, we will need to inherit class A virtually into classes B and C.


//	  A
//  /   \
// B     C
//  \   /
//    D

#include <iostream>
using namespace std;

class A { public: int a; };

class B: virtual public A { public: int b; };

class C: virtual public A { public: int C; };

class D: public B, public C { public: int d; };

int main() {
	D d;
	d.a = 10; // compiler will throw error without virutal keyword

	return 0;
}

What is encapsulation?

It is an object-oriented programming concept that binds/bundles TOGETHER the data and the method which operates/manipulates that data.

class Base {
	int x;
public:
	int getX() { return x; }
	void setX(int _val) { x = _val; }
};

What is the difference between abstraction and encapsulation?

  • Abstraction is used to show only necessary things. i.e implementation hiding.
  • Encapsulation binds the data & functions together which keeps both safe from outside interference. i.e information hiding

in this example,

  • Encapsulation: any member of the class can only be accessed by the object.
  • Abstraction: implementation of the method add is hidden.
#include <iostream>
using namespace std;

class Base {
private:
	int x, y;
public:
	Base(int a, int b): x(a), y(b) {}
	int add() { return x + y; } // abstraction
};

int main() {
	Base b(1, 2);
	int total = b.add();
	cout << total << endl;
	
	return 0;
}

What is Object Slicing?

- Object slicing is used to describe the situation when you assign an object of a derived class to an instance of a base class.
- To prevent unexpected behaviour, we can use pointers or references.

class Base {
   int x;
};

class Derived : public Base {
   int y;
};

int main()  
{  
   base b;  
   derived d;  
   b=d;  // only x gets copied to b
   return 0;  
}

In the above example, since the derived extends Base, now it contains x and y. On assignment, only x is copied to the b and d got sliced.

What are static and dynamic binding in C++?

  • Static binding: a function call determined at compile time.
  • Dynamic binding: a function call determined at run time.
#include <iostream>
using namespace std;

class Base {
public:
	virtual void print() { cout << "Base" << endl; }
};

class Derived: public Base {
public:
	void print() { cout << "Derived" << endl; }
};

int main() {
	// without virtual keyword it will print Base and Base
	Base base;
    Derived derived;
	
    Base *ptr = &base;
    ptr->print(); // Base
 
    ptr = &derived;
    ptr->print(); // Derived
	
	return 0;
}

What is type casting?

Converting an expression of a given type into another type is known as type-casting.
There are four types of type casting available in C++:

1. Static cast

  • The static cast performs Implicit conversions between types.
  • The static cast is more restrictive than C-style. for example, char* to int* is not allowed.
int main() {
	char c;  // 1 byte data
	int *i = (int*)&c; // 4 bytes
	*i = 5; // pass at compile time but fail at run time.
	
	int *i = static_cast<int*>(&c); // compile time error 
    return 0;
}
  • We can use a static cast for up and downcasting. But, the static cast is not recommended for downcasting.

Here is an example:

class Base {};
class D1 : public Base {};
class D2 : public Base {};

int main() {
	D1 d1;
	D2 d2;

	Base *b1 = static_case<Base*>(&d1); 
	Base *b2 = static_case<Base*>(&d2); 
	
	D1 *d3 = static_cast<D1*>(b2); // pass at compile time but our pointer holds
	D1 *d4 = static_cast<D2*>(b1); // wrong values (confused). use dynamic casting.
    return 0;
}

2. Dynamic Cast

  • Dynamic cast is used at run time to find out the correct downcast.
  • SYNTAX: dynamic_cast < new_type > .....
  • 1 virtual function is required in the base class.
  • Dynamic cast returns a value of < new_type > if the casting is successful.
  • The dynamic cast returns a null pointer if the casting fails and < new_type > is a pointer.
  • Dynamic cast will throw an exception of type "std::bad_cast" if the casting fails and < new_type > is a reference type.
#include <iostream>
using namespace std;

class Base {
	virtual void print() {
		cout << "Base" << endl;
	}
};
class D1 : public Base {
	void print() { cout << "D1" << endl; }
};
class D2 : public Base {
	void print() { cout << "D2" << endl; }
};

int main() {
	D1 d1;

	Base *b1 = dynamic_cast<Base*>(&d1); 

	D2 *d2 = dynamic_cast<D2*>(b1); // will return NULL
	return 0;
}

3. Const cast:

  • Const cast is used to change the constant value of any object. OR remove the constant behaviour.
#include <iostream>
using namespace std;

int main() {
	const int x = 5;
	const int *y = &x;
	
	int *i = const_cast<int*>(y);
	*i = 10; // pass at compile but invalid.
	cout << x << endl; // 5 because we can't change the address value
	cout << *i << endl; // 10
	
	int a = 5;
	const int *b = &a;
	int *j = const_cast<int*>(b);
	*j = 10; // valid because the origin variable a is not a const.
	cout << a << endl; // 10
	cout << *j << endl; // 10
	
	return 0;
}

4. Reinterpret cast:

  • It can typecast any pointer to any other pointer. (DANGEROUS) we should be very careful.
#include <iostream>
using namespace std;

class A {
public:
	void print() { cout << "A" << endl; }
};

class B {
public:
	void print() { cout << "B" << endl; }
};

int main() {
	A *a1 = new A();
	B *b = new B();
	
	A *a2 = reinterpret_cast<A*>(b);
	a2->print(); // It will pass and print B;
	
	return 0;
}
  • Really useful while working with bits.
#include <iostream>
using namespace std;

struct A {
	int x; // 4 bytes
	char c; // 4 bytes both char and bool
	bool b;
};

int main() {
	A a;
	
	a.x = 5;
	a.c = 'a';
	a.b = true;

	int *i = reinterpret_cast<int*>(&a);
	cout << *i << endl; // print 5
	i++; // increment it by 4 bytes.
	cout << *i << endl; // compiler will convert the char into int and print a num

	char *c = reinterpret_cast<char*>(i);
	cout << *c << endl; // print a
	c++; // increment by 1 byte.

	bool *b = reinterpret_cast<bool*>(c);
	cout << *b << endl; // print 1 (true)
	
	return 0;
}

What is the difference between shallow copy and deep copy?

  • In shallow copy, an object is created by simply copying the data of all variables of the original object.
#include <iostream>
using namespace std;

class Base {
	int x, y;
public:
	void set(int a, int b) {
		x = a;
		y = b;
	}

	void print() { 
		cout << x << " " << y << endl;
	}
};

int main() {
	Base b;
	b.set(1, 2);
	b.print(); // 1 2

	//copy contructor
	Base b1 = b;
	b1.print(); // 1 2
	
	return 0;
}
  • In Deep copy, an object is created by copying data of all variables and it also allocates similar memory resources with the same value to the object.
#include <iostream>
using namespace std;

class Base {
	int *x;
	int y;
public:
	Base() {
		x = new int;
	}

	Base(Base& b) {
		x = new int;
		*x = *(b.x);
		y = b.y;
	}

	~Base() {
		delete x;
	}
	
	void set(int a, int b) {
		*x = a;
		y = b;
	}

	void print() { 
		cout << *x << " " << y << endl;
	}
};

int main() {
	Base b1;
	b1.set(1, 2);
	b1.print(); // 1 2

	//copy contructor
	Base b2 = b1;
	b2.print(); // 1 2
	
	return 0;
}

What are vPointers and vTables?

  • Anywhere in the inheritance tree, if there is a virtual function, the compiler will create a vTable for it.
  • There is a different vTable for every type (not for instance).
  • vPointer points to the vTable of that class. Every time the compiler creates a vTable for a class, it adds an extra argument to it (a pointer). similar to this pointer.

What is a pure function?

Pure function always returns the same result for the same arguments. i.e sin(), max(), floor() etc.

#include<iostream>
#include<cmath>

using namespace std;
int main() {
   int num = 9;
   cout << sqrt(num) << endl; // 3
   return 0;
}

What is an Interface?

  • An interface describes the behaviour or capabilities of a class without committing to a particular implementation of that class. We use abstract classes to implement it.
  • A class is made abstract by declaring at least one of its functions as a pure virtual function.
#include <iostream>
using namespace std;
 
class Base {
protected:
    int x, y;
public:
    virtual int add() = 0; // pure virtual function
    void set(int a, int b) { x = a; y = b; }
};
 
class Derived1: public Base {
   public:
      int add() { return x + y; }
};

class Derived2: public Base {
   public:
      int add() { return x + y; }
};
 
int main() {
    Derived1 d1;
    Derived2 d2;
    d1.set(1, 2);
    cout << d1.add() << endl; // 3
    
    d2.set(2, 3);
    cout << d2.add() << endl;  // 5

   return 0;
}

What is a Template?

The simple idea is to pass data type as a parameter so that we don’t need to write the same code for different data types.

#include <iostream>
using namespace std;
 
template <typename T> T getMax(T x, T y) {
    return (x > y) ? x : y;
}
 
int main() {
    cout << getMax<int>(2, 5) << endl; // 5
    cout << getMax<double>(2.0, 5.0) << endl; // 5
    return 0;
}

How static variable behaves in a Template?

  • Each instantiation of a function template has its own copy of local static variables.
#include <iostream>
using namespace std;

template<typename T>
void print(const T x) {
	static int i = 10;
	cout << ++i << endl;
}

int main() {
	// two copies of static variable i exist.
	// because we're using templates
	print(1); // 11
	print('x'); // 11
	return 0;
}

What is function overloading & overriding?

  • Function overloading is determined at compile time. This binding is called static binding.
#include <iostream>
using namespace std;

void print(int _val) {
	cout << _val << endl;
}

void print(double _val) {
	cout << _val << endl;
}

void print(char _val) {
	cout << _val << endl;
}

int main() {
	print('x');
	print(1);
	print(1.11);
}
  • Function overriding is a feature that allows us to have the same function in the child class which is already present in the parent class.
#include <iostream>
using namespace std;

class Base {
public:
	void print() { cout << "Base" << endl; }
};

class Derived: public Base {
public:
	void print() { cout << "Derived" << endl; }
};

int main() {
	Base b;
	Derived d;
	b.print();
	d.print();
}

How to use operator overloading?

#include <iostream>
using namespace std;

class A {
	int x;
public:
	A(): x{0} {}
	A(int x): x{x} {}
	A operator + (const A &a2) {
		A a;
		a.x = x + a2.x;
		return a;
	}

	void print() {
		cout << "Total: " << x << endl;
	}
};

int main() {
	A a1(1);
	A a2(2);
	
	A a3 = a1 + a2;
	a3.print(); // Total: 3
	
	return 0;
}

How does the comma operator work?

#include <iostream>
using namespace std;

int main() {
	int x, y;
	// here 1, 2, 3 will be executed.
	// in the case of x, it will be assigned to 1.
	// But, in case of y, it will be assigned to the last element.
	x = 1, 2, 3;
	y = (1, 2, 3);

	cout << x << endl; // 1
	cout << y << endl; // 3
}

What is the difference between a conversion operator and a constructor?

  • When a constructor takes only one parameter and a user initializes the same data type into the object then that constructor becomes a conversion constructor.
  • The conversion operator converts a value of a class type to a different type.
#include <iostream>
using namespace std;
class Base {
	int x;
public:
	Base(int x=0) {
		cout << "conversion constructor" << endl;
	}
	operator string () { 
		cout << "conversion operator" << endl;
		return to_string(x);
	}
};

int main() {
	Base b(1);
	string s = b; // invoke conversion operator
	b = 10; // invoke conversion constructor
	return 0;
}

What is a Segmentation fault?

  • A segmentation fault occurs because of a memory access violation. i.e when a program tries to access a memory location that it is not allowed to access.
  • you can use gdb to debug any segfault.
#include <iostream>
using namespace std;

int main() {
	int *i = NULL;
	// we can't write at NULL
	// we will get segfault here
	*i = 10; 
	cout << *i << endl;
	
	return 0;
}

What are SOLID? 5 Principles of Object-Oriented Design

1. Single Responsibility principle:

  • The single responsibility principle makes sure that if a class has a responsibility then this responsibility must be tightly coupled. In other words, it should only have one responsibility and it should have only one reason to change it.
  • It is useful in case of testing and organization.

2. Open-Closed Principle:

Open closed principle states that software entities i.e classes, functions etc. should be open for extension, but closed for modification.

3. Liskov Substitution Principle:

The principle states that every subclass or derived class should be substitutable for its base or parent class.

4. Interface Segregation Principle:

The interface segregation principle states that a client should never be forced to implement an interface that it doesn’t use, or clients shouldn’t be forced to depend on methods they do not use.

5. Dependency Inversion Principle:

Entities must depend on abstractions, not on concretions. DIP states that the high-level module must not depend on the low-level module, but should depend on abstractions.

What is the difference between void and void*?

Void:

  1. void data type denotes that a function will not return anything.
  2. The size of the void is 1 byte. (only works in GCC)

Void *

  1. it is a universal pointer.
  2. We can convert any data type pointer to void* except function pointer, const or volatile.
int *x = new int(10);
void *v = x;
  1. Void pointer can't be dereferenced.
  • Use Cases:
  1. malloc and calloc return void* and we can typecast then to desired data type.
  2. In c, void* is used to create a generic function. i.e qsort function.

What is structural padding?

  • Padding is used to speed up the cpu optimization. i.e | a | b | - | - | i | i | i | i |
  • 32 bit processor reads 4 bytes.
  • The 64-bit processor reads 8 bytes.
#include <iostream>
using namespace std;

#pragma pack(1) // it will disable padding

struct A {
	char a;
	char b;
	int i;
};

int main() {
	// it will print 8 bytes if we don't disable padding
	// otherwise it will be 6 bytes.
	cout << sizeof(A) << endl;
	return 0;
}

What is Pimpl?

Pimpl (pointer to implementation) is used to hide the implementation, especially in member variables and private methods.

Usecase:

  • Minimization of compilation dependencies.
  • Separation of interface and implementation.
  • Portability.

What is Raii?

Raii (Resource acquisition is initialization) binds the life cycle of a resource which basically means memory will automatically be released when an object goes out of scope. i.e we don't have care of memory leaks.

What is function chaining?

  • Function chaining is a sequence of a function call.
  • It is useful to display functional dependencies. i.e we have to call function x before calling y.
#include <iostream>
using namespace std;

class Base {
	int x, y;
public:
	Base& setX(int a) { x = a; return *this; }
	Base& setY(int b) { y = b; return *this; }
	void print() { cout << x << " " << y << endl; }
};

int main() {
	Base b;
	// functional chaining
	b.setX(1).setY(2).print();
	return 0;
}

What is snooping (cache)?

  • Snooping ensures memory cache coherency in symmetric multiprocessing (SMP) systems.
  • Each processor in a system cache on a bus monitor. And, that bus verifies whether it has a copy of the requested data.
  • Also, whenever a processor writes data, all the other processor cache copies will be invalidated or updated.

What is program memory?

In c++, there are different types of memory used. Think of them as memory blocks that are stacked on top of each other.

  1. Heap: Heap section provide dynamic memory.
  2. Stack: It is used to store local variables and functions.
  3. Global (static): in data memory, global variables are stored.
  4. Program memory: It is used to store program code.

What is a Thread?

A thread is a single sequential flow of control within a program.

What is time slicing?

A time slice is the short time frame that gets assigned to process for CPU execution.

What is the difference between a thread and a process?

  • A process is the execution of a program that allows you to perform the appropriate actions mentioned in the program.
  • Thread is an execution unit that is part of a process. A process can have multiple threads, all executing at the same time
  • The process takes more time for creation and termination in comparison to thread.
  • Threads share memory whereas a process is isolated.

What are the ways to create a thread?

There are three ways to create a thread:

1. Function Pointer:

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

void thread_function() { cout << "thread executing" << endl; }

int main()
{
    thread t(thread_function);
    cout << "main thread" << endl;
    t.join();
    cout << "exiting main thread" << endl;
    return 0;
}

2. Function Objects:

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

class Base {
public:
	void operator()() { cout << "thread executing" << endl; }
};

int main() {
	thread t((Base()));
	cout << "main thread" << endl;
	t.join();
	cout << "exiting main thread" << endl;
	return 0;
}

3. Lambda Functions:

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

int main()  {
    thread t([] {
        cout << "thread executing" << endl;
    });
    cout << "main thread" << endl;
    t.join();
    cout << "exiting main thread" << endl;
    return 0;
}

Why must you override the run() method in your thread class?

  • In the thread class, the run method is defined with an empty implementation. if we don't override it then the method will be executed and there will be no output.
  • Also, whenever the run method gets overridden the performance of the system gets improved.

Why is thread behaviour considered unpredictable?

Thread behaviour is unpredictable because the execution of threads depends on the thread scheduler. Even on the same platform, the same threading program may produce different outputs.

What is a thread scheduler?

Scheduling is the process of executing multiple threads on a single CPU in some order. This activity is handled by a process called scheduler.

Can you start a thread twice?

No. After starting a thread, it can never be started again. IllegalThreadStateException is thrown if you try to do so.

What is a daemon thread?

  • It is a low-priority thread.
  • It may support other threads or constantly run in the background i.e windows services.

What is thread starvation?

Thread starvation states a situation where a thread is unable to gain regular access to the shared resources.

What is livelock?

  • A livelock is similar to a deadlock, except that the states of the processes involved in the livelock constantly change with regard to one another.
  • Livelock is a special case of resource starvation

What is Deadlock?

Deadlock is a situation where multiple processes are in the memory and don't able to start their execution because the resources are being held by another resource.

Why use multithreading in your applications?

The main reason to use multithreading is to improve the performance of the application. For example, A web server can utilize multiple threads to simultaneous process requests for data at the same time.

What is a thread pool?

  • A thread pool is a group of pre-instantiated threads that can be "reused" to execute tasks.
  • A thread pool is an alternative to creating a new thread for each task you need to execute.

How might you achieve thread-safety?

  • Make sure a function doesn't touch any global or static variable.
  • Use a mutex to synchronize access to the critical data.
  • Mutex and locks are useful, but make sure we are not locking the same mutex twice in the same thread.

What is Volatile?

The volatile keyword basically states that the volatile variable's value could change unexpectedly. So, the compiler won't try to optimize it. i.e I/O-mapped memory location

What is a race condition?

A race condition occurs when two or more threads can access shared data and try to change it at the same time. We don't know which thread will change the data because the thread scheduling algorithm decides the order. In simple words, threads are racing to change/access the data.

What is context switching?

Context Switching involves storing the context or state of a process so that it can be reloaded when required and execution can be resumed from the same point as earlier.

Context Switching Triggers

  1. Multitasking
  2. Interrupt Handling
  3. User and Kernel Mode Switching.

What are external linkage and internal linkage?

  • Compiler generate a translation unit whenever we write an implementation file such as .cpp, .cxx etc. Internal linkage refers to everything only in the scope of this translation unit.
  • External linkage refers to everything accessible through the whole program.

What is the difference between the 32bit and 64bit systems?

  • In a 32bit system, the CPU can address a maximum of 4GB of RAM. In reality, ~3.5gb, because part of the registry is used to store other temporary values besides memory addresses.
  • In a 64bit system, the CPU can reference about 16exbyte. But, most importantly it can address more than 4GB of RAM.
    For example, in the case of a 32bit processor, if a system has 16GB of RAM then 12GB will be inaccessible by the CPU.

Blackbox vs Whitebox Testing

  • Blackbox testing is done without the knowledge of the internal structure of the program.
  • Whitebox testing is done with the knowledge of the internal structure of the program.
  • The Black Box test doesn’t require programming knowledge because the user does not care or know about the internal structure.
  • In Black Box testing the main goal is to test the behaviour of the software whereas in white box testing we test the internal operation of the system.
  • In comparison, White Box testing is a more time-consuming process.

How to call a function before main()?

  • Use a constructor to call the function.
#include <iostream>
using namespace std;

void xyz() {
	cout << "xyz" << endl;
}

class Base {
public:
	Base() { xyz(); }
};

Base b;

int main() {
	cout << "Main" << endl;
	return 0;
}
  • Use a static variable to call the function.
#include <iostream>
using namespace std;

int xyz() {
	cout << "xyz" << endl;
	return 0;
}

class Base {
public:
	static int staticVar;
};

int Base::staticVar = xyz();

int main() {
	cout << "Main" << endl;
	return 0;
}

How to print something N number of times without using loops or recursion?

  • Use constructor to solve this issue
#include <iostream>
using namespace std;

class Base {
public:
	Base() {
		cout << "Hello World" << endl;
	}
};

int main() {
	Base b[5];
	return 0;
}

Return 2D array from a function:

#include <iostream>
using namespace std;

const int N = 3;

void print(int** arr) {
	for (int i=0; i < N; ++i) {
		for (int j=0; j < N; ++j) {
			cout << arr[i][j];
		}
		cout << endl;
	}
}

int** get() {
	int** arr = new int*[N];
	for (int i=0; i < N; ++i) {
		arr[i] = new int[N];
		for (int j=0; j < N; ++j) {
			arr[i][j] = i + j;
		}
	}
	return arr;
}

int main() {
	int **arr;
	arr = get();
	print(arr);
	
	return 0;
}

Return an array from a function:

#include <iostream>
using namespace std;

int* get() {
	// static is important otherwise the array will 
	// be destroyed because arr only exists within the function
	static int arr[3] = {1, 2, 3};
	return arr; 
}

// generate array dynamically
int *getArr() {
	int* arr = new int[3]; // heap memory
	arr[0] = 1;
	arr[1] = 2;
	arr[2] = 3;
	return 
}

int main() {
	int *arr = get();
	cout << arr[0] << endl;
	cout << arr[1] << endl;
	cout << arr[2] << endl;

	int *arr = getArr();
	cout << arr[0] << endl;
	cout << arr[1] << endl;
	cout << arr[2] << endl;
	return 0;
}

How to call a base class function from a derived class?

  • The common option is to use the scope resolution operator.
  • Typecast the derived class into the base class.
#include <iostream>
using namespace std;

class Base {
public:
	void print() { cout << "Base" << endl; }
};

class Derived: public Base {
public:
	void print() { cout << "Derived" << endl; }
};

int main() {
	Derived d;
	d.print();
	d.Base::print(); // static binding

	// using type cast
	// we need to first upcast and then downcast to make it work
	Base *b = static_cast<Base*>(&d);
	b->print();
	
	Derived *d1 = static_cast<Derived*>(&d);
	d1->print();
	return 0;
}

How can you print without semicolons?

#include<iostream>
using namespace std;

int main() {
    if (cout << "Hello world") {}
}

How to convert char* to int (AtoI function)?

#include <iostream>
using namespace std;

int AtoI(char *str) {
	int result = 0;
	int sign = 1;
	int i = 0;
	
	if (str[0] == '-') {
		sign = -1;
		i++;
	}
	
	for(; str[i] != '\0'; ++i) {
		result = result * 10 + str[i] - '0';
	}
	
	return sign * result;
}

int main() {
	char str[] = "-12345";
	int val = AtoI(str);
	cout << val << endl;
}

How to stop someone from taking the address of your object?

  • Option 1 - Overload & operator and make it private.
#include <iostream>
using namespace std;

class Base {
	int x;
public:
	Base() {}
private:
	Base* operator &() {
		cout << "overloading operator" << endl;
		return this;
	}
};

int main() {
	Base b;
	Base *p = &b;
}
  • Option 2 - Delete & operator from your class.
#include <iostream>
using namespace std;

class Base {
	int x;
public:
	Base() {}
	Base* operator &() = delete;
};

int main() {
	Base b;
	Base *p = &b;
}

Return multiple values from a function?

#include <iostream>
using namespace std;

struct data {
	int i;
	char c;
	bool b;
};

data print() {
	return data{1, 'x', true};
}

int main() {
	data d;
	d = print();
	cout << d.i << endl;
	cout << d.c << endl;
	cout << d.b << endl;
	
	return 0;
}

How to count set bits in an integer?

#include <iostream>
using namespace std;

int main() {
	int num = 7; // 0111
	int count = 0;

	// Brian Kernighan method
	for (count = 0; num; ++count) {
		num &= num - 1;
	}
	cout << count << endl; // 3

	num = 7;
	count = 0;
	// simple
	while(num) {
		count += num & 1;
		num >>= 1;
	}
	cout << count << endl; // 3
	
	return 0;
}

How to overload pre and post-increment operators?

#include <iostream>
using namespace std;

class Base {
	int x;
public:
	Base(): x{0} {}
	Base(int _val): x{_val} {}
	
	void print() { cout << x << endl; }

	Base & operator++() {
		x++;
		return *this;
	}

	Base operator++(int xyz) {
		Base temp = *this;
		++(*this);
		return temp;
	}
};

int main() {
	Base b(100);
	b.print(); // 100
	(b++).print(); // 100
	(++b).print(); // 102
	return 0;
}

How to set, unset, toggle, check, and change a single bit?

#include <iostream>
using namespace std;

int main() {
	int num, pos;
	
	// set one bit - or operator
	num = 3; // 0011
	pos = 3;
	num |= (1 << pos);
	cout << num << endl; // 11

	// unset one bit - & operator
	num = 3; // 0011
	pos = 1;
	num &= ~(1 << pos);
	cout << num << endl; // 1
	
	// toggle one bit - xor operator
	num = 3; // 0011
	pos = 2;
	num ^= (1 << pos);
	cout << num << endl; // 7

	// check one bit
	num = 3; // 0011
	pos = 1;
	int bit = (num >> pos) & 1;
	cout << bit << endl; // 1

	// change Nth bit
	num = 3; // 0011
	int n = 2;
	int val = 1;
	num = num & ~(1 << n) | (val << n);
	cout << num << endl; // 7
}