C++ OOP Interview Questions
Last Updated :
26 Aug, 2025
C++ supports Object-Oriented Programming through features like classes, inheritance, polymorphism, and encapsulation. These concepts help build reusable, maintainable, and efficient code, which is why they are often a key focus in interviews.
1. What is the difference between struct and class?
| struct | class |
|---|
| Members are public by default. | Members are private by default. |
| Public inheritance by default | Supports inheritance (with public, protected, or private access). |
| Used for Plain Old Data (POD) structures, or simple data grouping. | Suitable for complex objects that may include methods, constructors, and destructors. |
Example:
C++
#include <iostream>
using namespace std;
struct MyStruct {
int x; // public by default
void show() {
cout << "Struct x = " << x << endl;
}
};
class MyClass {
int y; // private by default
public:
MyClass(int val) : y(val) {} // constructor
void show() {
cout << "Class y = " << y << endl;
}
};
int main() {
MyStruct s;
s.x = 10; // allowed (public by default)
s.show();
MyClass c(20);
// c.y = 20; // not allowed, y is private
c.show(); // access via public function
}
2. What are the C++ access modifiers?
The access restriction specified to the class members (whether it is member function or data member) is known as access modifiers/specifiers.
C++ provides three access modifiers:
- Private - It can neither be accessed nor be viewed from outside the class
- Protected - It can be accessed if and only if the accessor is the derived class
- Public - It can be accessed or be viewed from outside the class
Example:
C++
#include <iostream>
using namespace std;
class Demo {
private:
int a = 1; // Not accessible outside class
protected:
int b = 2; // Accessible in derived class
public:
int c = 3; // Accessible from anywhere
void show() {
cout << "a = " << a << ", b = " << b << ", c = " << c << endl;
}
};
class Derived : public Demo {
public:
void access() {
// cout << a << endl; Not allowed (private)
cout << b << endl; // Allowed (protected)
cout << c << endl; // Allowed (public)
}
};
int main() {
Derived d; // Create object normally
d.access(); // Call member function
return 0;
}
3. What is the difference between virtual functions and pure virtual functions?
Virtual Function | Pure Virtual Function |
|---|
| A virtual function has an implementation in the base class and can be overridden in derived classes. | A Pure Virtual Function is a member function of a base class that is only declared in a base class and defined in a derived class to prevent it from becoming an abstract class. |
| A virtual Function has its definition in its respective base class. | There is no definition in Pure Virtual Function and is initialized with a pure specifier (= 0). |
| The base class has a virtual function that can be represented or instanced; In simple words, its object can be made. | A base class having pure virtual function becomes abstract that cannot be represented or instanced; In simple words, it means its object cannot be made. |
Example:
C++
#include <iostream>
using namespace std;
// Base with virtual and pure virtual
class Base {
public:
virtual void greet() { // Virtual function
cout << "Hello from Base" << endl;
}
virtual void pureGreet() = 0; // Pure virtual function
};
// Derived implements pure virtual
class Derived : public Base {
public:
void greet() override {
cout << "Hello from Derived (Override)" << endl;
}
void pureGreet() override {
cout << "Hello from Derived (Pure Virtual Implemented)" << endl;
}
};
int main() {
Derived d;
Base* ptr = &d;
ptr->greet(); // Calls overridden version
ptr->pureGreet(); // Calls derived's implementation
return 0;
}
4. Can a virtual function be called from a constructor? What happens when we do it?
Yes, a virtual function can be called from a constructor in C++, but the behavior is not what most people expect:
- When a constructor is running, the derived part of the object is not yet constructed.
- So, if you call a virtual function inside a constructor, the call will resolve to the base class version, not the derived one.
- This happens because during base class construction, the object is treated as a base-class object only.
Example:
C++
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "Base constructor\n";
show(); // virtual call inside constructor
}
virtual void show() { cout << "Base show()\n"; }
};
class Derived : public Base {
public:
Derived() { cout << "Derived constructor\n"; }
void show() override { cout << "Derived show()\n"; }
};
int main() {
Derived d;
return 0;
}
OutputBase constructor
Base show()
Derived constructor
Notice that even though Derived overrides show(), the base version runs inside the base constructor.
5. What is Function Overriding?
- Function overriding occurs when a derived class defines a function with the same name, parameters, and return type as in the base class.
- It allows the derived class to provide its own implementation of the base function.
- It is an example of runtime polymorphism (late binding), where the function executed is decided at runtime based on the object type.
Example:
C++
#include <iostream>
using namespace std;
class Animal {
public:
// Virtual function allows overriding
virtual void sound() {
cout << "Animal makes a sound" << endl;
}
};
class Dog : public Animal {
public:
// Overrides the base class function
void sound() override {
cout << "Dog barks" << endl;
}
};
int main() {
Animal* a; // Base class pointer
Dog d; // Derived class object
a = &d; // Base pointer points to derived object
a->sound(); // Runtime polymorphism: Dog's version called
return 0;
}
6. Explain the constructor & destructor in C++ .
- A constructor is a special member function in C++ that has the same name as the class and is automatically called when an object is created. It is used to initialize the object’s data members.
- A destructor is a special member function with the same name as the class, preceded by a tilde (~). It is automatically called when an object goes out of scope or is deleted, and is used to release resources or perform cleanup.
- Constructors can be overloaded (multiple constructors with different parameters), but a class can only have one destructor, which cannot be overloaded.
- Both constructors and destructors do not have a return type, not even void.
C++
#include <iostream>
using namespace std;
class Demo {
int value;
public:
// Constructor
Demo(int v) {
value = v;
cout << "Constructor called, value = " << value << endl;
}
// Destructor
~Demo() {
cout << "Destructor called for value = " << value << endl;
}
};
int main() {
Demo obj1(10); // Constructor automatically called
Demo obj2(20); // Constructor automatically called
} // Destructors automatically called here as objects go out of scope
OutputConstructor called, value = 10
Constructor called, value = 20
Destructor called for value = 20
Destructor called for value = 10
7. What happens if we define a virtual destructor but forget to mark the base class destructor as virtual?
If a base class destructor is not virtual, deleting a derived object via a base class pointer causes undefined behavior: only the base destructor runs, and the derived destructor is skipped. This leads to resource leaks.
To ensure proper cleanup, always make base class destructors virtual when using inheritance.
Example:
C++
#include <iostream>
using namespace std;
class Base {
public:
~Base() { cout << "Base Destructor\n"; }
};
class Derived : public Base {
public:
~Derived() { cout << "Derived Destructor\n"; }
};
int main() {
Base* ptr = new Derived();
delete ptr;
}
It's a silent issue no compile error, no crash, just leaked resources
8. Can constructors be private in C++? If yes, how are they used?
- Yes, constructors can be private in C++.
- When a constructor is private, objects of the class cannot be created directly outside the class.
- Such constructors are usually used in:
- Singleton design pattern: to restrict object creation and ensure only one instance exists.
- Factory methods: object creation is controlled by static functions within the class.
- Utility classes: where you don’t want anyone to instantiate the class (only static members are used).
Example (Singleton Pattern):
C++
#include <iostream>
using namespace std;
class Singleton {
private:
static Singleton* instance; // holds single instance
Singleton() { cout << "Private constructor called\n"; } // private constructor
public:
static Singleton* getInstance() {
if (instance == nullptr)
instance = new Singleton();
return instance;
}
void show() { cout << "Singleton instance working\n"; }
};
Singleton* Singleton::instance = nullptr;
int main() {
// Singleton obj; // Error: constructor is private
Singleton* s = Singleton::getInstance(); // created via static method
s->show();
}
OutputPrivate constructor called
Singleton instance working
Private constructors control instantiation, commonly used in design patterns.
9. When should we use multiple inheritance?
- Use multiple inheritance when a class logically needs to derive features or behavior from more than one base class.
- It is useful when a class needs to combine independent functionalities (e.g., a class that is both Printable and Serializable).
- It can model real-world scenarios where an object belongs to multiple categories (e.g., Teacher who is also a Researcher).
- However, it should be used carefully because it can lead to issues like the diamond problem, so prefer it only when there’s a clear and justifiable relationship.
Multiple InheritancesExample:
C++
#include <iostream>
using namespace std;
// Parent class A
class A {
public:
void showA() {
cout << "Function from class A" << endl;
}
};
// Parent class B
class B {
public:
void showB() {
cout << "Function from class B" << endl;
}
};
// Child class C inherits from both A and B
class C : public A, public B {
public:
void showC() {
cout << "Function from class C" << endl;
}
};
int main() {
C obj; // Object of derived class
obj.showA(); // Access from class A
obj.showB(); // Access from class B
obj.showC(); // Own function
return 0;
}
OutputFunction from class A
Function from class B
Function from class C
This shows how a derived class C can reuse functionality from two parent classes A and B.
10. What is virtual inheritance?
- Virtual inheritance is a C++ mechanism used to solve the diamond problem in multiple inheritance.
- It ensures that when a class is derived from two classes that have a common base, only one copy of the common base is inherited, avoiding ambiguity and duplication.
- Without virtual inheritance, each path in the inheritance hierarchy brings its own copy of the base class, leading to multiple instances and conflicts.
Without Virtual Inheritance – Diamond Problem:
C++
#include <iostream>
using namespace std;
class Base {
public:
void show() { cout << "Base class" << endl; }
};
class A : public Base {};
class B : public Base {};
class C : public A, public B {}; // Diamond problem
int main() {
C obj;
// obj.show(); // Ambiguous: which Base::show() ?
}
With Virtual Inheritance- Solves Ambiguity:
C++
#include <iostream>
using namespace std;
class Base {
public:
void show() { cout << "Base class" << endl; }
};
class A : virtual public Base {};
class B : virtual public Base {};
class C : public A, public B {}; // Only one copy of Base
int main() {
C obj;
obj.show(); // No ambiguity
}
11. Can a derived class access private members of the base class? How?
A derived class cannot directly access private members of a base class. Access is possible only through:
- protected members (directly accessible in derived).
- public or protected getters/setters in base.
- Declaring the derived class (or function) as a friend.
Example:
C++
#include <iostream>
using namespace std;
class Base {
private:
int x = 42;
friend class Derived; // Grant access
};
class Derived : public Base {
public:
void show() {
cout << "Accessing private x: " << x << endl;
}
};
int main() {
Derived d;
d.show();
}
12. What are the different types of polymorphism in C++?
There are two types of polymorphism
1. Compile Time Polymorphism or Static Binding: This type of polymorphism is achieved during the compile time of the program which results in it making a bit faster than Run time. Also, Inheritance is not involved in it. It is comprised of 2 further techniques:
- Function Overloading: When there are multiple functions with the same name but different parameters then this is known as function overloading.
C++
// same name different arguments
int GFG() {}
int GFG(int a) {}
float GFG(double a) {}
int GFG(int a, double b) {}
- Operator Overloading: It is basically giving practice of giving a special meaning to the existing meaning of an operator or in simple terms redefining the pre-redefined meaning
C++
class GFG {
// private and other modes
statements public returnType
operator symbol(arguments){ statements } statements
};
2. Run-Time Polymorphism or Late Binding: Run-time polymorphism takes place when functions are invoked during run time.
- Function Overriding: Function overriding occurs when a base class member function is redefined in a derived class with the same arguments and return type.
C++
// C++ program to demonstrate
// Function overriding
#include<iostream>
using namespace std;
class GFG {
public:
virtual void display()
{
cout << "Function of base class" << endl;
}
};
class derived_GFG : public GFG {
public:
void display()
{
cout << "Function of derived class" << endl;
}
};
int main()
{
derived_GFG dg;
dg.display();
return 0;
}
Output:
Function of derived class
13. What happens when we override a function but forget to use virtual in the base class?
- If you override a function in the derived class but the base class function is not marked virtual, then function overriding won’t work as runtime polymorphism.
- Instead, function hiding occurs when the base class function is hidden by the derived class function if called through a derived object.
- But if you use a base class pointer or reference, the base version is always called (compile-time binding), not the derived one.
Example:
C++
#include <iostream>
using namespace std;
class Base {
public:
void show() { cout << "Base\n"; }
};
class Derived : public Base {
public:
void show() { cout << "Derived\n"; }
};
int main() {
Base* ptr = new Derived();
ptr->show(); // Base version called
}
There is no runtime failure, just static binding to the base version.
14. What is object slicing in C++? How does it occur?
- Object slicing happens in C++ when a derived class object is assigned to a base class object by value.
- In this process, only the base part of the derived object is copied, and the derived-specific members (extra data or overridden behavior) are “sliced off”.
This usually occurs when:
- Passing a derived object by value to a function that takes a base class parameter.
- Assigning a derived object to a base class variable (not pointer or reference).
- To avoid slicing, use pointers or references to base instead of objects by value.
Example:
C++
#include <iostream>
using namespace std;
class Base {
public:
int x = 5;
void print() { cout << "Base x = " << x << endl; }
};
class Derived : public Base {
public:
int y = 10;
void print() { cout << "Derived y = " << y << endl; }
};
int main() {
Derived d;
Base b = d; // Object slicing
b.print(); // Only base part copied
// b.y; // Error: y is sliced off
}
15. What is a virtual destructor?
- A virtual destructor in C++ makes sure that when a base class pointer deletes a derived class object, the derived destructor is called first, followed by the base destructor.
- Without it, only the base destructor executes, which can lead to incomplete cleanup and resource leaks.
- It is mainly used in polymorphic base classes where objects may be deleted through a base pointer.
Example:
C++
#include <iostream>
using namespace std;
class Base {
public:
Base() { cout << "Base constructor\n"; }
virtual ~Base() { cout << "Base destructor\n"; } // Must be virtual
};
class Derived : public Base {
public:
Derived() { cout << "Derived constructor\n"; }
~Derived() { cout << "Derived destructor\n"; }
};
int main() {
Base* ptr = new Derived(); // Base pointer to derived
delete ptr; // Correct destructor chain due to virtual destructor
}
16. Is destructor overloading possible? If yes then explain and if no then why?
- Destructor overloading is not possible in C++. A class can have only one destructor, and it cannot take parameters or have a return type.
- The reason is: the destructor is called automatically by the compiler when an object goes out of scope or is deleted, so it must have a fixed, predictable signature.
- If overloading were allowed, the compiler wouldn’t know which destructor to call during cleanup, leading to ambiguity.
Example:
C++
class MyClass {
public:
~MyClass() {}
};
17. What do you know about friend class and friend function?
- A friend function is a non-member function that has access to the private and protected members of a class. It is declared using the keyword friend inside the class.
- A friend class is a class that can access the private and protected members of another class. The friendship is declared inside the class using friend class.
- Friendship is not mutual (if A is a friend of B, B isn’t automatically a friend of A) and not inherited (derived classes don’t inherit friendship).
- They are mainly used when two classes or functions need close cooperation and direct access to each other’s internals.
Example:
C++
#include <iostream>
using namespace std;
class A {
private:
int secret = 42;
public:
// Friend function
friend void revealSecret(A obj);
// Friend class
friend class B;
};
// Friend function can access private data
void revealSecret(A obj) {
cout << "Friend function accessed secret: " << obj.secret << endl;
}
// Friend class can access private data
class B {
public:
void showSecret(A obj) {
cout << "Friend class accessed secret: " << obj.secret << endl;
}
};
int main() {
A a;
B b;
revealSecret(a); // Using friend function
b.showSecret(a); // Using friend class
return 0;
}
OutputFriend function accessed secret: 42
Friend class accessed secret: 42
This shows that both a friend function and a friend class can directly access private members of another class.
18. What is an abstract class and when do you use it?
- A class with at least one pure virtual function (virtual void func() = 0;).
- Cannot be instantiated directly.
- Provides a common interface for derived classes.
- Enforces implementation of specific functions in subclasses.
- Enables runtime polymorphism.
When to use an abstract class?
- When you want to define a base class that outlines a common interface.
- To ensure derived classes implement certain functions.
- When you need to share some common functionality among related classes.
- To enable polymorphic behavior through base class pointers or references.
Example:
C++
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() = 0; // Pure virtual → makes Shape abstract
};
class Circle : public Shape {
public:
void draw() override {
cout << "Drawing Circle\n";
}
};
int main() {
Shape* ptr = new Circle();
ptr->draw(); // Polymorphic behavior
delete ptr;
}
19. What are the static data members and static member functions?
A static data member belongs to the class, not to individual objects. Only one copy exists, shared by all objects.
Syntax:
static Data_Type Data_Member;
A static member function can access only static members of the class and can be called using the class name.
Syntax:
classname::function name(parameter);
Example:
C++
#include <iostream>
using namespace std;
class Counter {
static int count; // Static data member
public:
Counter() { ++count; }
static void showCount() { // Static function
cout << "Count = " << count << endl;
}
};
int Counter::count = 0; // Initialize static member
int main() {
Counter a, b, c;
Counter::showCount(); // Call static function via class
}
20. Can we call a virtual function from a constructor?
Yes, we can call a virtual function from a constructor, but during base class construction the derived part of the object is not yet initialized. Hence, the base class version of the function is called, not the derived version.
Example:
C++
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "Base Constructor\n";
virtualFunc(); // Calls Base version only
}
virtual void virtualFunc() {
cout << "Base::virtualFunc()\n";
}
virtual ~Base() {}
};
class Derived : public Base {
public:
void virtualFunc() override {
cout << "Derived::virtualFunc()\n";
}
};
int main() {
Derived d;
return 0;
}
During Base constructor execution, Derived is not yet constructed, so virtualFunc() is not overridden yet.