oop-interview-guide.md

C++ OOP Extensive Interview Guide

This guide is designed for rigorous technical interviews. It contains deep conceptual questions, code snippets, and "Spot the Bug" challenges.


Part 1: Core Concepts and Definitions

1. What are the four pillars of OOP?

  1. Encapsulation: Bundling data and methods together and restricting direct access (data hiding). "Perfect encapsulation" means all attributes are private.
  2. Abstraction: Hiding complex implementation details and exposing only the necessary interface (e.g., using pure virtual functions).
  3. Inheritance: Acquiring properties and behaviors from another class (the is-a relationship).
  4. Polymorphism: The ability of a function, object, or method to take on multiple forms (Compile-time via overloading, Run-time via virtual functions).

2. Why OOP over Procedural Programming?

Procedural programming (C-style) stores data in variables and uses standalone functions to modify it. This breaks down in large systems. OOP provides Scalability, Code Reusability, and Maintainability by tightly coupling data with the logic that acts upon it into single entities.

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

The only difference is the default access modifier. Members and base classes are private by default in a class, but public by default in a struct. (Note: In C, a struct cannot have functions. In C++, it can).

4. What is the difference between Encapsulation and Abstraction?

  • Encapsulation is about hiding the state/data (using private/protected modifiers) to maintain data integrity.
  • Abstraction is about hiding the implementation details (using abstract classes/interfaces) to simplify the usage of an object.

Part 2: Memory and Constructors

5. Explain Stack Allocation vs Heap Allocation for Objects.

  • Stack: Student A(1,1,1,"name");. Very fast. Memory is automatically cleaned up when the object goes out of scope (Destructor called automatically).
  • Heap: Student* A = new Student(1,1,1,"name");. Dynamic memory allocation. The object lives forever until you explicitly call delete A; (Destructor is NOT called automatically).

6. Why must a Copy Constructor take a const reference (const ClassName&)?

If passed by value (ClassName obj), C++ must create a temporary copy of the object to pass it into the function. To create that copy, it calls the copy constructor. To pass it into that copy constructor, it calls it again. This triggers an infinite compiler allocation loop, resulting in a stack overflow error.

7. What is a Move Constructor and why is noexcept important?

A move constructor steals heap resources directly from a temporary object rather than doing a slow deep copy (e.g., pointing a new pointer to an old heap array, and setting the old pointer to nullptr). Without noexcept, STL containers like std::vector will bypass your optimized move logic and use slow deep copies instead to maintain exception safety.


Part 3: Polymorphism and Inheritance

8. What is Early Binding vs Late Binding?

  • Early Binding (Compile-Time): The compiler looks at the pointer type and immediately binds the function call to that class.
  • Late Binding (Run-Time): Declaring a method as virtual forces the program to wait until execution. It looks at the actual object the pointer is pointing to in memory (using the vtable) and calls the correct child method.

9. Explain Upcasting vs Downcasting.

  • Upcasting: Pointing a Parent pointer to a Child object (Shape* s = new Circle();). Safe and implicit. The core of runtime polymorphism.
  • Downcasting: Taking an upcasted parent pointer and converting it back into a child pointer. You must explicitly typecast this in C++ (Circle* c = (Circle*)s;) because the parent pointer could theoretically be pointing to a different sibling class.

10. Can a Pure Virtual Function have a default implementation?

Yes! This is a famous trick question. A pure virtual function (virtual void draw() = 0;) can have a body provided outside the class definition (void Shape::draw() { ... }). Child classes are still forced to override it, but they can manually call the parent's default logic inside their override.

11. Why MUST destructors be virtual in polymorphic base classes?

If you upcast (Base* b = new Derived();) and then delete b;, early binding will only call ~Base(). The derived part of the object is never destroyed, causing a massive memory leak. virtual ~Base() ensures ~Derived() is called first.


Part 4: Spot The Bug

Snippet A: The Missing Reference

class Test { public: Test(Test t) { cout << "Copying..."; } };

The Bug: The copy constructor takes Test t by value instead of by reference (const Test& t). This will result in an infinite recursion compile-time error.

Snippet B: The Slicing Problem

class Base { public: int x; }; class Child : public Base { public: int y; }; void printData(Base b) { cout << b.x; } int main() { Child c; printData(c); }

The Bug: printData accepts the object by value (Base b). When c is passed, it gets "sliced". Only the Base part of c is copied into b, and all Child specific data (y) is lost completely. Fix: Pass by reference void printData(const Base& b).