Search test library by skills or roles
⌘ K
C++ interview questions for freshers
1. What's the difference between `struct` and `class` in C++? It's like asking, 'Are they twins or just good friends?'
2. Can you explain what a pointer is in C++? Imagine it as a treasure map that tells you where something valuable is hidden.
3. What is the purpose of the `new` and `delete` keywords in C++? Think of them as tools to borrow and return toys from a big toy library.
4. What is a constructor in C++? Why is it useful? Imagine it's like setting up a new toy when you get it, so it's ready to play with.
5. What's the difference between pass by value and pass by reference? Consider sending a copy of your drawing versus letting your friend draw directly on your original.
6. Explain what is meant by function overloading in C++. It's like having your name, but people call you by different nicknames.
7. What is the purpose of the `const` keyword? Think of it as a rule that says, 'You can look, but don't touch!' about something.
8. What are header files and source files in C++? Imagine organizing toys where the box label is the header, and the actual toys are the source.
9. Describe what inheritance is in C++. It's when a toy robot gets new features from a toy car. What are the benefits?
10. What is polymorphism in C++? Consider a shape that can be drawn as a square or a circle. What are the different ways polymorphism can be achieved in C++?
11. What are namespaces in C++? How do they help? Think of them as different rooms in your house to prevent toy collisions.
12. Explain the difference between `==` and `=` in C++. One checks if things are the same, and the other assigns a value. Can you give an example for when it matters?
13. What is the Standard Template Library (STL) in C++? Consider it as a box of pre-built toy parts that you can use in many ways.
14. How does exception handling work in C++? Imagine if a toy breaks, how do you handle the problem gracefully without breaking all your other toys?
15. What are virtual functions in C++? Can you give an example of how virtual functions are useful with inheritance?
16. Explain the concept of memory leaks in C++. What could cause them and how can you prevent them?
17. What is a friend function in C++? Imagine a function that's allowed to play with the private parts of a toy, with the toy's permission.
18. Describe the purpose of the `static` keyword in C++. How does the meaning of `static` change when applied to a variable versus a function?
19. What are smart pointers in C++? Why and when should you use them? Consider them as toy handlers that automatically clean up toys when you're done.
20. Explain what is meant by RAII (Resource Acquisition Is Initialization). Why is RAII a good practice to follow in C++?
C++ interview questions for juniors
1. What is the difference between `class` and `struct` in C++?
2. Explain what a pointer is, and why is it useful?
3. What is the meaning of `const` keyword in C++?
4. What are the differences between pass by value, pass by reference, and pass by pointer?
5. What is the difference between `malloc` and `new`?
6. What does the term 'memory leak' mean in C++?
7. Explain the concept of inheritance.
8. What is function overloading?
9. What is the purpose of a constructor?
10. What is a destructor and when is it called?
11. What are virtual functions?
12. What is polymorphism, and how is it achieved in C++?
13. What are the access modifiers in C++ (public, private, protected) and how do they work?
14. Explain the Standard Template Library (STL). What are some common containers?
15. What is an iterator in C++?
16. What are templates in C++?
17. What is exception handling, and why is it important?
18. What is the difference between pre-increment and post-increment operators?
19. Describe the concept of namespaces in C++.
20. Explain what RAII (Resource Acquisition Is Initialization) is.
21. What are smart pointers and why are they used?
22. What are lambda expressions?
23. What is the purpose of the `static` keyword in C++?
24. What is operator overloading?
25. What are friend functions and friend classes?
26. Explain the concept of dynamic memory allocation.
27. What are some common debugging techniques in C++?
28. What is the difference between shallow copy and deep copy?
29. What is a header file, and why do we use them?
30. What are some of the new features introduced in C++11 or later standards that you find most useful?
C++ intermediate interview questions
1. What are virtual functions and why are they important in C++?
2. Explain the difference between shallow copy and deep copy.
3. What is the purpose of the `friend` keyword in C++?
4. How does exception handling work in C++? What are try, catch, and throw?
5. Describe the difference between `new` and `malloc` when allocating memory in C++.
6. What are namespaces in C++, and how do they help prevent naming collisions?
7. Explain the concept of operator overloading in C++ with an example.
8. What are smart pointers in C++, and why are they preferred over raw pointers?
9. Describe the difference between compile-time polymorphism and runtime polymorphism.
10. What is the Standard Template Library (STL) in C++? Give some examples of containers it provides.
11. Explain the concept of move semantics in C++11 and how it improves performance.
12. What is RTTI (Runtime Type Information) in C++ and when might you use it?
13. Describe the use of lambda expressions in C++11 and later.
14. Explain the concept of RAII (Resource Acquisition Is Initialization) and how it's used in C++.
15. What are templates in C++ and how do they enable generic programming?
16. How can you achieve thread safety in a multithreaded C++ application?
17. What are the different types of casting operators in C++ (static_cast, dynamic_cast, const_cast, reinterpret_cast) and when should each be used?
18. Explain what a virtual destructor is and why it's important in inheritance hierarchies.
19. Describe how you would implement a simple singleton pattern in C++.
20. What are variadic templates in C++ and how are they useful?
C++ interview questions for experienced
1. Explain the concept of RAII and how it prevents memory leaks in C++.
2. How does the C++ memory model support multithreading, and what are the key challenges?
3. Describe the difference between `std::move` and `std::forward`, and when should each be used?
4. What are the advantages and disadvantages of using exceptions for error handling in C++?
5. Explain the purpose of the `noexcept` specifier and its impact on code generation and exception safety.
6. How does the compiler resolve virtual function calls at runtime, and what are the performance implications?
7. Describe the SFINAE (Substitution Failure Is Not An Error) principle and provide an example of its use.
8. What are the differences between shared pointers, unique pointers and weak pointers? When do you use each?
9. Discuss the use cases for constexpr functions and variables in modern C++.
10. Explain the role of allocators in C++ and how they can be customized for specific memory management requirements.
11. How does the concept of perfect forwarding work, and why is it important for generic programming?
12. Describe the various ways to achieve compile-time polymorphism in C++, and compare their trade-offs.
13. What is the purpose of the `std::optional` type, and how does it improve code safety and readability?
14. Explain the concept of move semantics and its benefits for performance and resource management.
15. How can you prevent name collisions in large C++ projects?
16. Explain the difference between a function object (functor) and a lambda expression in C++.
17. Describe how you would implement a thread pool in C++ using standard library features.
18. Discuss the performance implications of using virtual inheritance compared to non-virtual inheritance.
19. Explain the purpose of static polymorphism and compare to dynamic polymorphism. Give an example of each
20. What is the difference between a shallow copy and a deep copy? How do you implement a deep copy?

90 C++ Interview Questions to Hire Top Engineers


Siddhartha Gunti Siddhartha Gunti

September 09, 2024


When evaluating C++ candidates, recruiters and hiring managers need a reliable way to assess their skills. It can be daunting to navigate the nuances of C++ and identify true expertise, as discussed in our skills required for a C++ developer blog post.

This blog post provides a curated list of C++ interview questions categorized by experience level, from freshers to experienced professionals, including a section of MCQs. It's designed to equip you with the right questions to gauge a candidate's understanding and practical abilities.

By using these questions, you can identify top C++ talent; for a more streamlined and objective assessment, consider using our C++ online test before the interview.

Table of contents

C++ interview questions for freshers
C++ interview questions for juniors
C++ intermediate interview questions
C++ interview questions for experienced
C++ MCQ
Which C++ skills should you evaluate during the interview phase?
3 Tips for Using C++ Interview Questions
Hire Top C++ Talent with Skills Assessments
Download C++ interview questions template in multiple formats

C++ interview questions for freshers

1. What's the difference between `struct` and `class` in C++? It's like asking, 'Are they twins or just good friends?'

In C++, the primary difference between struct and class lies in their default access specifier. Members of a struct are public by default, whereas members of a class are private by default. This means that if you don't explicitly specify an access specifier (like public:, private:, or protected:) for a member in a struct, it will be accessible from anywhere. Conversely, in a class, you'd need to explicitly declare members as public to make them accessible from outside the class.

Beyond the default access, they are essentially the same. Both struct and class can have member functions, inheritance, and can be used to create objects. In essence, struct is often used for simple data structures, while class is preferred for more complex objects with encapsulation.

2. Can you explain what a pointer is in C++? Imagine it as a treasure map that tells you where something valuable is hidden.

In C++, a pointer is a variable that stores the memory address of another variable. Think of it like a treasure map; the map itself isn't the treasure, but it tells you where to find the treasure. Similarly, the pointer isn't the actual data, but it holds the address of where the data is stored in memory.

Here's a simple example:

int number = 42;
int *pointerToNumber = &number; // pointerToNumber now holds the memory address of number

//To access the value at the address use the dereference operator *
int value = *pointerToNumber; //value will be 42
  • & is the "address-of" operator, giving you the memory address of a variable.
  • * when declaring a pointer variable (e.g., int *pointer) means "this variable will store a memory address of an integer". When used on an existing pointer (e.g., *pointerToNumber), it's the dereference operator which gets the value stored at that memory address.

3. What is the purpose of the `new` and `delete` keywords in C++? Think of them as tools to borrow and return toys from a big toy library.

In C++, new and delete are operators used for dynamic memory management. new is used to allocate memory on the heap (the "toy library"), which is a region of memory available to the program during runtime. When you use new, you're essentially borrowing a toy from the library. It returns a pointer to the newly allocated memory. For example: int* ptr = new int;.

delete is used to deallocate memory that was previously allocated with new. This returns the "toy" back to the library. If you don't use delete, the memory remains allocated, leading to a memory leak. For example: delete ptr; ptr = nullptr; (setting the pointer to nullptr after deleting is good practice to prevent dangling pointers).

4. What is a constructor in C++? Why is it useful? Imagine it's like setting up a new toy when you get it, so it's ready to play with.

In C++, a constructor is a special member function of a class that is automatically called when an object of that class is created. Its main purpose is to initialize the object's data members and perform any other necessary setup to ensure the object is in a valid and usable state.

Constructors are useful because they guarantee that objects are properly initialized before they are used. This prevents unpredictable behavior due to uninitialized data. Like setting up a new toy, it gets all the pieces in place so the program runs correctly from the beginning. For example: class MyClass { public: MyClass() { myVariable = 0; } int myVariable; }; ensures that myVariable is always 0 when a MyClass object is created.

5. What's the difference between pass by value and pass by reference? Consider sending a copy of your drawing versus letting your friend draw directly on your original.

Pass by value means a copy of the variable's value is passed to the function. Any modifications made to the parameter inside the function do not affect the original variable outside the function. Think of it like sending your friend a copy of your drawing. They can scribble all over the copy, but your original remains untouched.

Pass by reference, on the other hand, passes the memory address (a reference) of the variable to the function. Therefore, any changes made to the parameter inside the function directly affect the original variable outside the function. This is like letting your friend draw directly on your original drawing; their changes are permanent. In code (e.g., C++), this often involves pointers or reference variables. For example, void modify(int &x) uses pass by reference.

6. Explain what is meant by function overloading in C++. It's like having your name, but people call you by different nicknames.

Function overloading in C++ allows you to define multiple functions with the same name but different parameter lists (different number, types, or order of arguments) within the same scope. The compiler then chooses the appropriate function to call based on the arguments passed during the function call. It's like having one function name that can perform slightly different actions depending on the input.

For example:

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;}

Here, the add function is overloaded. One takes two int arguments, another takes two double arguments, and a third takes three int arguments. The compiler will select the appropriate add function based on the types and number of arguments you provide when you call it.

7. What is the purpose of the `const` keyword? Think of it as a rule that says, 'You can look, but don't touch!' about something.

The const keyword in programming is used to declare a variable or object as constant, meaning its value cannot be changed after it's initialized. It's like saying, "This is fixed; don't modify it!" This helps prevent accidental modifications that could lead to bugs.

Think of const as a promise to the compiler (and other developers) that a particular value will not be altered. It allows for optimizations during compilation and improves code readability by clearly indicating which values are meant to remain constant. For example:

const int max_size = 100;

This declares max_size as a constant integer with a value of 100. Any attempt to modify max_size later in the code will result in a compilation error.

8. What are header files and source files in C++? Imagine organizing toys where the box label is the header, and the actual toys are the source.

In C++, header files (e.g., .h or .hpp) are like the labels on toy boxes. They declare what's inside: functions, classes, variables, etc. They contain declarations but not the actual implementation. For example, a header might declare a function int add(int a, int b);. Think of it as a contract, telling other parts of your code what functionality is available. Headers are included using #include.

Source files (e.g., .cpp) are like the toy box itself, containing the actual toys or, in programming terms, the implementation of the functions and classes declared in the header. The int add(int a, int b) function declared in the header would be defined in the source file like this: int add(int a, int b) { return a + b; }. Source files are compiled to create object files, which are then linked together to form the executable.

9. Describe what inheritance is in C++. It's when a toy robot gets new features from a toy car. What are the benefits?

Inheritance in C++ is a mechanism where a new class (derived class) acquires properties and behaviors from an existing class (base class). The toy robot inheriting features from a toy car illustrates this: the robot, the derived class, gains functionalities (like moving on wheels) from the car, the base class. It's a way to create a hierarchical relationship between classes.

The benefits include code reusability – you don't have to rewrite code for similar functionalities. It also allows for polymorphism, where objects of different classes can be treated as objects of a common type (the base class). This leads to more organized and maintainable code because changes to the base class automatically propagate to derived classes (unless overridden). Also, inheritance helps in creating abstractions by defining common interfaces in the base class.

10. What is polymorphism in C++? Consider a shape that can be drawn as a square or a circle. What are the different ways polymorphism can be achieved in C++?

Polymorphism in C++ means "many forms." It allows objects of different classes to be treated as objects of a common type. Specifically, it enables you to call methods on an object without knowing its exact type at compile time. This allows writing generic code that can work with objects of different types.

There are a couple of ways to achieve polymorphism in C++:

  • Virtual Functions (Runtime Polymorphism): Using virtual functions in a base class allows derived classes to override the function's behavior. When calling the function through a base class pointer or reference, the actual function called depends on the object's runtime type. The shape example would use this:

    class Shape {
    public:
        virtual void draw() {
            // Default implementation or do nothing
        }
    };
    
    class Circle : public Shape {
    public:
        void draw() override {
            // Draw a circle
        }
    };
    
    class Square : public Shape {
    public:
        void draw() override {
            // Draw a square
        }
    };
    
  • Function Overloading (Compile-time polymorphism): Defining multiple functions with the same name but different parameter types within the same scope.

  • Operator Overloading (Compile-time polymorphism): Redefining the behavior of operators (e.g., +, -, *) for user-defined types.

  • Templates (Compile-time polymorphism): Using templates to write generic code that can work with different data types. The shape example is less relevant here, but templates can be useful if you have a generic drawing function that needs to handle different shape data types.

11. What are namespaces in C++? How do they help? Think of them as different rooms in your house to prevent toy collisions.

Namespaces in C++ are a way to group related entities like classes, functions, variables, etc., under a single name. They help in organizing code and prevent name collisions, especially when using multiple libraries or large projects. Think of them as creating different rooms (namespaces) in your house (code) to store similar things.

For example, you might have two functions named print, but one belongs to namespace Math and the other to Graphics. Without namespaces, this would cause a naming conflict. With namespaces, you can differentiate them using Math::print() and Graphics::print(). This improves code readability and maintainability.

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

namespace Graphics {
 void drawCircle() { /* ... */ }
}

12. Explain the difference between `==` and `=` in C++. One checks if things are the same, and the other assigns a value. Can you give an example for when it matters?

In C++, = is the assignment operator, and == is the equality operator. The assignment operator (=) assigns the value on the right-hand side to the variable on the left-hand side. The equality operator (==) compares the values on both sides and returns a boolean value (true or false) indicating whether they are equal.

It matters significantly in conditional statements. For example:

int x = 5;
if (x = 10) { // Assignment: x is assigned 10, and the condition evaluates to true (10 is non-zero)
  // This block will always execute, and x will be 10.
}

if (x == 10) { // Equality: Checks if x is equal to 10
  // This block will execute only if x is equal to 10.
}

In the first if statement, x is assigned the value 10, and the result of the assignment (10) is treated as a boolean true because it's non-zero. The code inside the if block will execute. In the second if statement, it will evaluate to true and execute the statements within.

13. What is the Standard Template Library (STL) in C++? Consider it as a box of pre-built toy parts that you can use in many ways.

The Standard Template Library (STL) in C++ is a collection of pre-built, generic classes and functions that provide common programming data structures and algorithms. Think of it as a toolbox filled with ready-to-use components, like containers (e.g., vector, list, map), algorithms (e.g., sort, find), and iterators (to traverse containers). These components are designed to work with various data types, promoting code reusability and efficiency.

Essentially, instead of writing your own implementations for common tasks like sorting or managing lists, you can directly use the STL components. This saves development time and ensures that you're using well-tested and optimized code. For example, std::vector provides dynamic array functionality, std::sort efficiently sorts elements, and std::map implements a dictionary-like structure.

14. How does exception handling work in C++? Imagine if a toy breaks, how do you handle the problem gracefully without breaking all your other toys?

Exception handling in C++ allows you to deal with errors or exceptional circumstances that arise during program execution. It employs try, catch, and throw blocks. A try block encloses code that might throw an exception. If an exception occurs within the try block, the program searches for a suitable catch block to handle it.

Analogy: Imagine a toy breaking. We try playing with it. If it breaks (throw an exception), instead of the whole playroom collapsing, we catch the broken toy, perhaps set it aside for repair (handle the exception), and continue playing with the other toys without disruption. The other toys represent the rest of the program, which continues executing normally because the error was gracefully handled. A basic code structure is shown below:

try {
  // Code that might throw an exception
  if (toy_breaks) {
    throw ToyBrokenException("Oh no, the toy is broken!");
  }
} catch (const ToyBrokenException& e) {
  // Handle the exception
  std::cerr << "Exception caught: " << e.what() << std::endl;
  // Perform cleanup or recovery actions
}

15. What are virtual functions in C++? Can you give an example of how virtual functions are useful with inheritance?

Virtual functions in C++ are member functions declared with the virtual keyword. Their primary purpose is to achieve runtime polymorphism, also known as dynamic dispatch. When a virtual function is called through a pointer or reference of the base class type, the actual function that gets executed is determined by the type of the object being pointed to or referenced, not the type of the pointer or reference itself.

Here's an example illustrating their usefulness:

class Animal {
public:
    virtual void makeSound() {
        std::cout << "Generic 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;
    }
};

int main() {
    Animal* animal1 = new Dog();
    Animal* animal2 = new Cat();

    animal1->makeSound(); // Output: Woof!
    animal2->makeSound(); // Output: Meow!

    delete animal1;
    delete animal2;
    return 0;
}

In this example, even though animal1 and animal2 are pointers of type Animal*, the makeSound() function that is actually called depends on whether the pointer points to a Dog object or a Cat object. This demonstrates how virtual functions enable you to treat objects of different classes in a uniform way while still allowing them to exhibit their specific behaviors.

16. Explain the concept of memory leaks in C++. What could cause them and how can you prevent them?

Memory leaks in C++ occur when dynamically allocated memory (using new) is no longer accessible to the program, but hasn't been deallocated (using delete or delete[]). This leads to wasted memory resources, potentially causing performance degradation or even program crashes. Causes include: forgetting to delete memory allocated with new, exceptions thrown before memory is deallocated, and losing track of pointers to allocated memory.

Prevention strategies involve: employing smart pointers (e.g., unique_ptr, shared_ptr) to automatically manage memory deallocation, using RAII (Resource Acquisition Is Initialization) to tie resource management to object lifetime, and being meticulous about matching every new with a corresponding delete. Also using memory leak detection tools can identify leaks during development.

17. What is a friend function in C++? Imagine a function that's allowed to play with the private parts of a toy, with the toy's permission.

In C++, a friend function is a function that is granted special access to the private and protected members of a class. Normally, only member functions of a class can access these members. A friend function, however, is not a member function of the class, but it's declared as a friend within the class definition. This declaration gives the friend function the right to access the class's private and protected parts, effectively bypassing the usual access restrictions.

Think of it like this: the class is a walled garden, and normally, only gardeners (member functions) can tend to the plants inside (private members). A friend function is like a visitor who's been given a special key to enter the garden and help out, even though they're not a gardener themselves. The class (the walled garden) explicitly grants this access through the friend keyword. Example: friend void someFunction(MyClass obj);

18. Describe the purpose of the `static` keyword in C++. How does the meaning of `static` change when applied to a variable versus a function?

In C++, the static keyword has different meanings depending on the context. When applied to a variable inside a function, it means the variable is initialized only once, and its value persists across multiple calls to the function. It has local scope but global lifetime. When static is applied to a variable outside a function (global scope), it means the variable has internal linkage; it is only visible within the same source file (translation unit) where it is defined.

When applied to a function, static also means the function has internal linkage, and it is only visible within the same source file where it's defined. This prevents naming conflicts and allows you to create functions with the same name in different source files. For example:

static int my_static_function() { return 0; }

19. What are smart pointers in C++? Why and when should you use them? Consider them as toy handlers that automatically clean up toys when you're done.

Smart pointers in C++ are classes that behave like regular pointers but provide automatic memory management. They act as toy handlers, ensuring toys (dynamically allocated memory) are cleaned up when no longer needed, preventing memory leaks. They achieve this through Resource Acquisition Is Initialization (RAII), where the smart pointer acquires the resource (memory) and automatically releases it in its destructor.

You should use smart pointers to manage dynamically allocated memory instead of raw pointers to avoid memory leaks and dangling pointers. Specifically, use std::unique_ptr for exclusive ownership, std::shared_ptr for shared ownership, and std::weak_ptr to observe std::shared_ptr without taking ownership. For example:

#include <memory>

void foo() {
 std::unique_ptr<int> ptr(new int(10)); // Automatic deletion when ptr goes out of scope
 // ... use ptr
}

20. Explain what is meant by RAII (Resource Acquisition Is Initialization). Why is RAII a good practice to follow in C++?

RAII (Resource Acquisition Is Initialization) is a programming technique where the acquisition of a resource (e.g., memory, file handles, network sockets) is tied to the lifetime of an object. When the object is created, the resource is acquired in the constructor. When the object goes out of scope and is destroyed, the resource is automatically released in the destructor. This ensures that resources are always released, regardless of how the code exits the scope (e.g., normal exit, exceptions).

RAII is a good practice in C++ because it helps prevent resource leaks and simplifies resource management. By automating resource release, it reduces the chances of forgetting to free resources manually, which can lead to memory leaks or other resource-related problems. RAII also integrates well with exception handling. When an exception is thrown, the stack is unwound, and destructors are called for objects that have gone out of scope, ensuring resources are properly released even in exceptional circumstances. For example:

class FileHandler {
  FILE* file;
public:
  FileHandler(const char* filename, const char* mode) {
    file = fopen(filename, mode);
    if (!file) {
      throw std::runtime_error("Could not open file");
    }
  }
  ~FileHandler() {
    if (file) {
      fclose(file);
    }
  }
  // other methods to interact with file
};

C++ interview questions for juniors

1. What is the difference between `class` and `struct` in C++?

The primary difference between class and struct in C++ lies in their default access specifier. For class, members are private by default, meaning they are only accessible from within the class itself or by friends. Conversely, for struct, members are public by default, meaning they are accessible from anywhere.

Essentially, you can use class and struct interchangeably, but the default access control changes the behavior. If you don't specify an access specifier, class defaults to private and struct defaults to public.

2. Explain what a pointer is, and why is it useful?

A pointer is a variable that stores the memory address of another variable. Instead of directly holding a value, it "points to" the location in memory where that value is stored. Think of it like an address to a house, rather than the house itself. For example, in C++, int *ptr; declares a pointer ptr that can store the address of an integer variable. You can assign it using the address-of operator: ptr = &my_int;.

Pointers are useful for several reasons. They allow you to:

  • Directly manipulate memory, which is essential for tasks like dynamic memory allocation (malloc in C, new in C++).
  • Pass large data structures efficiently to functions by passing their addresses instead of copying the entire structure. This saves memory and improves performance.
  • Implement data structures like linked lists and trees, where elements are connected through pointers. If you are working with memory intensive or performance critical applications or libraries, pointers will be essential.

3. What is the meaning of `const` keyword in C++?

The const keyword in C++ is a type qualifier that specifies that a variable's value cannot be changed after initialization. It essentially makes a variable read-only. This helps to enforce data integrity and prevent accidental modification of important values.

Using const offers several benefits: It improves code readability by clearly indicating which variables are intended to remain constant. It enables the compiler to perform optimizations, as it knows the value won't change. It also helps catch errors at compile time if an attempt is made to modify a const variable. const can be applied to variables, pointers, function parameters, and member functions of classes.

4. What are the differences between pass by value, pass by reference, and pass by pointer?

Pass by value creates a copy of the variable and passes it to the function. Modifications to the variable inside the function do not affect the original variable outside the function. Pass by reference passes the memory address of the original variable to the function. Therefore, any changes made to the variable inside the function directly affect the original variable. Finally, pass by pointer involves passing a pointer (which is a variable that stores the memory address) to the function. Like pass by reference, changes made through the pointer inside the function will affect the original variable. However, pointers offer more flexibility, allowing for operations like pointer arithmetic and null pointer handling.

5. What is the difference between `malloc` and `new`?

malloc and new are both used for dynamic memory allocation, but they differ significantly.

malloc is a C function that allocates a block of raw memory of a specified size in bytes. It returns a void*, which must be explicitly cast to the desired type. malloc does not perform any initialization or call constructors. free is used to deallocate memory allocated by malloc.

new is a C++ operator that allocates memory and constructs an object of a specified type. It automatically calculates the required memory size based on the object's type. new also calls the object's constructor to initialize it. The delete operator is used to deallocate memory allocated by new and calls the object's destructor. new is type-safe, meaning you don't need to cast the returned pointer.

6. What does the term 'memory leak' mean in C++?

In C++, a memory leak occurs when dynamically allocated memory is no longer accessible to the program but hasn't been freed using delete or delete[]. This means the memory is reserved but unusable, effectively wasting resources. Over time, repeated memory leaks can exhaust available memory, leading to program slowdown or even crashes.

Common causes include forgetting to delete memory allocated with new, losing track of pointers to allocated memory, or exceptions preventing the delete statement from executing. Tools like memory profilers and debuggers are used to detect and resolve memory leaks by identifying allocations without corresponding deallocations.

7. Explain the concept of inheritance.

Inheritance is a fundamental concept in object-oriented programming where a new class (subclass or derived class) is created from an existing class (base class or parent class). The subclass inherits properties and behaviors (attributes and methods) of the base class. This promotes code reusability, reduces redundancy, and establishes an "is-a" relationship between the classes.

For example, consider a Vehicle class with attributes like speed and color. A Car class can inherit from Vehicle, automatically gaining those attributes. Car can then add its own specific attributes, such as number_of_doors. Inheritance allows you to model real-world relationships and create a hierarchy of classes.

8. What is function overloading?

Function overloading is a feature in object-oriented programming where multiple functions can have the same name but different parameters. The compiler differentiates them based on the number, type, or order of arguments passed during the function call. This allows you to create functions that perform similar operations but accept different inputs.

For example, you might have an add() function that can add two integers, two floats, or an integer and a float. The specific add() function that gets called is determined by the arguments you provide. In languages like C++ and Java, function overloading is a common way to provide flexibility and expressiveness in your code. Languages like Python don't support function overloading in the same way, typically relying on default argument values and variable arguments (*args, **kwargs) to achieve similar results.

9. What is the purpose of a constructor?

The primary purpose of a constructor is to initialize the state of an object when it is created. It is a special method within a class that is automatically called when a new instance (object) of that class is created.

Constructors ensure that the object starts with valid and meaningful data. They can set default values for attributes, perform any necessary setup, or enforce constraints on the initial state of the object. If a class doesn't have a explicitly defined constructor, a default constructor (usually empty) is automatically provided by the compiler.

10. What is a destructor and when is it called?

A destructor is a special member function of a class that is automatically called when an object of that class goes out of scope or is explicitly deleted. Its primary purpose is to release any resources (memory, file handles, network connections, etc.) held by the object during its lifetime. It's named with a tilde () followed by the class name (e.g., `MyClass()`).

Destructors are called in the following situations:

  • When a local variable goes out of scope.
  • When an object is explicitly deleted using delete.
  • During stack unwinding in exception handling (if an exception is thrown while the object is in scope).
  • When a temporary object's lifetime ends.
  • When a program exits, and global/static objects are being destroyed. Destructors are called in the reverse order of constructors.

11. What are virtual functions?

Virtual functions are a core feature of polymorphism in object-oriented programming, primarily in languages like C++. They allow a derived class to override the behavior of a base class function. When a virtual function is called through a base class pointer or reference, the actual function executed is determined at runtime based on the object's actual type, not the pointer/reference type.

Here's a brief breakdown:

  • Purpose: Enable runtime polymorphism.
  • Declaration: Declared using the virtual keyword in the base class.
  • Override: Derived classes can provide their own implementation of the virtual function.
  • Dynamic Dispatch: The correct version of the function to call is resolved at runtime (dynamic dispatch).

12. What is polymorphism, and how is it achieved in C++?

Polymorphism means "many forms". In C++, it's the ability of a single function or operator to behave differently in different contexts. It allows objects of different classes to be treated as objects of a common type.

C++ achieves polymorphism through two main mechanisms:

  • Compile-time (static) polymorphism: Achieved through function overloading and operator overloading. The correct function to call is determined at compile time based on the arguments provided. It also uses Templates which allows functions or classes to operate with generic types.

  • Runtime (dynamic) polymorphism: Achieved through virtual functions and inheritance. This allows a derived class to override a base class's function, and the correct function to call is determined at runtime based on the actual type of the object using pointers or references.

    class Base {
    public:
        virtual void display() { std::cout << "Base class" << std::endl; }
    };
    
    class Derived : public Base {
    public:
        void display() override { std::cout << "Derived class" << std::endl; }
    };
    

13. What are the access modifiers in C++ (public, private, protected) and how do they work?

C++ access modifiers control the visibility and accessibility of class members (variables and methods). There are three main access modifiers:

  • public: Members declared as public are accessible from anywhere, both inside and outside the class. There are no restrictions on accessing public members.
  • private: Members declared as private are only accessible from within the same class. They cannot be accessed directly from outside the class, not even from derived classes. They implement data hiding.
  • protected: Members declared as protected are accessible from within the same class and from derived classes (subclasses). They are not accessible from outside the class hierarchy. protected access provides a level of access between public and private, allowing derived classes to inherit and use certain members while still preventing external access. For example:
class Base {
public:
    int publicVar; // Accessible from anywhere
private:
    int privateVar; // Only accessible from within Base
protected:
    int protectedVar; // Accessible from Base and derived classes
};

14. Explain the Standard Template Library (STL). What are some common containers?

The Standard Template Library (STL) is a set of C++ template classes providing common programming data structures and functions such as lists, stacks, arrays, etc. It's a library of container classes, algorithms, and iterators; it is a generic library, meaning its components are parameterized. This allows them to work with a variety of data types.

Some common STL containers include:

  • vector: A dynamic array.
  • list: A doubly-linked list.
  • deque: A double-ended queue.
  • set: A collection of unique elements.
  • map: A collection of key-value pairs where each key is unique. Often implemented as a red-black tree for efficient lookups.
  • unordered_map: Similar to map but implemented as a hash table for potentially faster (but not guaranteed) lookups.

15. What is an iterator in C++?

In C++, an iterator is an object that acts as a generalized pointer, allowing you to traverse elements in a container (like arrays, vectors, lists) without needing to know the underlying structure of that container. Think of it as a pointer that knows how to move to the next element in a collection.

Iterators provide a consistent way to access elements sequentially. They are characterized by operations like incrementing (++ to move to the next element), dereferencing (* to access the current element), and comparison (==, != to check if two iterators point to the same element or if an iterator has reached the end of the container). Standard Template Library (STL) algorithms use iterators extensively to operate on different container types.

16. What are templates in C++?

Templates in C++ are a powerful feature that enables generic programming. They allow you to write code that can work with different data types without having to write separate versions for each type.

Essentially, templates are blueprints for creating functions or classes. The actual type used is determined at compile time. This avoids code duplication and increases code reusability. You can have function templates and class templates. For example:

template <typename T>
T max(T a, T b) {
 return (a > b) ? a : b;
}

17. What is exception handling, and why is it important?

Exception handling is a mechanism to deal with runtime errors (exceptions) in a program gracefully, preventing the program from crashing. It involves identifying potential error-prone sections of code, wrapping them in try blocks, and providing catch blocks to handle specific exception types. Optionally, a finally block ensures code execution regardless of whether an exception occurred.

It's important because it enhances program robustness and reliability. Without it, an unhandled exception can lead to abrupt termination. Exception handling allows a program to recover from errors, log them for debugging, or perform cleanup actions before potentially shutting down or continuing operation smoothly. It helps create more user-friendly applications by preventing unexpected crashes and providing informative error messages.

18. What is the difference between pre-increment and post-increment operators?

The key difference lies in when the increment operation occurs relative to the expression's evaluation. Pre-increment (++x) increments the variable before its value is used in the expression. Post-increment (x++) increments the variable after its original value has been used in the expression.

For example:

int x = 5;
int y = ++x; // x is now 6, y is also 6 (pre-increment)

int a = 5;
int b = a++; // a is now 6, b is 5 (post-increment)

19. Describe the concept of namespaces in C++.

Namespaces in C++ are used to organize code into logical groups and prevent name collisions, especially in large projects or when using external libraries. They essentially create a scope for identifiers (variables, functions, classes, etc.), allowing you to use the same name in different namespaces without conflict.

For example, you can define a function print() within namespace A and another print() within namespace B. To access them, you would use the scope resolution operator (::) like A::print() and B::print(). You can also use the using keyword to bring names or entire namespaces into the current scope, simplifying access (e.g., using namespace A;). Code Example:

namespace A {
  void print() { /* ... */ }
}

namespace B {
  void print() { /* ... */ }
}

20. Explain what RAII (Resource Acquisition Is Initialization) is.

RAII (Resource Acquisition Is Initialization) is a C++ programming technique where resource management (like memory allocation, file handles, network sockets) is tied to the lifespan of an object. The key idea is that a resource is acquired during object construction (initialization) and automatically released during object destruction (destruction).

This guarantees resource release, even if exceptions are thrown, because destructors are always called when an object goes out of scope. This avoids resource leaks. For example:

class FileHandler {
  std::ofstream file;
public:
  FileHandler(const std::string& filename) : file(filename) {
    if (!file.is_open()) {
      throw std::runtime_error("Could not open file");
    }
  }
  ~FileHandler() { file.close(); }
  void writeData(const std::string& data) { file << data << std::endl; }
};

21. What are smart pointers and why are they used?

Smart pointers are classes that act like pointers, but also manage the memory they point to. They automatically deallocate the memory when the smart pointer is no longer needed, preventing memory leaks.

They are used because manual memory management with raw pointers in languages like C++ is error-prone. Smart pointers ensure proper resource management through RAII (Resource Acquisition Is Initialization). Common types include:

  • unique_ptr: Exclusive ownership.
  • shared_ptr: Shared ownership (reference counting).
  • weak_ptr: Non-owning observer of a shared_ptr.

22. What are lambda expressions?

Lambda expressions are anonymous functions, meaning they are functions without a name. They provide a concise way to create small, single-expression functions, often used for short-lived operations. Lambda expressions are particularly useful when passing functions as arguments to higher-order functions (functions that take other functions as arguments).

In many languages, they are defined using a specific syntax (e.g., lambda arguments: expression in Python or (arguments) -> expression in Java). They can take multiple arguments but usually consist of a single expression. Example: x -> x * 2 (Java), which represents a function that takes an argument x and returns x multiplied by 2.

23. What is the purpose of the `static` keyword in C++?

The static keyword in C++ has different meanings depending on the context in which it is used.

  • Within a function: It creates a static local variable. This variable is initialized only once and retains its value between function calls. It essentially has a lifetime that extends beyond the function's execution, unlike regular local variables.
  • Within a class: It creates static member variables and static member functions. Static member variables are shared by all instances of the class (only one copy exists), and they are not associated with any specific object. Static member functions can only access static member variables and can be called using the class name instead of an object (e.g., ClassName::staticFunction();). They don't have a this pointer.
  • Outside a class (global scope): It gives the variable or function internal linkage. This means the variable or function is only visible within the same translation unit (source file) in which it is declared. It prevents name collisions between different files in a project.

In essence, static controls the scope and lifetime of variables and functions.

24. What is operator overloading?

Operator overloading allows operators like +, -, *, /, ==, etc. to have different meanings depending on the data types of their operands. It essentially provides a way to redefine the behavior of operators for user-defined types (classes or structs). This can make code more readable and intuitive, as you can use familiar operators to perform operations that are specific to your custom data structures. For example, you might overload the + operator to add two Vector objects together.

Languages like C++, Python, and C# support operator overloading, while languages like Java do not. When implemented thoughtfully, it enhances code clarity. However, overuse can lead to confusion if the overloaded behavior is not intuitive or consistent with the operator's conventional meaning.

25. What are friend functions and friend classes?

Friend functions and friend classes are mechanisms in C++ that allow a function or class to access the private and protected members of another class. This is an exception to the typical access rules, granting specific external entities special privileges.

Specifically:

  • A friend function is a function that is not a member of a class, but is declared as a friend within the class. It can then access the private and protected members of that class.
  • A friend class is a class that is declared as a friend of another class. All member functions of the friend class can access the private and protected members of the class that declared it as a friend.

26. Explain the concept of dynamic memory allocation.

Dynamic memory allocation is a process where memory is allocated during the runtime of a program, as opposed to during compilation (static allocation). This allows programs to request memory as needed, making efficient use of system resources. Common functions include malloc (C), new (C++), and their corresponding deallocation counterparts like free and delete.

Key benefits include flexibility in handling varying data sizes and the ability to create data structures that grow or shrink during program execution. Without dynamic allocation, programs would need to pre-allocate a fixed amount of memory, potentially wasting space or facing limitations if the required memory exceeds the pre-allocated amount. Failure to properly deallocate memory can lead to memory leaks.

27. What are some common debugging techniques in C++?

Common debugging techniques in C++ include using a debugger (like GDB or Visual Studio Debugger) to step through code, inspect variables, and set breakpoints. Print statements (using std::cout) can help trace program execution and variable values. Code reviews and static analysis tools can also identify potential bugs early. Understanding and handling exceptions correctly is crucial.

Other useful techniques are using assertions to check for unexpected conditions, memory debugging tools (like Valgrind) to detect memory leaks and errors, and logging for recording program events and errors. Simplification and divide-and-conquer by isolating code snippets to reproduce bugs is helpful. For example, using techniques such as binary search to narrow down the section of problematic code. Reproducing the bug with the minimum amount of code is also helpful.

28. What is the difference between shallow copy and deep copy?

Shallow copy creates a new object but the new object contains references to the original elements. Modifying a mutable element within the copied object will affect the original object as well.

Deep copy creates a new object and recursively copies all the objects found in the original object. Changes made to a copy will not reflect in the original.

29. What is a header file, and why do we use them?

A header file is a file containing declarations of functions, classes, variables, and other program elements. In C and C++, it typically has a .h extension. We use header files to separate the interface (declarations) from the implementation (definitions) of our code, which promotes modularity and reusability.

Specifically, header files:

  • Allow us to declare functions and variables in one file and define them in another.
  • Enable code reuse by including the same declarations in multiple source files.
  • Improve compilation time by allowing the compiler to only parse the declarations when needed, rather than the entire implementation. This is achieved through the #include preprocessor directive. For example:
    #include "myheader.h" 
    

30. What are some of the new features introduced in C++11 or later standards that you find most useful?

Several C++11 and later features significantly improve code quality and development efficiency. One of the most useful is auto type deduction, which simplifies variable declarations and avoids redundant type specifications, especially with complex template types. For example:

auto x = compute_something(); // Type of x is deduced from the return type of compute_something()

Another crucial feature is range-based for loops, which provide a more concise and readable way to iterate through containers and arrays compared to traditional iterator-based loops. Furthermore, smart pointers such as std::unique_ptr and std::shared_ptr are invaluable for managing memory and preventing memory leaks. They automate resource management and make exception safety significantly easier to achieve.

C++ intermediate interview questions

1. What are virtual functions and why are they important in C++?

Virtual functions are member functions declared with the virtual keyword in a base class. Their primary importance lies in enabling runtime polymorphism, also known as dynamic dispatch. This means that when a derived class object is accessed through a base class pointer or reference, the correct version of the virtual function (the one defined in the derived class) is called, not the base class version. If the function is not virtual, the base class version is always called (static dispatch).

The key reasons for using virtual functions are:

  • Achieving Polymorphism: Allowing objects of different classes to be treated as objects of a common type, while still executing the correct behavior for their specific type.
  • Extensibility: Making code more flexible and easier to extend. New derived classes can be added without modifying existing code that uses base class pointers or references.
  • Abstract Classes: Virtual functions can be declared as pure virtual functions (virtual void foo() = 0;), making the base class abstract and forcing derived classes to implement the function. This defines an interface that all derived classes must adhere to.

2. Explain the difference between shallow copy and deep copy.

A shallow copy creates a new object, but it copies references to the objects contained within the original object. This means that if you modify a nested object in the copy, the corresponding object in the original also changes.

In contrast, a deep copy creates a new object and recursively copies all of the objects found within the original. Changes to the copy's nested objects will not affect the original. In Python, you can create a deep copy using copy.deepcopy(). Shallow copies are typically faster, but deep copies provide more isolation.

3. What is the purpose of the `friend` keyword in C++?

The friend keyword in C++ grants a function or class access to the private and protected members of another class. This breaks encapsulation to a degree, but allows controlled access in specific cases.

It's useful when a function or class needs privileged access to another class's internals without being a member of that class. For example, a matrix class could declare a vector class as a friend so the vector class could directly manipulate the matrix's internal data. Using friend should be done cautiously, as excessive use can weaken encapsulation and make code harder to maintain. Use it only when there's a clear and justifiable reason for bypassing normal access restrictions.

4. How does exception handling work in C++? What are try, catch, and throw?

Exception handling in C++ allows you to deal with runtime errors gracefully. It uses try, catch, and throw keywords. A try block encloses the code that might throw an exception. If an exception occurs within the try block, the control is transferred to a catch block. The catch block is designed to handle a specific type of exception.

The throw keyword is used to signal an exception. When an exception is thrown, the C++ runtime searches for a matching catch block to handle it. If no matching catch block is found, the program terminates. Here's a simple example:

try {
  // Code that might throw an exception
  if (error_condition) {
    throw std::runtime_error("An error occurred");
  }
}
catch (const std::runtime_error& e) {
  // Handle the exception
  std::cerr << "Exception caught: " << e.what() << std::endl;
}

5. Describe the difference between `new` and `malloc` when allocating memory in C++.

new and malloc are both used for dynamic memory allocation, but they differ significantly in C++. new is a C++ operator that allocates memory and also calls the constructor of the object being created, ensuring proper initialization. It returns a pointer of the correct type. When the object is no longer needed, delete should be used to deallocate the memory, which also calls the object's destructor.

malloc, on the other hand, is a C function that simply allocates a block of raw, uninitialized memory of a specified size in bytes. It returns a void*, which must be explicitly cast to the desired type. malloc does not call constructors. To free memory allocated with malloc, free must be used. Failing to match new with delete or malloc with free can lead to memory leaks or other undefined behavior.

6. What are namespaces in C++, and how do they help prevent naming collisions?

Namespaces in C++ are declarative regions that provide a scope to the names (identifiers) inside it. They help organize code and avoid naming collisions, especially in large projects or when using multiple libraries. By enclosing code within a namespace, you essentially create a distinct area where names can be defined without conflicting with names in other namespaces or the global scope.

For example, suppose two libraries both define a function called print. Without namespaces, this would lead to a collision. However, if one library defines print within namespace LibraryA and the other within LibraryB, you can use LibraryA::print() and LibraryB::print() to specifically call the desired function. This avoids ambiguity and allows the use of multiple libraries with potentially conflicting names. using namespace declarations can simplify access to names within a namespace.

7. Explain the concept of operator overloading in C++ with an example.

Operator overloading in C++ allows you to redefine the meaning of operators (like +, -, *, /, ==, etc.) for user-defined data types (classes). This enables you to use operators with objects of your classes in a natural and intuitive way.

For example, consider a Point class representing a point in 2D space. You can overload the + operator to add two Point objects, resulting in a new Point object whose coordinates are the sum of the corresponding coordinates of the original points:

class Point {
public:
    int x, y;
    Point(int x = 0, int y = 0) : x(x), y(y) {}
    Point operator+(const Point& other) const {
        return Point(x + other.x, y + other.y);
    }
};

int main() {
    Point p1(1, 2);
    Point p2(3, 4);
    Point p3 = p1 + p2; // Uses the overloaded + operator
    // p3.x will be 4, p3.y will be 6
}

8. What are smart pointers in C++, and why are they preferred over raw pointers?

Smart pointers in C++ are classes that act like pointers but automatically manage the memory they point to. This helps prevent memory leaks and dangling pointers. They are preferred over raw pointers because raw pointers require manual memory management using new and delete, which is error-prone.

Smart pointers offer automatic memory management through RAII (Resource Acquisition Is Initialization). When a smart pointer goes out of scope, it automatically releases the memory it manages. The main types are:

  • unique_ptr: Exclusive ownership.
  • shared_ptr: Shared ownership through reference counting.
  • weak_ptr: Non-owning observer of a shared_ptr.

Using smart pointers leads to safer and more robust code by eliminating the need to manually manage memory, thus reducing the risk of memory leaks and improving exception safety. Example:

#include <memory>

int main() {
  std::unique_ptr<int> ptr(new int(10));
  // Memory is automatically released when ptr goes out of scope
  return 0;
}

9. Describe the difference between compile-time polymorphism and runtime polymorphism.

Compile-time polymorphism, also known as static polymorphism or early binding, is achieved through function overloading and operator overloading. The compiler knows at compile time which function to call based on the number and type of arguments. It enhances performance because the function call is resolved during compilation.

Runtime polymorphism, also known as dynamic polymorphism or late binding, is primarily achieved through virtual functions and inheritance. The decision of which function to call is made at runtime based on the actual object type. This allows for more flexibility but might introduce slight overhead due to runtime resolution (e.g., vtable lookup).

10. What is the Standard Template Library (STL) in C++? Give some examples of containers it provides.

The Standard Template Library (STL) is a set of template classes and functions in C++ that provide common programming data structures and algorithms. It offers reusable components, making C++ code more efficient and easier to maintain. The STL is a generic library, meaning its components work with various data types.

Some examples of containers provided by the STL include:

  • vector: A dynamic array.
  • list: A doubly-linked list.
  • deque: A double-ended queue.
  • set: A collection of unique elements.
  • map: A collection of key-value pairs (associative array).
  • unordered_map: Similar to map, but with no guaranteed ordering and typically faster access times (hash table implementation).
  • stack: A LIFO (Last-In, First-Out) data structure.
  • queue: A FIFO (First-In, First-Out) data structure.

11. Explain the concept of move semantics in C++11 and how it improves performance.

Move semantics in C++11 allows transferring ownership of resources from one object to another without copying. This is particularly useful when dealing with temporary objects or objects that are about to be destroyed. Instead of performing a deep copy, the resources (e.g., dynamically allocated memory) are simply 'moved' to the new object, and the original object is left in a valid but indeterminate state. The move constructor and move assignment operator facilitate this process.

Performance improves significantly, especially with large objects, because unnecessary copying is avoided. Copying involves allocating new memory and duplicating the data, while moving simply involves updating pointers. For example, consider a std::vector. Copying a large vector can be time-consuming. Move semantics allows the vector's internal pointer to the underlying data to be transferred to a new vector, avoiding the costly copy operation. This is commonly used when returning objects by value from functions. For instance, std::move can be used to explicitly invoke move semantics.

12. What is RTTI (Runtime Type Information) in C++ and when might you use it?

RTTI (Runtime Type Information) is a C++ mechanism that allows you to determine the type of an object during runtime. It is primarily enabled through the dynamic_cast operator and the typeid operator. dynamic_cast safely converts pointers or references to base class types into pointers or references to derived class types, and typeid returns a std::type_info object representing the type of an expression.

You might use RTTI when you need to perform operations that depend on the actual type of an object, especially when dealing with polymorphism. For example, if you have a base class pointer and you need to call a specific function that only exists in a particular derived class, you could use dynamic_cast to check if the object is of that derived type before calling the function. Be cautious though, over reliance on RTTI can sometimes indicate a design flaw where polymorphism isn't being fully utilized.

13. Describe the use of lambda expressions in C++11 and later.

Lambda expressions in C++11 and later provide a concise way to create anonymous function objects (closures). They allow you to define functions inline, often where they are used, improving code readability and maintainability. Lambdas are particularly useful when working with algorithms that require function objects as arguments, like std::sort, std::for_each, or std::transform.

Key features include: capture lists (specifying variables from the surrounding scope to be available inside the lambda), parameter lists (like regular functions), return type deduction (or explicit specification), and the function body. They can be used to create simple, single-use functions without the need for formal function definitions.

For example, a simple lambda to add two numbers:

auto add = [](int x, int y) { return x + y; };
int result = add(5, 3); // result is 8

14. Explain the concept of RAII (Resource Acquisition Is Initialization) and how it's used in C++.

RAII (Resource Acquisition Is Initialization) is a C++ programming technique where resource management (like memory, file handles, sockets, etc.) is tied to the lifetime of an object. The core idea is that a resource is acquired when an object is constructed (initialized), and it is automatically released when the object is destroyed (goes out of scope).

In C++, RAII is typically implemented using classes whose constructors acquire resources and whose destructors release them. This ensures resources are always cleaned up properly, even in the presence of exceptions, preventing resource leaks. Smart pointers (like std::unique_ptr and std::shared_ptr) are prime examples of RAII in action, managing dynamically allocated memory.

15. What are templates in C++ and how do they enable generic programming?

Templates in C++ are a powerful feature that enables generic programming. They allow you to write code that can work with different data types without having to rewrite the code for each type. Essentially, templates are blueprints for creating functions or classes that operate on generic types.

Templates facilitate generic programming by using type parameters. When you define a template, you specify placeholder types. The compiler then generates specific versions of the function or class at compile time based on the actual types used when the template is instantiated. This avoids code duplication and promotes code reusability, improving efficiency and maintainability. For example, a template function template <typename T> T max(T a, T b) can find the maximum of any type for which the > operator is defined. Template classes operate similarly; a std::vector<T> uses a template to create a vector of integers, strings, or any other defined type.

16. How can you achieve thread safety in a multithreaded C++ application?

Thread safety in C++ multithreaded applications can be achieved using several mechanisms. Common approaches include:

  • Mutexes (Mutual Exclusion): Protect shared resources by allowing only one thread to access them at a time. Use std::mutex for locking and unlocking, and std::lock_guard or std::unique_lock for RAII-style locking.
  • Read-Write Locks: Allow multiple threads to read a shared resource concurrently, but only one thread to write at a time. std::shared_mutex can be used for this purpose (C++17 and later).
  • Atomic Operations: Use atomic variables (std::atomic<T>) for simple operations that need to be thread-safe without the overhead of mutexes. Useful for counters or flags.
  • Condition Variables: Allow threads to wait for a specific condition to become true. Used in conjunction with mutexes. std::condition_variable provides methods like wait(), notify_one(), and notify_all().
  • Thread-Local Storage: Each thread has its own copy of a variable. Achieved using thread_local keyword, eliminating the need for synchronization.
  • Avoiding Shared Mutable State: Whenever possible, design your application to minimize or eliminate shared mutable data. Immutable data structures or message passing can simplify thread safety.

17. What are the different types of casting operators in C++ (static_cast, dynamic_cast, const_cast, reinterpret_cast) and when should each be used?

C++ provides four main casting operators: static_cast, dynamic_cast, const_cast, and reinterpret_cast. static_cast performs non-polymorphic conversions, such as converting between numeric types or related class types (compile-time check). Use it when you know the conversion is safe and well-defined. dynamic_cast is used for polymorphic conversions (runtime check), primarily when downcasting pointers or references within an inheritance hierarchy. It returns nullptr if the cast fails (for pointers) or throws an exception for references. const_cast is specifically designed to add or remove the const or volatile qualifiers from a type. Use it only when you absolutely need to modify a const object and are certain it's safe to do so (the object wasn't originally declared const). reinterpret_cast is the most dangerous cast, as it simply reinterprets the bits of one type as another type without any type checking. Use it only for low-level operations like converting between pointers and integers or when dealing with hardware.

18. Explain what a virtual destructor is and why it's important in inheritance hierarchies.

A virtual destructor ensures that when a derived class object is deleted through a base class pointer, the derived class's destructor is also called. Without a virtual destructor, only the base class's destructor would be called, leading to potential resource leaks if the derived class allocated memory or held other resources.

Consider a scenario where a Base class pointer points to a Derived class object. If delete base_pointer is called and Base's destructor is not virtual, only Base's destructor runs. If Derived allocated memory, that memory won't be freed, resulting in a memory leak. By declaring virtual ~Base() {}, you guarantee that ~Derived() will also be called before ~Base() is called, preventing resource leaks and ensuring proper cleanup.

19. Describe how you would implement a simple singleton pattern in C++.

To implement a singleton in C++, you typically make the constructor private to prevent direct instantiation. A static member variable of the class type holds the single instance, and a static method provides access to it. The first time the static method is called, it creates the instance; subsequent calls return the existing one. Here's a basic example:

class Singleton {
private:
    Singleton() {}
    static Singleton* instance;
public:
    static Singleton* getInstance() {
        if (!instance) {
            instance = new Singleton();
        }
        return instance;
    }
};

Singleton* Singleton::instance = nullptr;

This approach isn't thread-safe. For thread safety, you'd need to add locking mechanisms (e.g., using mutexes) to the getInstance() method to prevent race conditions during instance creation in a multi-threaded environment. Meyers Singleton is another way to implement singleton in C++ which takes care of thread safety.

20. What are variadic templates in C++ and how are they useful?

Variadic templates are a C++ template feature that allows a template to accept a variable number of arguments. This is achieved using a parameter pack, which is a template parameter that represents zero or more template arguments. They are declared using the ellipsis ... syntax (e.g., template<typename... Args>).

They are useful for:

  • Creating functions that can accept a variable number of arguments, like printf or a custom logging function.
  • Implementing generic data structures or algorithms that can work with different types and numbers of elements without needing to write separate code for each specific case. For example, std::tuple is implemented using variadic templates.
  • Performing compile-time computations involving a variable number of arguments through recursion or folding techniques.

Example:

template<typename... Args>
void print_all(Args... args) {
    (std::cout << ... << args) << std::endl;
}

C++ interview questions for experienced

1. Explain the concept of RAII and how it prevents memory leaks in C++.

RAII (Resource Acquisition Is Initialization) is a C++ programming technique where resource management (like memory allocation, file handling, etc.) is tied to the lifetime of an object. The constructor of the object acquires the resource, and the destructor releases the resource. This ensures that resources are automatically released when the object goes out of scope, regardless of how the scope is exited (e.g., normal completion, exception thrown).

RAII prevents memory leaks because the destructor, which releases the allocated memory, is guaranteed to be called when the object is destroyed. Consider using smart pointers like std::unique_ptr or std::shared_ptr, which are RAII wrappers around raw pointers. When the smart pointer goes out of scope, it automatically deletes the underlying memory, preventing leaks. For example: std::unique_ptr<int> ptr(new int(5)); // Memory automatically released when ptr goes out of scope.

2. How does the C++ memory model support multithreading, and what are the key challenges?

The C++ memory model defines how threads interact with memory, ensuring data consistency in multithreaded programs. It provides guarantees about when writes made by one thread become visible to other threads. Key elements include:

  • Atomics: Atomic variables provide synchronization primitives that guarantee atomic read-modify-write operations, preventing data races.
  • Memory Ordering: Specifies the constraints on how memory operations can be reordered by the compiler and processor. Common orderings include std::memory_order_relaxed, std::memory_order_acquire, std::memory_order_release, std::memory_order_acq_rel, and std::memory_order_seq_cst.
  • Happens-Before Relationship: Defines a partial ordering of operations across threads. If operation A happens-before operation B, then the effects of A are visible to B.

Challenges in multithreading arise from:

  • Data Races: Occur when multiple threads access the same memory location concurrently, and at least one thread is writing. The memory model provides tools (atomics, mutexes) to prevent these. Example:

    #include <atomic>
    std::atomic<int> counter = 0; //instead of regular int counter
    counter++;
    
  • Deadlocks: Situations where two or more threads are blocked indefinitely, waiting for each other to release resources.

  • Livelocks: Threads repeatedly attempt to acquire locks but fail, preventing progress.

  • False Sharing: Threads operating on different data items that happen to reside within the same cache line, leading to unnecessary cache invalidation and performance degradation.

3. Describe the difference between `std::move` and `std::forward`, and when should each be used?

std::move unconditionally casts its argument to an rvalue reference. It's used to enable moving from an object, signaling that the object's resources can be transferred. After std::move, the moved-from object should be left in a valid but unspecified state.

std::forward, on the other hand, is a conditional cast that only casts its argument to an rvalue reference if the argument was initialized with an rvalue. It's primarily used in template functions to perfectly forward arguments to other functions, preserving their original value category (lvalue or rvalue). Use std::move when you explicitly want to transfer ownership. Use std::forward when you want to forward arguments without changing their value category, typically in generic code.

4. What are the advantages and disadvantages of using exceptions for error handling in C++?

Exceptions in C++ provide a structured way to handle errors, offering advantages like improved code readability by separating error handling logic from the main code flow. They also ensure that errors aren't ignored, as unhandled exceptions terminate the program, forcing developers to address them. Exceptions can also automatically unwind the stack, calling destructors for objects that go out of scope, preventing resource leaks (RAII).

However, exceptions have disadvantages. They can introduce performance overhead, especially if exceptions are frequently thrown and caught. This is because the compiler needs to generate extra code for stack unwinding and exception handling. Exception specifications, deprecated in C++11 and removed in C++17, also added complexity without significant benefit. In some cases, using return codes for error handling can be more efficient and predictable. Also, exception handling can lead to complex control flow, making debugging and reasoning about program behavior more challenging.

5. Explain the purpose of the `noexcept` specifier and its impact on code generation and exception safety.

The noexcept specifier in C++ is used to indicate that a function will not throw exceptions. Its primary purpose is to help the compiler optimize the code generation by allowing it to avoid generating exception handling code for that function. This can lead to smaller and faster code, especially in performance-critical sections.

When a function is declared noexcept, the compiler can make certain assumptions about its behavior. If a noexcept function does throw an exception, std::terminate is called, immediately halting program execution. This offers a strong guarantee to the caller that the function will either complete successfully or terminate the program, which can simplify exception safety guarantees. Also, moves are preferred over copies, providing stronger exception safety.

6. How does the compiler resolve virtual function calls at runtime, and what are the performance implications?

The compiler resolves virtual function calls at runtime using a mechanism called the Virtual Table (vtable). Each class with virtual functions has a vtable, which is an array of function pointers, one for each virtual function in the class and its base classes. Each object of that class has a pointer (vptr) to the vtable of its class. When a virtual function is called through a base class pointer or reference, the compiler uses the vptr to find the appropriate vtable and then uses the vtable to call the correct function. This is known as dynamic dispatch.

The performance implications involve a slight overhead. There's the memory overhead of storing the vptr in each object and the vtable for each class. There's also a runtime overhead because instead of a direct function call, there's an indirect call involving the vptr dereference and vtable lookup. However, this overhead is generally small compared to the benefits of polymorphism and dynamic behavior, especially in complex systems. Modern processors also employ techniques like branch prediction that mitigate some of the performance impact. The cost of the virtual function call is generally one or two extra memory dereferences.

7. Describe the SFINAE (Substitution Failure Is Not An Error) principle and provide an example of its use.

SFINAE (Substitution Failure Is Not An Error) is a principle in C++ template metaprogramming. It states that when substituting template arguments into a function template or class template, if the substitution results in an invalid type or expression, it's not an error. Instead, the compiler simply removes that function or class from the overload set or template argument list.

Example:

template <typename T>
auto check_has_member(int) -> decltype(std::declval<T>().member, std::true_type{}); // Valid if T has member

template <typename T>
std::false_type check_has_member(...); // Fallback

struct HasMember { int member; };
struct NoMember {};

static_assert(decltype(check_has_member<HasMember>(0))::value == true, "HasMember has member");
static_assert(decltype(check_has_member<NoMember>(0))::value == false, "NoMember doesn't have member");

In this example, check_has_member uses SFINAE to check if a type T has a member named member. If T doesn't have member, the first overload is invalid and removed from consideration, and the compiler chooses the second overload returning std::false_type.

8. What are the differences between shared pointers, unique pointers and weak pointers? When do you use each?

Smart pointers are used to manage dynamically allocated memory safely. Unique pointers (std::unique_ptr) provide exclusive ownership; only one unique pointer can point to an object at a time. When the unique pointer goes out of scope, the object is automatically deleted. Use them when you want to ensure that only one owner exists for a resource. Shared pointers (std::shared_ptr) allow multiple pointers to point to the same object, using a reference count to track the number of owners. The object is deleted only when the last shared pointer goes out of scope. They are useful when multiple parts of your program need to share ownership of a resource. Weak pointers (std::weak_ptr) provide a non-owning reference to an object managed by a shared pointer. They don't increment the reference count and can be used to detect if the object has been deleted. Use them to observe an object managed by a shared pointer without taking ownership, preventing circular dependencies.

9. Discuss the use cases for constexpr functions and variables in modern C++.

constexpr functions and variables are evaluated at compile time, offering significant performance benefits and enabling compile-time computations. Key use cases include:

  • Compile-time constants: Defining constants whose values are known at compile time, enhancing code optimization and enabling use in contexts requiring compile-time evaluation (e.g., array sizes, template arguments).
  • Metaprogramming: Performing complex calculations and logic at compile time, reducing runtime overhead. For instance, computing factorials or Fibonacci numbers at compile time.
  • Embedded systems: Ensuring code predictability and determinism, critical in resource-constrained environments where runtime performance is paramount.
  • Generic Programming: constexpr functions and variables can be used in templates to perform computations based on template arguments, allowing for more flexible and efficient code generation. For example, consider a function that calculates the size of a static array based on a template parameter. template <size_t N> constexpr size_t get_array_size() { return N * 2; }
  • Improved Performance: By shifting computations to compile time, programs can avoid runtime overhead, leading to faster execution. This is especially beneficial for frequently used calculations or those that depend on constant values. For example:
constexpr int square(int n) { return n * n; }
int main() {
  int arr[square(5)]; // Array size known at compile time
}

10. Explain the role of allocators in C++ and how they can be customized for specific memory management requirements.

Allocators in C++ are responsible for encapsulating memory allocation and deallocation strategies. They decouple memory management from the objects that use the memory. The standard library containers (like std::vector, std::list, etc.) use allocators to handle memory needs, allowing you to specify custom allocation behaviors without changing the container's logic.

Custom allocators can be created to optimize memory usage for specific scenarios. For example, you might implement an allocator that uses a memory pool to reduce fragmentation or one that allocates memory from a specific region. To create a custom allocator, you need to define a class that meets the allocator requirements, which involves providing types like value_type, pointer, reference, and implementing the allocate() and deallocate() methods. The standard library provides std::allocator_traits to help ensure your custom allocator meets the standard's requirements and to facilitate interoperability.

11. How does the concept of perfect forwarding work, and why is it important for generic programming?

Perfect forwarding allows you to pass arguments to another function while preserving their original type and value category (lvalue or rvalue). It's achieved using std::forward. Essentially, it avoids unnecessary copies and conversions when you're writing generic functions or templates that need to work with a variety of argument types.

In generic programming, perfect forwarding is crucial because it enables you to write functions that can accept any type of argument and forward them to another function as if they were passed directly. This is particularly important when dealing with move semantics and rvalue references, where preserving the original type and value category is essential for performance and correctness. Without it, you might lose the ability to move objects, leading to unnecessary copies and potentially incorrect behavior.

12. Describe the various ways to achieve compile-time polymorphism in C++, and compare their trade-offs.

Compile-time polymorphism in C++ is primarily achieved through templates and function overloading.

  • Templates: Enable writing generic code that works with different data types without the need for explicit type specification. The compiler generates specific code instances for each used data type. This offers high performance, but can lead to code bloat if many specializations are created. Templates are very powerful for generic programming.
  • Function Overloading: Allows defining multiple functions with the same name but different parameter lists. The compiler selects the appropriate function based on the argument types at compile time. This increases code readability and flexibility, but is limited to variations in argument types. Trade-offs include potential ambiguity if overloaded functions have similar parameter types, requiring careful design.

13. What is the purpose of the `std::optional` type, and how does it improve code safety and readability?

std::optional is a type introduced in C++17 to represent a value that may or may not be present. It's a container that can hold either a value of a specified type T, or nothing at all. Its purpose is to provide a clear and type-safe way to represent optional values, avoiding the ambiguity and potential errors associated with using sentinel values (like nullptr or magic numbers) to indicate absence.

std::optional improves code safety and readability by:

  • Explicitly expressing optionality: Makes it clear in the function signature or variable declaration that a value might be missing.
  • Avoiding null pointer dereferences: Eliminates the risk of dereferencing a null pointer because the optional must be checked before accessing the value.
  • Improving code clarity: By using std::optional, you avoid using potentially ambiguous sentinel values. std::optional has methods like has_value() and value() to explicitly check and retrieve the value, making the code easier to understand.
  • Providing compile-time safety: std::optional enforces type safety, preventing accidental use of absent values, leading to more robust code.

14. Explain the concept of move semantics and its benefits for performance and resource management.

Move semantics allows transferring ownership of resources from one object to another without copying them, which significantly boosts performance, especially for large objects. Instead of creating a complete copy (which can be expensive), the resources are 'moved' to the new owner, and the original object is left in a valid but typically empty state. This avoids unnecessary memory allocation and deallocation.

The key benefit is avoiding deep copies. Consider a std::vector. Copying it creates a new vector and copies all its elements. Moving, however, simply transfers the pointer to the underlying data buffer to the new vector and sets the original vector's pointer to nullptr. This is much faster. Move semantics also enable efficient resource management, preventing resource leaks and improving code efficiency by minimizing redundant operations. Move constructors and move assignment operators (e.g., MyClass(MyClass&& other) and MyClass& operator=(MyClass&& other)) are used to implement move semantics.

15. How can you prevent name collisions in large C++ projects?

Name collisions in large C++ projects can be prevented primarily through the use of namespaces. Encapsulating code within namespaces creates distinct scopes, effectively avoiding conflicts between identifiers (variables, functions, classes) that might otherwise share the same name. For example, you might have namespace my_library containing all the code specific to your library. Also consider using class namespacing, for example MyLibrary::MyClass. Furthermore, a good file structure helps organize code and provides visual context to developers.

Other techniques include using descriptive and consistent naming conventions, and following the principle of least privilege; limiting the scope of variables and functions as much as possible. Avoid using using namespace in header files, as it can introduce unintended namespace pollution in files that include the header. Instead, either fully qualify the names (e.g., std::cout) or use using declarations (e.g., using std::cout;) within specific scopes.

16. Explain the difference between a function object (functor) and a lambda expression in C++.

A function object (or functor) is a class object that overloads the operator(). This allows you to call an object of that class as if it were a function. For example:

struct MyFunctor {
    int operator()(int x) {
        return x * 2;
    }
};

MyFunctor my_functor;
int result = my_functor(5); // Calls the operator() overload

A lambda expression, on the other hand, is a concise way to define an anonymous function object inline. Lambdas are typically used for short, simple operations and can capture variables from their surrounding scope. A lambda expression like [](int x){ return x * 2; } is equivalent to defining a function object, but it's more compact and can be defined directly where it's used. The compiler generates an unnamed functor class behind the scenes for lambda expressions.

17. Describe how you would implement a thread pool in C++ using standard library features.

A thread pool can be implemented in C++ using std::thread, std::mutex, std::condition_variable, and a queue. A queue (e.g., std::queue or std::deque) stores tasks (typically function objects). A set of worker threads continuously monitors this queue. A mutex protects access to the queue, and a condition variable signals the threads when new tasks are added.

Each worker thread performs the following steps in a loop: acquire the mutex, check if there are any tasks in the queue. If empty, the thread waits on the condition variable, releasing the mutex. If there are tasks, it removes one, releases the mutex, executes the task, and repeats. When the thread pool is shut down, a signal is sent to all waiting threads via the condition variable, allowing them to exit gracefully. The std::future can be used to retrieve results from the tasks.

18. Discuss the performance implications of using virtual inheritance compared to non-virtual inheritance.

Virtual inheritance introduces overhead compared to non-virtual inheritance. This overhead primarily stems from the need to resolve the correct base class subobject at runtime. In non-virtual inheritance, each derived class directly contains its base class subobjects, leading to a straightforward and efficient memory layout. However, with virtual inheritance, a virtual base class is shared among multiple derived classes, typically implemented using virtual function tables (vtables) and virtual pointers (vpointers) or similar mechanisms.

This runtime resolution incurs costs in terms of both memory and speed. Memory is used to store the vtable and vpointer. Speed is impacted because accessing members of the virtual base class requires an extra level of indirection through the vpointer to the vtable, and then to the appropriate offset within the shared base class subobject. This contrasts with non-virtual inheritance where the address of the base class member can be determined at compile time with a simple offset. Although the performance impact might be minimal in many cases, it can become noticeable in performance-critical sections of code or when dealing with deeply nested inheritance hierarchies involving multiple virtual base classes. For example, the size of objects will be larger and member access will require additional indirections.

19. Explain the purpose of static polymorphism and compare to dynamic polymorphism. Give an example of each

Static polymorphism (also known as compile-time polymorphism) is achieved through function overloading and operator overloading. The specific function to be executed is determined at compile time based on the function signature (the number and type of arguments). Dynamic polymorphism (also known as run-time polymorphism) is achieved through virtual functions and inheritance. The specific function to be executed is determined at run time based on the actual type of the object.

Example of static polymorphism (function overloading in C++):

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

Example of dynamic polymorphism (virtual functions in C++):

class Animal {
public:
virtual void makeSound() { std::cout << "Generic animal sound" << std::endl; }
};
class Dog : public Animal {
public:
void makeSound() override { std::cout << "Woof!" << std::endl; }
};
int main() {
Animal* animal = new Dog();
animal->makeSound(); // Calls Dog::makeSound() at runtime
}

20. What is the difference between a shallow copy and a deep copy? How do you implement a deep copy?

A shallow copy creates a new object but references the same memory locations as the original object for the data it contains. Modifying the copied object might affect the original. A deep copy, on the other hand, creates a completely independent object and recursively copies all the data, including nested objects, to new memory locations. Changes to the copy will not affect the original.

Deep copies can be implemented in a few ways, like using JSON.parse(JSON.stringify(object)) for objects containing serializable data. Another way is by recursively traversing the object and creating new instances of each nested object. For custom objects with complex structures, you might need to implement a custom deep copy method tailored to the object's specific properties and how they are structured in memory.

C++ MCQ

Question 1.

Consider the following C++ code:

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

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

int main() {
    Base* ptr = new Derived();
    ptr->print();
    delete ptr;
    return 0;
}

What is the output of this code?

Options:
Question 2.

Consider the following C++ code:

#include <iostream>

class Base {
public:
    Base() { std::cout << "Base Constructor" << std::endl; }
    ~Base() { std::cout << "Base Destructor" << std::endl; }
};

class Derived : public Base {
public:
    Derived() { std::cout << "Derived Constructor" << std::endl; }
    ~Derived() { std::cout << "Derived Destructor" << std::endl; }
};

int main() {
    Derived d;
    return 0;
}

What is the order of constructor and destructor calls when the Derived object d is created and goes out of scope?

Options:
Question 3.

What happens if you allocate memory using new in C++ but forget to deallocate it using delete?

Options:
Question 4.

Consider the following C++ code:

class MyClass {
public:
    static int count;
    MyClass() { count++; }
};

int MyClass::count = 0;

int main() {
    MyClass obj1;
    MyClass obj2;
    std::cout << MyClass::count << std::endl;
    return 0;
}

What will be the output of this program?

Options:
Question 5.

Which of the following statements is most accurate regarding friend functions in C++?

Options:

Options:
Question 6.

Consider the following C++ code:

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

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

int main() {
    Base* ptr = new Derived();
    ptr->print();
    return 0;
}

What will be the output of this program?

Options:
Question 7.

What happens if an exception is thrown inside a try block, and there is no matching catch block to handle it in the immediate scope?

Options:
Question 8.

Consider the following C++ code:

class Shape {
public:
    virtual double area() = 0; 
};

class Circle : public Shape {
double radius;
public:
    Circle(double r) : radius(r) {}
    double area() { return 3.14 * radius * radius; }
};

class Square : public Shape {
double side;
public:
    Square(double s) : side(s) {}
    double area() { return side * side; }
};

int main() {
    Shape* shapePtr; 
    // some code here
    return 0;
}

Which of the following statements, when inserted into main(), will cause a compilation error?

Options:
Question 9.

Consider the following C++ class definition:

class MyClass {
private:
  int value;
public:
  MyClass(int val) : value(val) {}
  int getValue() const {
    return value;
  }
  void setValue(int val) {
    value = val;
  }
};

const MyClass obj(10);

Which of the following statements about obj is true?

Options:
Question 10.

What is the primary purpose of using class templates in C++?

Options:

Options:
Question 11.

What is the primary purpose of std::move in C++11 and later?

Options:

Options:
Question 12.

Consider the following C++ code snippet:

class Animal {
public:
    Animal() { std::cout << "Animal constructor" << std::endl; }
    virtual void speak() { std::cout << "Generic animal sound" << std::endl; }
};

class Mammal : virtual public Animal {
public:
    Mammal() { std::cout << "Mammal constructor" << std::endl; }
};

class Bird : virtual public Animal {
public:
    Bird() { std::cout << "Bird constructor" << std::endl; }
};

class Bat : public Mammal, public Bird {
public:
    Bat() { std::cout << "Bat constructor" << std::endl; }
};

What is the primary purpose of using virtual inheritance in the Mammal and Bird classes when inheriting from Animal? Select the correct option.

Options:
Question 13.

Consider the following C++ code:

#include <iostream>

class Complex {
private:
 double real, imag;
public:
 Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}

 Complex operator+(const Complex& other) const {
 return Complex(real + other.real, imag + other.imag);
 }

 Complex operator*(const Complex& other) const {
 return Complex(real * other.real - imag * other.imag, real * other.imag + imag * other.real);
 }

 friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
 os << c.real << " + " << c.imag << "i";
 return os;
 }
};

int main() {
 Complex c1(1.0, 2.0);
 Complex c2(2.0, 3.0);
 Complex c3 = c1 * c2 + c1;
 std::cout << c3 << std::endl;
 return 0;
}

What will be the output of this program?

Options:
Question 14.

Consider the following C++ code snippet using std::unique_ptr with a custom deleter:

#include <iostream>
#include <memory>

struct Resource {
    int data;
    Resource(int d) : data(d) { std::cout << "Resource acquired: " << data << std::endl; }
    ~Resource() { std::cout << "Resource released: " << data << std::endl; }
};

struct MyDeleter {
    void operator()(Resource* res) {
        std::cout << "Custom deleter called for: " << res->data << std::endl;
        delete res;
    }
};

int main() {
    std::unique_ptr<Resource, MyDeleter> ptr(new Resource(42));
    return 0;
}

What will be the output of this program?

Options:
Question 15.

What is object slicing in C++ and what are its consequences?

Options:

Options:
Question 16.

What is the primary effect of returning a local object by value from a function in C++?

Options:
Question 17.

What is the size of object c in bytes, given the following C++ code?

#include <iostream>

class A {};

class B : public A {};

class C : public B {};

int main() {
  C c;
  std::cout << sizeof(c) << std::endl;
  return 0;
}

options:

Options:
Question 18.

What is the state of memory after executing the following C++ code snippet? Assume no other variables are in scope.

#include <iostream>
#include <memory>

struct Node {
    std::shared_ptr<Node> next;
    ~Node() { std::cout << "Node destroyed" << std::endl; }
};

int main() {
    std::shared_ptr<Node> node1 = std::make_shared<Node>();
    std::shared_ptr<Node> node2 = std::make_shared<Node>();

    node1->next = node2;
    node2->next = node1;

    return 0;
}

options:

Options:
Question 19.

Consider the following C++ code:

class MyClass {
public:
    int *data;
    MyClass(int val) : data(new int(val)) {}
    ~MyClass() { delete data; }

    //What is the correct implementation for copy constructor?
};

Which of the following copy constructors will ensure deep copy and prevent dangling pointers after copying objects of MyClass?

Options:
Question 20.

What is the output of the following C++ code?

#include <iostream>
#include <functional>

int main() {
 int x = 5;
 std::function<int(int)> adder = [x](int y) { return x + y; };
 x = 10;
 std::cout << adder(3) << std::endl;
 return 0;
}

Options:

Options:
Question 21.

Consider the following C++ code:

#include <iostream>

class Engine {
public:
    Engine() { std::cout << "Engine Constructed\n"; }
    ~Engine() { std::cout << "Engine Destroyed\n"; }
};

class Car {
public:
    Car() : engine() { std::cout << "Car Constructed\n"; }
    ~Car() { std::cout << "Car Destroyed\n"; }
private:
    Engine engine;
};

int main() {
    Car myCar;
    return 0;
}

What is the output of this program?

Options:
Question 22.

Consider the following C++ code:

class Base {
public:
    Base() { std::cout << "Base constructor\n"; }
    ~Base() { std::cout << "Base destructor\n"; }
};

class Derived : public Base {
public:
    Derived() { std::cout << "Derived constructor\n"; }
    ~Derived() { std::cout << "Derived destructor\n"; }
};

int main() {
    Base* ptr = new Derived();
    delete ptr;
    return 0;
}

What is the output of this program if the destructor of Base is NOT virtual?

options:

Options:
Question 23.

What is the primary advantage of using function objects (functors) over regular functions in C++ when performing operations like sorting or searching with the Standard Template Library (STL)?

Options:
Question 24.

Consider the following C++ code:

#include <iostream>

void foo(double d) {
    std::cout << "foo(double)" << std::endl;
}

void foo(int i) {
    std::cout << "foo(int)" << std::endl;
}

int main() {
    float f = 3.14f;
    foo(f);
    return 0;
}

What will be the output of this program?

Options:
Question 25.

Consider the following C++ code snippet:

#include <iostream>
#include <type_traits>

template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
void process(T value) {
    std::cout << "Processing an integer: " << value << std::endl;
}

template <typename T, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void process(T value) {
    std::cout << "Processing a float: " << value << std::endl;
}

int main() {
    process(5);  
    process(5.5);
}

What is the output of this program?

Options:

Options:

Which C++ skills should you evaluate during the interview phase?

Assessing a candidate's complete skillset in a single interview is challenging. However, for C++, certain core skills are more important to evaluate. Focusing on these key areas will help you identify candidates with the right C++ proficiency.

Which C++ skills should you evaluate during the interview phase?

Object-Oriented Programming (OOP)

Gauge a candidate's OOP understanding with targeted MCQs. An assessment like Adaface's C++ test can filter candidates based on their OOP knowledge.

To further assess OOP skills, ask targeted interview questions. This allows candidates to explain their understanding and apply the concepts.

Explain the difference between inheritance and polymorphism with a practical example.

Look for a clear explanation of both concepts. The candidate should also provide an example demonstrating how they are used in real-world scenarios, showcasing their practical grasp of OOP.

Memory Management

Use MCQs to assess understanding of memory allocation, deallocation, and smart pointers. You can also use C++ online test to test these skills.

Pose interview questions to evaluate a candidate's practical knowledge of memory management. This helps to understand their approach to potential issues.

Describe common memory management problems in C++ and how to avoid them.

Look for awareness of memory leaks, dangling pointers, and buffer overflows. The candidate should also describe how techniques such as RAII and smart pointers can mitigate these risks.

Data Structures and Algorithms

Use MCQs that test knowledge of common data structures (arrays, linked lists, trees, etc.) and algorithmic complexity. This will help filter out candidates who lack a solid foundation.

Use specific interview questions to assess practical application of these concepts. Understand how they relate to real-world problems.

Describe a situation where you would choose a hash table over a binary search tree.

The ideal response would explain the trade-offs between the two data structures. It should also consider factors such as search speed, insertion/deletion costs, and memory usage.

3 Tips for Using C++ Interview Questions

Before you start putting what you've learned into practice, here are a few tips to help you use C++ interview questions more effectively. These tips will help you optimize your interview process and identify the best candidates.

1. Leverage C++ Skills Assessments

To streamline your hiring process, consider using skills assessments before interviews. This will help you filter candidates based on their technical abilities.

Adaface offers a range of C++ and related tests to evaluate candidates. These include a general C++ online test, as well as assessments for C and embedded C. We even have assessments for other languages like C# if that is also relevant. These tests offer objective insights into a candidate's coding proficiency.

Using these tests allows you to focus your interview time on candidates who have demonstrated a solid understanding of C++ concepts. This approach saves time and resources, ensuring that you're only interviewing the most qualified individuals.

2. Curate a Focused Set of Interview Questions

Time is limited during interviews, so it’s important to ask the most relevant questions. Carefully select a set of questions that target the most important aspects of C++ development for your specific role.

Consider including questions related to data structures or system design, depending on the requirements of the position. See more interview questions related to data structure.

By focusing on key areas, you can maximize your evaluation of candidates on the most critical skills.

3. Ask Effective Follow-Up Questions

Don't rely solely on initial answers; asking follow-up questions is key to gauge a candidate's true understanding. This helps you determine the depth of their knowledge and their ability to apply concepts in practical situations.

For example, if a candidate explains a concept like polymorphism, a follow-up question could be: "Can you describe a real-world scenario where polymorphism would be particularly useful in C++?" This can reveal if they truly understand the practical implications of polymorphism.

Hire Top C++ Talent with Skills Assessments

Looking to hire C++ developers? Accurately evaluating their C++ skills is key to making the right hiring decisions. Using skills tests like the C++ Online Test or the C Online Test can provide an objective measure of a candidate's abilities.

Once you've used skills tests to identify top candidates, you can confidently proceed to interviews. Streamline your hiring process with our online assessment platform and sign up to get started.

C++ Online Test

40 mins | 10 MCQs and 1 Coding Question
The C++ Online Test uses scenario-based and code tracing MCQ questions to evaluate a candidate's ability to write C++ programs (data types, functions, data structures, STL), structure the code using Object-oriented programming principles (classes, inheritance, polymorphism, overloading), handle exceptions and manage memory. The test uses coding questions to evaluate hands-on C++ coding skills.
Try C++ Online Test

Download C++ interview questions template in multiple formats

C++ Interview Questions FAQs

What are some good C++ interview questions for freshers?

Good questions for freshers include those that test their understanding of basic C++ concepts like data types, operators, control structures, and simple object-oriented programming principles.

What C++ concepts should junior developers know?

Junior developers should have a solid grasp of pointers, memory management, classes, inheritance, polymorphism, and basic data structures and algorithms in C++.

What should I ask intermediate C++ developers?

For intermediate developers, focus on questions related to design patterns, templates, exception handling, the Standard Template Library (STL), and multi-threading.

What kind of questions can I ask experienced C++ developers?

Experienced C++ developers can be asked about system design, optimization techniques, advanced memory management, C++ standards, and their experience with large-scale projects.

Why use C++ interview questions?

C++ interview questions help gauge a candidate's understanding of the language, their problem-solving skills, and their ability to apply C++ concepts in real-world scenarios.

What's the best way to assess C++ skills?

Skills assessments, combined with targeted interview questions, provide a well-rounded evaluation of a candidate's C++ expertise. Skills assessments can help filter candidates quickly, and the interview can further help find the right fit.

Related posts

Free resources

customers across world
Join 1200+ companies in 80+ countries.
Try the most candidate friendly skills assessment tool today.
g2 badges
logo
40 min tests.
No trick questions.
Accurate shortlisting.