Skip to main content

C++ Interview Questions

Introduction

C++ remains one of the most widely used programming languages in the industry, particularly for performance-critical applications. As a result, C++ programming skills are highly valued, and interviews for C++ roles often include specific technical questions to evaluate a candidate's proficiency.

This guide covers the most common C++ interview questions you might encounter, from basic concepts to more advanced topics. Each question includes a detailed explanation and code examples to help you understand and prepare for your interviews.

Fundamental C++ Concepts

1. What is C++ and how does it differ from C?

Answer: C++ is a general-purpose programming language created as an extension of the C language with added object-oriented features. Key differences include:

  • C++ supports classes and objects
  • C++ supports function overloading
  • C++ has built-in exception handling
  • C++ includes reference variables
  • C++ supports operator overloading
  • C++ has a more extensive standard library

2. Explain the differences between stack and heap memory in C++.

Answer: Stack and heap are two regions of memory used for different purposes:

Stack:

  • Automatically managed memory (allocation/deallocation)
  • Used for local variables and function calls
  • Fast access and fixed size
  • Memory is automatically freed when variables go out of scope
  • Limited in size

Heap:

  • Dynamically allocated memory
  • Managed manually by the programmer (using new/delete)
  • Slower access compared to stack
  • Can grow as needed during runtime
  • Needs explicit deallocation to prevent memory leaks
cpp
void exampleFunction() {
// Stack allocation
int stackArray[1000]; // Stored on stack

// Heap allocation
int* heapArray = new int[1000]; // Stored on heap

// Use the arrays...

// Must manually deallocate heap memory
delete[] heapArray;

// stackArray is automatically deallocated when function ends
}

3. What are the features of Object-Oriented Programming in C++?

Answer: C++ implements the four main features of Object-Oriented Programming:

  1. Encapsulation: Bundling data and methods that operate on that data within a single unit (class) and restricting access to internal details.
cpp
class BankAccount {
private:
double balance; // Encapsulated data

public:
// Controlled access through methods
void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}

double getBalance() {
return balance;
}
};
  1. Inheritance: Ability to create new classes (derived) from existing classes (base), inheriting their attributes and behaviors.
cpp
class Shape {
protected:
int width, height;
public:
void setDimensions(int w, int h) {
width = w;
height = h;
}
};

class Rectangle : public Shape {
public:
int area() {
return width * height;
}
};
  1. Polymorphism: Ability of different classes to be treated as instances of the same class through inheritance and virtual functions.
cpp
class Animal {
public:
virtual void makeSound() {
std::cout << "Some animal sound" << std::endl;
}
};

class Dog : public Animal {
public:
void makeSound() override {
std::cout << "Woof!" << std::endl;
}
};

class Cat : public Animal {
public:
void makeSound() override {
std::cout << "Meow!" << std::endl;
}
};

// Polymorphic behavior
void animalSound(Animal* animal) {
animal->makeSound();
}

// Usage
Dog dog;
Cat cat;
animalSound(&dog); // Outputs: Woof!
animalSound(&cat); // Outputs: Meow!
  1. Abstraction: Simplifying complex reality by modeling classes appropriate to the problem, focusing on relevant details while hiding unnecessary implementation.
cpp
class DatabaseConnection {
private:
// Complex implementation details hidden
string connectionString;
void establishSocketConnection() { /* ... */ }
void authenticateUser() { /* ... */ }

public:
// Simple abstracted interface
bool connect(string server, string user, string password) {
connectionString = "server=" + server + ";user=" + user;
establishSocketConnection();
return authenticateUser();
}

void disconnect() {
// Implementation hidden
}
};

Memory Management in C++

4. What is a memory leak in C++ and how can it be prevented?

Answer: A memory leak occurs when dynamically allocated memory is not properly deallocated, causing the program to consume more and more memory over time. This can eventually lead to system crashes or decreased performance.

Common Causes:

  • Forgetting to call delete or delete[] for memory allocated with new or new[]
  • Losing pointers to allocated memory
  • Exception scenarios where deallocation code isn't reached

Prevention Methods:

  1. RAII (Resource Acquisition Is Initialization): Use smart pointers and container classes
cpp
// Instead of this (potential memory leak)
void riskyFunction() {
int* array = new int[100];
// If an exception occurs here, delete[] won't be called
processData(array);
delete[] array;
}

// Use this (RAII approach)
void safeFunction() {
// Memory automatically managed, even with exceptions
std::unique_ptr<int[]> array(new int[100]);
processData(array.get());
// No need for explicit delete
}
  1. Smart Pointers: Use std::unique_ptr, std::shared_ptr, and std::weak_ptr
cpp
void modernCppExample() {
// Automatically deleted when out of scope
std::unique_ptr<int> ptr1 = std::make_unique<int>(42);

// Reference counted, deleted when all references are gone
std::shared_ptr<int> ptr2 = std::make_shared<int>(100);

// Another reference to same memory
std::shared_ptr<int> ptr3 = ptr2;

// Memory will be freed when both ptr2 and ptr3 go out of scope
}
  1. Always match new with delete and new[] with delete[]

  2. Implement proper exception handling

5. Explain the differences between new/delete and malloc/free.

Answer: Both are used for dynamic memory allocation, but with key differences:

Featurenew/deletemalloc/free
TypeC++ operatorsC functions
ReturnsTyped pointervoid* pointer
FailureThrows exceptionReturns NULL
ConstructorsCalls constructors/destructorsDoesn't call constructors/destructors
Size calculationAutomaticManual using sizeof
OverloadingCan be overloadedCannot be overloaded
cpp
// malloc/free example
int* p1 = (int*)malloc(sizeof(int));
*p1 = 10;
free(p1);

// new/delete example
int* p2 = new int;
*p2 = 10;
delete p2;

// Array allocation
int* arr1 = (int*)malloc(5 * sizeof(int));
free(arr1);

int* arr2 = new int[5];
delete[] arr2; // Must use delete[] for arrays

6. What are smart pointers and what types are available in C++?

Answer: Smart pointers are C++ objects that act like raw pointers but provide automatic memory management, helping to prevent memory leaks and other memory-related errors.

Main Types in Modern C++:

  1. std::unique_ptr - Implements exclusive ownership model; cannot be copied, only moved.
cpp
std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
// std::unique_ptr<int> ptr2 = ptr1; // Error: cannot copy
std::unique_ptr<int> ptr2 = std::move(ptr1); // OK: transfer ownership
// Now ptr1 is nullptr
  1. std::shared_ptr - Implements shared ownership model with reference counting.
cpp
std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
std::shared_ptr<int> ptr2 = ptr1; // Both share ownership
std::cout << ptr1.use_count() << std::endl; // Outputs: 2
  1. std::weak_ptr - Non-owning "weak" reference to object managed by std::shared_ptr.
cpp
std::shared_ptr<int> shared = std::make_shared<int>(42);
std::weak_ptr<int> weak = shared;

// Using a weak_ptr
if (auto temp = weak.lock()) { // Creates shared_ptr if object exists
std::cout << *temp << std::endl;
} else {
std::cout << "Object no longer exists" << std::endl;
}

C++ Classes and Object-Oriented Programming

7. What is the difference between a class and a struct in C++?

Answer: In C++, the only technical difference between a class and a struct is the default access specifier:

  • In a struct, members are public by default
  • In a class, members are private by default

Everything else (inheritance, methods, etc.) works the same for both.

cpp
struct MyStruct {
int x; // Public by default
void foo(); // Public by default
private:
int y; // Explicitly private
};

class MyClass {
int x; // Private by default
void foo(); // Private by default
public:
int y; // Explicitly public
};

Conventionally, structs are often used for simple data containers, while classes are used for more complex behavior with data hiding.

8. Explain the concept of constructors and destructors in C++.

Answer: Constructors and destructors are special member functions in a class:

Constructors:

  • Initialize objects when they are created
  • Have the same name as the class
  • No return type, not even void
  • Can be overloaded to provide different ways to create objects

Destructors:

  • Clean up resources when objects are destroyed
  • Name is the class name preceded by tilde (~)
  • No parameters and no return type
  • Cannot be overloaded (only one destructor per class)
cpp
class Resource {
private:
int* data;

public:
// Default constructor
Resource() {
std::cout << "Default constructor called" << std::endl;
data = nullptr;
}

// Parameterized constructor
Resource(int size) {
std::cout << "Parameterized constructor called" << std::endl;
data = new int[size];
}

// Copy constructor
Resource(const Resource& other) {
std::cout << "Copy constructor called" << std::endl;
if (other.data) {
// Deep copy
data = new int[sizeof(other.data)/sizeof(int)];
memcpy(data, other.data, sizeof(other.data));
} else {
data = nullptr;
}
}

// Destructor
~Resource() {
std::cout << "Destructor called" << std::endl;
delete[] data; // Clean up dynamically allocated memory
}
};

// Usage
void constructorExample() {
Resource r1; // Default constructor
Resource r2(10); // Parameterized constructor
Resource r3 = r2; // Copy constructor
// Destructors automatically called when objects go out of scope
}

9. What is function overloading in C++?

Answer: Function overloading allows multiple functions with the same name but different parameters (different number or type of parameters) within the same scope.

The compiler distinguishes between overloaded functions based on the function signature (function name + parameter types).

cpp
class Calculator {
public:
// Overloaded functions
int add(int a, int b) {
return a + b;
}

double add(double a, double b) {
return a + b;
}

int add(int a, int b, int c) {
return a + b + c;
}
};

// Usage
Calculator calc;
int sum1 = calc.add(5, 3); // Calls first function
double sum2 = calc.add(3.5, 2.7); // Calls second function
int sum3 = calc.add(1, 2, 3); // Calls third function

Return type alone is not sufficient for overloading functions.

10. What is operator overloading in C++?

Answer: Operator overloading allows redefining the way operators work for user-defined types (classes), making them more intuitive to use.

cpp
class Complex {
private:
double real;
double imag;

public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}

// Overload + operator for adding two Complex objects
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}

// Overload << operator for easy output
friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
os << c.real;
if (c.imag >= 0) os << " + " << c.imag << "i";
else os << " - " << -c.imag << "i";
return os;
}
};

// Usage
Complex c1(3.0, 2.0);
Complex c2(1.5, -0.5);
Complex c3 = c1 + c2; // Using overloaded + operator
std::cout << c3; // Using overloaded << operator, outputs: 4.5 + 1.5i

Not all operators can be overloaded, and some rules apply:

  • Cannot create new operators
  • Cannot change operator precedence
  • Cannot change the number of operands
  • Some operators cannot be overloaded (like ., ::, ?:, etc.)

Inheritance and Polymorphism

11. Explain virtual functions and pure virtual functions in C++.

Answer:

Virtual Functions:

  • Declared with virtual keyword in base class
  • Enable polymorphic behavior through dynamic binding
  • Allow derived classes to override the function
  • Resolved at runtime based on the actual object type

Pure Virtual Functions:

  • Declared with = 0 syntax
  • No implementation in the base class
  • Make a class abstract (cannot instantiate it)
  • Derived classes must implement the function, or they also become abstract
cpp
class Shape {
public:
// Virtual function - has implementation but can be overridden
virtual void draw() {
std::cout << "Drawing a generic shape" << std::endl;
}

// Pure virtual function - must be implemented by derived classes
virtual double area() = 0;

// Regular destructor
~Shape() {
std::cout << "Shape destructor" << std::endl;
}
};

class Circle : public Shape {
private:
double radius;

public:
Circle(double r) : radius(r) {}

// Override virtual function
void draw() override {
std::cout << "Drawing a circle" << std::endl;
}

// Implement pure virtual function
double area() override {
return 3.14159 * radius * radius;
}
};

// Usage
void polymorphismExample() {
// Shape shape; // Error: cannot instantiate abstract class

Circle circle(5.0);
Shape* ptr = &circle; // Base class pointer to derived object

ptr->draw(); // Calls Circle::draw() (polymorphism)
std::cout << "Area: " << ptr->area() << std::endl; // Calls Circle::area()
}

12. What is the difference between function overloading and function overriding?

Answer:

AspectFunction OverloadingFunction Overriding
DefinitionMultiple functions with same name but different parametersRedefining a function from base class in derived class
ScopeSame classBase and derived classes
ParametersMust differ in number or typeSame signature (name, parameters, return type)
virtual keywordNot requiredRequired in base class for runtime polymorphism
ResolutionCompile-time (static binding)Runtime (dynamic binding) for virtual functions
cpp
// Function Overloading
class Calculator {
public:
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
};

// Function Overriding
class Animal {
public:
virtual void makeSound() {
std::cout << "Some generic sound" << std::endl;
}
};

class Dog : public Animal {
public:
// Same signature as base class function
void makeSound() override {
std::cout << "Woof!" << std::endl;
}
};

13. What is multiple inheritance and what is the diamond problem?

Answer: Multiple inheritance in C++ allows a class to inherit from more than one base class. While powerful, it can lead to ambiguity, especially with the "diamond problem."

Diamond Problem: Occurs when a class inherits from two classes that both inherit from a common base class. This creates two copies of the base class in the derived class, causing ambiguity.

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

class A {
public:
int value;
void display() { std::cout << "Class A" << std::endl; }
};

class B : public A {
public:
void display() { std::cout << "Class B" << std::endl; }
};

class C : public A {
public:
void display() { std::cout << "Class C" << std::endl; }
};

class D : public B, public C {
// D now has two copies of all A's members
};

// Problem
D d;
// d.value = 10; // Ambiguous: which 'value' to use? B::value or C::value?
// d.display(); // Ambiguous: which 'display' to call?

// To resolve, specify the path:
d.B::value = 10;
d.C::display();

Solution: Virtual Inheritance

cpp
class A {
public:
int value;
};

class B : virtual public A {}; // Note 'virtual'
class C : virtual public A {};

class D : public B, public C {
// Now D has only one copy of A's members
};

// No ambiguity
D d;
d.value = 10; // OK - only one 'value' exists

Templates and Generic Programming

14. What are templates in C++ and how are they used?

Answer: Templates are a powerful feature in C++ that enable generic programming. They allow you to write code that works with any data type without having to rewrite it for each type.

Function Templates:

cpp
// A generic function template
template <typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}

// Usage
int maxInt = max<int>(3, 7); // maxInt = 7
double maxDouble = max<double>(3.5, 2.5); // maxDouble = 3.5
std::string maxStr = max<std::string>("apple", "banana"); // maxStr = "banana"

Class Templates:

cpp
// A generic stack class template
template <typename T>
class Stack {
private:
std::vector<T> elements;

public:
void push(const T& element) {
elements.push_back(element);
}

T pop() {
if (elements.empty()) {
throw std::out_of_range("Stack is empty");
}
T top = elements.back();
elements.pop_back();
return top;
}

bool isEmpty() const {
return elements.empty();
}
};

// Usage
Stack<int> intStack;
intStack.push(10);
intStack.push(20);
std::cout << intStack.pop() << std::endl; // Outputs: 20

Stack<std::string> stringStack;
stringStack.push("Hello");
stringStack.push("World");
std::cout << stringStack.pop() << std::endl; // Outputs: World

Template Specialization:

cpp
// Primary template
template <typename T>
class DataProcessor {
public:
void process(T data) {
std::cout << "Processing generic data" << std::endl;
}
};

// Specialized template for int type
template <>
class DataProcessor<int> {
public:
void process(int data) {
std::cout << "Processing integer: " << data << std::endl;
}
};

// Usage
DataProcessor<double> dp1;
dp1.process(3.14); // Outputs: Processing generic data

DataProcessor<int> dp2;
dp2.process(42); // Outputs: Processing integer: 42

15. What is the Standard Template Library (STL)?

Answer: The Standard Template Library (STL) is a powerful set of C++ template classes that provide general-purpose classes and functions with templates to implement common data structures and algorithms.

Main Components of STL:

  1. Containers: Data structures that store objects and data

    • Sequence containers: vector, list, deque
    • Associative containers: set, map, multiset, multimap
    • Unordered containers: unordered_set, unordered_map, etc.
  2. Algorithms: Functions for operations on containers

    • Sorting: sort, partial_sort
    • Searching: find, binary_search
    • Modification: copy, move, transform
    • Numeric operations: accumulate, inner_product
  3. Iterators: Objects that connect algorithms with containers

    • Input iterators
    • Output iterators
    • Forward iterators
    • Bidirectional iterators
    • Random access iterators
cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

int main() {
// Container
std::vector<std::string> names = {"Alice", "Bob", "Charlie", "Dave"};

// Iterator
for (auto it = names.begin(); it != names.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;

// Algorithm
std::sort(names.begin(), names.end());

// Range-based for loop (uses iterators behind the scenes)
for (const auto& name : names) {
std::cout << name << " ";
}
std::cout << std::endl;

// More algorithms
auto it = std::find(names.begin(), names.end(), "Charlie");
if (it != names.end()) {
std::cout << "Found " << *it << std::endl;
}

// Using predicates with algorithms
auto count = std::count_if(names.begin(), names.end(),
[](const std::string& name) { return name.length() > 4; });
std::cout << "Names longer than 4 characters: " << count << std::endl;

return 0;
}

Modern C++ Features (C++11 and Beyond)

16. What are lambda expressions in C++?

Answer: Lambda expressions (introduced in C++11) allow you to define anonymous function objects inline, making code more concise and readable, especially when using STL algorithms.

Basic Syntax:

[capture](parameters) -> return_type { body }
  • Capture clause [ ]: Specifies which variables from the surrounding scope are available in the lambda
  • Parameter list ( ): Similar to function parameters
  • Return type -> type: Optional, compiler can deduce it in many cases
  • Function body { }: The actual code of the lambda

Examples:

cpp
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
// Simple lambda with no captures
auto greet = []() { std::cout << "Hello, World!" << std::endl; };
greet(); // Outputs: Hello, World!

// Lambda with parameters
auto add = [](int a, int b) { return a + b; };
std::cout << add(3, 4) << std::endl; // Outputs: 7

// Lambda with explicit return type
auto divide = [](double a, double b) -> double { return a / b; };
std::cout << divide(5, 2) << std::endl; // Outputs: 2.5

// Lambda with capture by value
int x = 10;
auto addX = [x](int a) { return a + x; };
std::cout << addX(5) << std::endl; // Outputs: 15

// Lambda with capture by reference
auto modifyX = [&x]() { x *= 2; };
modifyX();
std::cout << x << std::endl; // Outputs: 20

// Using lambdas with algorithms
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// Find first even number
auto firstEven = std::find_if(numbers.begin(), numbers.end(),
[](int n) { return n % 2 == 0; });
std::cout << "First even: " << *firstEven << std::endl; // Outputs: 2

// Count numbers greater than 5
int threshold = 5;
auto count = std::count_if(numbers.begin(), numbers.end(),
[threshold](int n) { return n > threshold; });
std::cout << "Numbers > 5: " << count << std::endl; // Outputs: 5

// Transform numbers
std::transform(numbers.begin(), numbers.end(), numbers.begin(),
[](int n) { return n * n; });

// Print transformed numbers
std::for_each(numbers.begin(), numbers.end(),
[](int n) { std::cout << n << " "; });
// Outputs: 1 4 9 16 25 36 49 64 81 100

return 0;
}

17. What is move semantics and how does it improve performance?

Answer: Move semantics (introduced in C++11) allow resources (like memory) to be transferred from one object to another without making expensive copies, particularly useful for managing dynamically allocated resources.

Key Components:

  1. Rvalue References: Denoted by Type&&, they refer to temporary objects or objects being moved from.

  2. Move Constructor: Constructs a new object by transferring resources from an existing object.

  3. Move Assignment Operator: Assigns to an existing object by transferring resources.

Benefits:

  • Eliminates unnecessary copying of resources
  • Significantly improves performance for large data structures
  • Enables efficient transfer of ownership
cpp
#include <iostream>
#include <vector>
#include <string>

class Resource {
private:
int* data;
size_t size;

public:
// Constructor
Resource(size_t sz) : size(sz) {
std::cout << "Allocating " << size << " integers" << std::endl;
data = new int[size];
}

// Destructor
~Resource() {
std::cout << "Freeing resource with " << size << " integers" << std::endl;
delete[] data;
}

// Copy constructor (expensive)
Resource(const Resource& other) : size(other.size) {
std::cout << "Copy constructor: copying " << size << " integers" << std::endl;
data = new int[size];
std::copy(other.data, other.data + size, data);
}

// Move constructor (efficient)
Resource(Resource&& other) noexcept : data(nullptr), size(0) {
std::cout << "Move constructor: moving " << other.size << " integers" << std::endl;

// Steal the resources from other
data = other.data;
size = other.size;

// Leave other in a valid but empty state
other.data = nullptr;
other.size = 0;
}

// Move assignment operator
Resource& operator=(Resource&& other) noexcept {
std::cout << "Move assignment" << std::endl;
if (this != &other) {
// Free current resources
delete[] data;

// Steal resources from other
data = other.data;
size = other.size;

// Leave other in a valid but empty state
other.data = nullptr;
other.size = 0;
}
return *this;
}

size_t getSize() const { return size; }
};

// Function that returns by value
Resource createResource(size_t size) {
return Resource(size); // Return value optimization may apply
}

int main() {
std::cout << "1. Creating resource1 directly:
";
Resource resource1(1000);

std::cout << "
2. Creating resource2 by copying resource1 (expensive):
";
Resource resource2 = resource1; // Copy constructor

std::cout << "
3. Creating resource3 by moving from temporary (efficient):
";
Resource resource3 = createResource(2000); // Move constructor

std::cout << "
4. Moving resource2 to resource4:
";
Resource resource4(std::move(resource2)); // Explicit move with std::move

std::cout << "
Resource sizes:
";
std::cout << "resource1: " << resource1.getSize() << std::endl;
std::cout << "resource2: " << resource2.getSize() << " (empty after move)" << std::endl;
std::cout << "resource3: " << resource3.getSize() << std::endl;
std::cout << "resource4: " << resource4.getSize() << std::endl;

std::cout << "
End of scope, all resources will be freed
";
return 0;
}

18. What are auto and decltype in C++?

Answer: auto and decltype are type deduction features introduced in C++11 that make the code more concise and maintainable.

auto:

  • Automatically deduces the type of a variable from its initializer
  • Reduces redundancy when types are long or complex
  • Makes code more maintainable when types change
cpp
// Without auto
std::vector<int>::iterator it = vec.begin();
std::map<std::string, std::vector<int>>::const_iterator mapIt = myMap.find("key");

// With auto
auto it = vec.begin(); // Deduces std::vector<int>::iterator
auto mapIt = myMap.find("key"); // Deduces std::map<std::string, std::vector<int>>::const_iterator

decltype:

  • Determines the type of an expression at compile time
  • Useful for declaring variables with the same type as another expression
  • Important for template metaprogramming
cpp
int x = 10;
decltype(x) y = 20; // y is int

const int& z = x;
decltype(z) w = y;


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)