C++ Interview Questions - FAQs
1. What is the difference between deep copy and shallow copy?
Shallow copy copies the values of the members of one object into another object, including pointers. Both objects will point to the same memory locations. Deep copy creates a new object and copies the values of the members, including dynamically allocated memory, into the new object.
// Shallow copy
class ShallowCopy {
public:
int* data;
};
ShallowCopy obj1;
obj1.data = new int(5);
ShallowCopy obj2 = obj1; // Shallow copy
// Both obj1 and obj2 point to the same memory location
// Changing the value through either object affects both
*(obj1.data) = 10;
// Deep copy
class DeepCopy {
public:
int* data;
DeepCopy(const DeepCopy& other) {
data = new int(*(other.data));
}
};
DeepCopy obj3;
obj3.data = new int(5);
DeepCopy obj4 = obj3; // Deep copy
// obj3 and obj4 have separate memory locations
// Changing the value through one object does not affect the other
*(obj3.data) = 10;
2. Explain the concept of Virtual Inheritance?
Virtual inheritance is used when a class wants to inherit from a base class through multiple paths, and there is a need to avoid ambiguity and prevent duplicate instances of the base class. It ensures that only one instance of the base class is inherited by the most derived class.
class Base {
public:
int value;
};
class Derived1 : public virtual Base {
};
class Derived2 : public virtual Base {
};
class MostDerived : public Derived1, public Derived2 {
};
MostDerived obj;
obj.value = 5;
3. What are the different types of polymorphism in C++
There are two types of polymorphism in C++: compile-time (or static) polymorphism and runtime (or dynamic) polymorphism.
- Compile-time polymorphism is achieved through function overloading and operator overloading. The compiler selects the appropriate function or operator based on the arguments and their types at compile time.
void add(int a, int b) { /* addition implementation */ }
void add(double a, double b) { /* addition implementation */ }
add(5, 10); // Calls add(int, int)
add(3.14, 2.71); // Calls add(double, double)
Runtime polymorphism is achieved through inheritance and virtual functions. The appropriate function is called based on the object's dynamic type at runtime.
class Shape {
public:
virtual void draw() { /* default drawing implementation */ }
};
class Circle : public Shape {
public:
void draw() { /* circle drawing implementation */ }
};
class Square : public Shape {
public:
void draw() { /* square drawing implementation */ }
};
Shape* shapePtr = new Circle();
shapePtr->draw(); // Calls Circle's draw() function
4. How does the "const" keyword work in C++?
- The "const" keyword in C++ is used to declare constants or to indicate that a function or method does not modify the object's state.
- When "const" is used to declare a variable, it means that the value of that variable cannot be changed.
const int MAX_VALUE = 100;
// MAX_VALUE cannot be modified after initialization
int x = 5;
const int* ptr = &x;
// The value pointed to by ptr cannot be modified through ptr
const int y = 10;
// y cannot be modified directly
5. What is the purpose of the "volatile" keyword in C++?
The "volatile" keyword in C++ is used to indicate that a variable may be modified by external factors that are beyond the control of the program. It informs the compiler not to perform certain optimizations on that variable. It is commonly used when working with hardware registers, multi-threading, or in situations where a variable's value can change unexpectedly.
volatile int sensorValue;
// Indicates that the sensorValue may change unexpectedly
volatile bool flag = false;
// Indicates that the flag can be modified by external factors
6. What is the difference between function overloading and function overriding in C++?
Function overloading refers to having multiple functions with the same name but different parameter lists within the same scope. The compiler determines the correct function to call based on the number, types, and order of arguments provided.
void print(int value) { /* print an integer value */ }
void print(double value) { /* print a double value */ }
Function overriding occurs when a derived class provides its own implementation of a function that is already defined in the base class. It is achieved through inheritance and virtual functions. The function in the derived class must have the same name, return type, and parameters as the base class function.
class Base {
public:
virtual void print() { /* base class print implementation */ }
};
class Derived : public Base {
public:
void print() override { /* derived class print implementation */ }
};
7. How are templates used in C++?
Templates in C++ allow for generic programming by creating functions or classes that can work with multiple data types.
Here's an example of a template function that finds the maximum value:
Here's an example of a template function that finds the maximum value:
template
T max(T a, T b) {
return (a > b) ? a : b;
}
int result1 = max(10, 20); // Returns 20
double result2 = max(3.14, 2.71); // Returns 3.14
8. What is the difference between struct and class in C++?
In C++, a struct and a class are very similar. The main difference is that members in a struct are public by default, while members in a class are private by default.
9. Explain the concept of multiple inheritance in C++?
Multiple inheritance in C++ allows a class to inherit from multiple base classes. This means that the derived class inherits the members (variables and functions) from all the base classes.
class Base1 {
public:
void method1() { /* implementation of method1 */ }
};
class Base2 {
public:
void method2() { /* implementation of method2 */ }
};
class Derived : public Base1, public Base2 {
};
Derived obj;
obj.method1(); // Call method1 from Base1
obj.method2(); // Call method2 from Base2
10. What is the "this" pointer in C++?
The "this" pointer in C++ is a special pointer that holds the memory address of the current object. It is implicitly available within the member functions of a class. It is used to refer to the members (variables and functions) of the current object and disambiguate between local variables and class members that have the same name.
class Person {
public:
void setName(std::string name) {
this->name = name; // "this" is used to refer to the member variable
}
private:
std::string name;
};
Person person;
person.setName("John");
11. How can you implement a singleton pattern in C++?
The singleton pattern in C++ ensures that only one instance of a class can be created throughout the program.
Here's a simple implementation of a singleton class:
Here's a simple implementation of a singleton class:
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
// Other member functions...
private:
Singleton() {} // Private constructor to prevent direct instantiation
Singleton(const Singleton&) = delete; // Disable copy constructor
Singleton& operator=(const Singleton&) = delete; // Disable assignment operator
};
Singleton& instance = Singleton::getInstance();
12. What are the difference between the "new" operator and the "malloc()" in C++?
- The "new" operator and the "malloc()" function both allocate memory, but they have some differences:
- "new" is an operator in C++ and "malloc()" is a function in C.
- "new" allocates memory and also calls the constructor of the object, while "malloc()" only allocates memory.
- "new" returns a pointer to the allocated object's type, while "malloc()" returns a void pointer (void*) that needs to be cast explicitly.
- "new" throws an exception if the allocation fails, while "malloc()" returns a null pointer (nullptr) on failure.
13. What is an abstract class in C++ and why is it required?
An abstract class in C++ is a class that cannot be instantiated. It is meant to serve as a base class for other classes and provides a common interface or set of methods that derived classes must implement. It is used to achieve abstraction and provide a blueprint for derived classes to follow. An abstract class typically contains one or more pure virtual functions, which are declared with the "= 0" syntax and have no implementation.
class Shape {
public:
virtual void draw() = 0; // Pure virtual function
// Other member functions...
};
class Circle : public Shape {
public:
void draw() override {
// Implementation of draw() for Circle
}
// Other member functions...
};
14. What is friend function?
A friend function in C++ is a function that is granted access to the private and protected members of a class. It is declared inside the class but is not a member of the class.
class MyClass {
private:
int data;
public:
friend void printData(const MyClass& obj);
};
void printData(const MyClass& obj) {
std::cout << obj.data << std::endl; // Can access the private member data
}
MyClass obj;
printData(obj);
15. What is inline function in C++?
An inline function in C++ is a function that is expanded at the point of its invocation, similar to a macro. It is a suggestion to the compiler to replace the function call with the actual function code to improve performance.
inline int add(int a, int b) {
return a + b;
}
int result = add(5, 10); // Replaced with the code: result = 5 + 10;
16. What is Function Pointer in C++?
Function pointers in C++ allow storing the address of a function and invoking it indirectly through the pointer. They are often used for callbacks, dynamic dispatch, and implementing data structures like function tables.
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int (*operation)(int, int); // Function pointer declaration
operation = &add; // Point to add function
int result1 = operation(5, 3); // Invokes add(5, 3)
operation = &subtract; // Point to subtract function
int result2 = operation(8, 2); // Invokes subtract(8, 2)
17. What is the difference between delete and delete[]?
- "delete" is used to deallocate memory that was allocated using the "new" operator for a single object.
- "delete[]" is used to deallocate memory that was allocated using the "new[]" operator for an array of objects.
18. What is an exception in C++?
An exception in C++ is an event that occurs during the execution of a program, which disrupts the normal flow of program execution.
It allows for handling and recovering from exceptional conditions, such as errors or unexpected situations, in a structured manner.
Exceptions are thrown using the "throw" keyword and can be caught and handled using try-catch blocks.
It allows for handling and recovering from exceptional conditions, such as errors or unexpected situations, in a structured manner.
Exceptions are thrown using the "throw" keyword and can be caught and handled using try-catch blocks.
try {
int result = divide(10, 0); // Division by zero will throw an exception
} catch (const std::exception& e) {
std::cout << "Exception occurred: " << e.what() << std::endl;
}
19. What is the difference between the "private", "protected" and "public" access specifiers?
- The access specifiers in C++ control the visibility and accessibility of class members.
- "private" members are only accessible within the class itself.
- "protected" members are accessible within the class and its derived classes.
- "public" members are accessible from anywhere in the program.
class MyClass {
private:
int privateData;
protected:
int protectedData;
public:
int publicData;
};
MyClass obj;
obj.publicData = 10; // Accessible
obj.protectedData = 20; // Not accessible outside the class or derived classes
obj.privateData = 30; // Not accessible outside the class
20. What are the differences between the prefix and postfix increment operators in C++??
The prefix increment operator (++x) increments the value of the variable and then returns the incremented value.
The postfix increment operator (x++) returns the current value of the variable and then increments it.
The postfix increment operator (x++) returns the current value of the variable and then increments it.
21. How can you implement a deep copy constructor in C++?
To implement a deep copy constructor in C++, you need to allocate new memory and copy the contents of the dynamically allocated resources from the source object to the new object.
class MyClass {
private:
int* data;
public:
// Deep copy constructor
MyClass(const MyClass& other) {
data = new int(*other.data);
}
// Other member functions...
};
22. Explain the concept of operator overloading in C++?
Operator overloading in C++ allows you to define how operators behave when used with user-defined types.
It enables you to extend the functionality of operators to work on objects of your own classes.
By overloading operators, you can define custom behaviors for operations such as addition (+), subtraction (-), comparison (==, !=), and more.
It enables you to extend the functionality of operators to work on objects of your own classes.
By overloading operators, you can define custom behaviors for operations such as addition (+), subtraction (-), comparison (==, !=), and more.
class Vector {
private:
int x, y;
public:
Vector(int x, int y) : x(x), y(y) {}
// Overloading the addition operator
Vector operator+(const Vector& other) const {
return Vector(x + other.x, y + other.y);
}
// Other member functions...
};
Vector v1(2, 3);
Vector v2(4, 5);
Vector result = v1 + v2; // Calls the overloaded '+' operator
23. How can you prevent a derived class from overriding a virtual function in C++?
To prevent a derived class from overriding a virtual function in C++, you can use the final keyword in the base class declaration.
When a function is marked as final, it cannot be overridden by any derived class.
When a function is marked as final, it cannot be overridden by any derived class.
class Base {
public:
virtual void foo() final {
// Implementation
}
};
class Derived : public Base {
public:
// Error: Attempting to override a final function
// void foo() override {}
};
24. How can you implement a custom iterator in C++?
To implement a custom iterator in C++, you need to define a class that satisfies the requirements of the iterator concept.
The class should provide the necessary member functions, such as operator++, operator*, operator->, and operator==, to enable iteration over a container or a range of elements.
The class should provide the necessary member functions, such as operator++, operator*, operator->, and operator==, to enable iteration over a container or a range of elements.
class MyIterator {
private:
// Iterator state and data
public:
// Constructor(s)
// Dereference operator
T& operator*() const;
// Member access operator
T* operator->() const;
// Pre-increment operator
MyIterator& operator++();
// Equality operator
bool operator==(const MyIterator& other) const;
// Inequality operator
bool operator!=(const MyIterator& other) const;
};
25. Explain the concept of move semantics in C++?
Move semantics in C++ allow for the efficient transfer of resources (such as dynamically allocated memory) from one object to another without unnecessary copying. Move semantics are enabled through move constructors and move assignment operators, which "steal" the resources from a source object rather than making a copy. This is particularly useful for large objects or resources that are expensive to copy, such as dynamically allocated memory.
class MyString {
private:
char* data;
public:
// Move constructor
MyString(MyString&& other) noexcept {
data = other.data;
other.data = nullptr;
}
// Move assignment operator
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
other.data = nullptr;
}
return *this;
}
// Other member functions...
};
26. How can you implement a custom smart pointer in C++?
To implement a custom smart pointer in C++, you need to define a class that manages the lifetime of a dynamically allocated resource. The class should handle memory allocation, deallocation, and provide ownership semantics. It typically includes implementing functions such as constructor(s), destructor, copy constructor, copy assignment operator, and overload the operator-> and operator* for convenient resource access.
template
class MySmartPtr {
private:
T* ptr;
public:
MySmartPtr(T* pointer) : ptr(pointer) {}
~MySmartPtr() { delete ptr; }
// Overloaded operators for convenience
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
// Other member functions...
};
27. Explain the concept of type erasure in C++?
- Type erasure is a technique in C++ that allows different types to be treated uniformly through a common interface, hiding their specific types behind a generic interface. It enables the manipulation of objects of different types in a generic manner without exposing their underlying implementation details.
- The concept of type erasure is often achieved using polymorphism, typically through the use of abstract base classes or interfaces. By defining a common interface, you can interact with objects of different types through a set of common operations.
class Printable {
public:
virtual void print() const = 0;
};
class Integer : public Printable {
private:
int value;
public:
Integer(int val) : value(val) {}
void print() const override {
std::cout << value << std::endl;
}
};
class String : public Printable {
private:
std::string value;
public:
String(const std::string& val) : value(val) {}
void print() const override {
std::cout << value << std::endl;
}
};
void printValue(const Printable& printable) {
printable.print();
}
int main() {
Integer num(42);
String str("Hello, world!");
printValue(num); // Prints: 42
printValue(str); // Prints: Hello, world!
return 0;
}
- In the example above, the Printable interface defines a common method print(), which is implemented differently by the Integer and String classes. The printValue() function takes a reference to a Printable object and invokes the print() method, regardless of the specific type of the object.
- By using type erasure, we can treat objects of different types as if they were of the same type (in this case, Printable). This allows us to write generic code that operates on objects without having to know their specific types in advance.
- Type erasure is a powerful technique used in various scenarios, including polymorphic containers, function objects, and type-safe callbacks, among others. It provides a flexible and generic way to work with objects of different types through a common interface.
28. Explain the concept of function objects (functors) in C++?
- Function objects, also known as functors, are objects that can be invoked as if they were functions.
- In C++, function objects are created by overloading the function call operator operator().
- They are often used as arguments to algorithms, allowing for customization of behavior.
- Function objects provide more flexibility than regular functions because they can maintain state and have their own member variables.
struct Adder {
int operator()(int a, int b) const {
return a + b;
}
};
Adder add;
int result = add(3, 4); // Invoking the function object
29. Explain the concept of perfect forwarding in C++?
- Perfect forwarding, introduced in C++11, allows function templates to forward arguments to other functions while preserving their value categories (lvalue or rvalue).
- It is achieved using universal references (declared with &&) and the std::forward function.
- Perfect forwarding is particularly useful when implementing generic functions or forwarding arguments to constructors or other functions.
- It avoids unnecessary copying and maintains the original value category of the forwarded arguments.
template
void process(T&& arg) {
// Perform some processing with arg
otherFunction(std::forward(arg)); // Forward arg to another function
}
30. Explain the concept of the RAII (Resource Acquisition Is Initialization) idiom in C++?
- The RAII (Resource Acquisition Is Initialization) idiom in C++ is a programming technique where resources, such as memory, file handles, or locks, are acquired during object construction and automatically released during object destruction.
- It ties the lifetime of a resource to the lifetime of an object, ensuring proper resource management.
- RAII is achieved by encapsulating the resource-handling logic within a class, where the resource is acquired in the constructor and released in the destructor.
- It guarantees that resources are always properly released, even in the presence of exceptions or early returns.
class FileHandle {
private:
FILE* file;
public:
FileHandle(const char* filename) {
file = fopen(filename, "r");
if (!file) {
// Handle error
}
}
~FileHandle() {
if (file) {
fclose(file);
}
}
// Other member functions...
};
void someFunction() {
FileHandle file("data.txt"); // Resource acquired during object initialization
// Use the file resource
// Resource released automatically during object destruction
}