Top 50 C++ Interview Questions and Answers

Commonly asked C++ interview questions, from fundamentals to advanced concepts.

1.What is C++?

C++ is a general-purpose, statically-typed, free-form, multi-paradigm programming language.

  • It was developed by Bjarne Stroustrup as an extension of the C language.
  • Supports object-oriented programming (OOP), generic programming, and procedural programming paradigms.
  • Known for its performance, efficiency, and flexibility, often used for system programming, game development, embedded systems, and high-performance computing.

2.What are the key differences between C and C++?

C and C++ share a common syntax base, but C++ introduces significant enhancements.

  • Paradigm: C is primarily a procedural language, while C++ is a multi-paradigm language supporting procedural, object-oriented, and generic programming.
  • OOP Features: C++ supports classes, objects, inheritance, polymorphism, encapsulation, and abstraction, which are absent in C.
  • Data Hiding: C++ introduces access specifiers (public, private, protected) for data hiding, which C lacks.
  • Memory Management: C++ has new and delete operators in addition to C's malloc and free.
  • Error Handling: C++ provides exception handling (try-catch), while C relies on return codes and errno.
  • Templates: C++ supports templates for generic programming, allowing functions and classes to operate with arbitrary types.
  • References: C++ introduces references, which are aliases to existing variables, unlike C.
  • Input/Output: C++ uses streams (cin, cout) for I/O, whereas C uses functions like scanf and printf.

3.Explain the core Object-Oriented Programming (OOP) concepts in C++.

OOP is a programming paradigm based on the concept of "objects", which can contain data and code.

  • Encapsulation: Bundling data (attributes) and methods (functions) that operate on the data within a single unit (a class), and restricting direct access to some of the object's components.
  • Inheritance: A mechanism where one class (child/derived) acquires the properties and behaviors of another class (parent/base). It promotes code reusability and represents an is-a relationship.
  • Polymorphism: The ability of objects of different classes to respond to the same message (method call) in different ways. It allows a single interface to represent different underlying forms.
  • Abstraction: Hiding the complex implementation details and showing only the essential features of an object. It focuses on what an object does rather than how it does it.

4.What is a class and an object in C++?

These are fundamental building blocks of Object-Oriented Programming.

  • Class: A blueprint or a template for creating objects. It defines the structure (data members) and behavior (member functions) that objects of that class will have.
    class Car {
    public:
        std::string brand;
        void drive() {
            // ... driving logic
        }
    };
    
  • Object: An instance of a class. It's a real-world entity that has a state (values of data members) and behavior (actions performed by member functions).
    Car myCar; // myCar is an object of the Car class
    myCar.brand = "Toyota";
    myCar.drive();
    

5.What are constructors and destructors in C++?

These are special member functions that manage the lifecycle of objects.

  • Constructor: A special member function that is automatically invoked when an object is created. Its primary purpose is to initialize the object's data members and allocate any necessary resources.
    • It has the same name as the class and no return type.
    • Can be overloaded (multiple constructors with different parameters).
    • Example:
      class MyClass {
      public:
          int value;
          MyClass(int v) : value(v) {} // Constructor
      };
      MyClass obj(10);
      
  • Destructor: A special member function that is automatically invoked when an object is destroyed (goes out of scope, or delete is called). Its purpose is to clean up resources allocated by the object during its lifetime (e.g., free dynamic memory).
    • It has the same name as the class, prefixed with a tilde (~), and no return type or parameters.
    • There can only be one destructor per class.
    • Example:
      class MyClass {
      public:
          ~MyClass() { // Destructor
              // Cleanup logic here
          }
      };
      

6.Explain different types of polymorphism in C++.

Polymorphism means "many forms" and allows a single interface to be used for different types.

  • Compile-time Polymorphism (Static Polymorphism):
    • Achieved through function overloading and operator overloading.
    • The compiler determines which function/operator to call at compile time based on the function signature or operator operands.
    • Example (Function Overloading):
      void print(int i) { std::cout << "Int: " << i; }
      void print(double d) { std::cout << "Double: " << d; }
      
  • Run-time Polymorphism (Dynamic Polymorphism):
    • Achieved through virtual functions and pointers/references to base classes.
    • The decision of which function to call is made at run time, based on the actual type of the object pointed to or referenced.
    • Requires a base class pointer/reference pointing to a derived class object and a virtual function in the base class.
    • Example:
      class Base { public: virtual void show() { std::cout << "Base"; } };
      class Derived : public Base { public: void show() { std::cout << "Derived"; } };
      Base* b = new Derived();
      b->show(); // Calls Derived::show() at runtime
      

7.What is inheritance in C++ and what are its types?

Inheritance is an OOP mechanism where a new class (derived class) is created from an existing class (base class).

  • Purpose: Promotes code reusability and establishes an is-a relationship between classes.
  • Types of Inheritance:
    • Single Inheritance: A class inherits from only one base class.
      class Base {};
      class Derived : public Base {};
      
    • Multiple Inheritance: A class inherits from multiple base classes.
      class Base1 {};
      class Base2 {};
      class Derived : public Base1, public Base2 {};
      
    • Multilevel Inheritance: A class inherits from a base class, which in turn inherits from another base class (A -> B -> C).
    • Hierarchical Inheritance: Multiple classes inherit from a single base class.
    • Hybrid Inheritance: A combination of two or more types of inheritance.

8.Explain encapsulation and access specifiers in C++.

Encapsulation is one of the fundamental OOP principles, implemented using access specifiers.

  • Encapsulation: The mechanism of binding data and the methods that operate on that data together within a single unit (a class). It also involves data hiding, preventing direct access to sensitive data from outside the object.
    • It protects data from accidental modification.
    • It allows for controlled access to data through public member functions (getters/setters).
  • Access Specifiers: Keywords that define the accessibility of class members (data and functions).
    • public: Members are accessible from anywhere outside the class.
    • private: Members are only accessible from within the class itself. This is the default for class members.
    • protected: Members are accessible from within the class itself and from derived classes.
    • Example:
      class BankAccount {
      private:
          double balance; // Private: only accessible within BankAccount
      public:
          BankAccount(double initial) : balance(initial) {}
          void deposit(double amount) { balance += amount; }
          double getBalance() { return balance; } // Public getter
      protected:
          std::string accountNumber; // Protected: accessible by derived classes
      };
      

9.What is abstraction in C++ and how is it achieved?

Abstraction is the concept of showing only essential information and hiding the complex implementation details.

  • Purpose: Focus on what an object does rather than how it does it, simplifying the user's interaction with the object.
  • How it's achieved:
    • Abstract Classes: Classes that cannot be instantiated directly and contain at least one pure virtual function.
      • A pure virtual function is declared with = 0; (e.g., virtual void draw() = 0;).
      • Derived classes must implement all pure virtual functions to be concrete (instantiable).
      class Shape { // Abstract class
      public:
          virtual void draw() = 0; // Pure virtual function
          void fillColor() { /* common fill logic */ }
      };
      class Circle : public Shape {
      public:
          void draw() { /* Circle drawing logic */ } // Must implement draw
      };
      
    • Access Specifiers: Using private and protected to hide internal details from external users.
    • Header Files: Declaring function prototypes in headers and providing implementation in .cpp files, separating interface from implementation.

10.Explain pointers and references in C++.

Pointers and references are both ways to access memory addresses indirectly, but they have key differences.

  • Pointers:
    • A variable that stores the memory address of another variable.
    • Can be reassigned to point to different variables.
    • Can be null (point to nothing).
    • Requires dereferencing (*) to access the value at the address it holds.
    • Allows pointer arithmetic (e.g., ptr++).
    • Example:
      int x = 10;
      int* ptr = &x; // ptr stores the address of x
      std::cout << *ptr; // Dereference to get value (10)
      int y = 20;
      ptr = &y; // Can be reassigned
      
  • References:
    • An alias or an alternative name for an already existing variable.
    • Must be initialized at the time of declaration and cannot be reassigned to refer to another variable later.
    • Cannot be null.
    • Do not require dereferencing; they behave exactly like the variable they refer to.
    • No reference arithmetic.
    • Example:
      int x = 10;
      int& ref = x; // ref is an alias for x
      std::cout << ref; // Access value directly (10)
      ref = 20; // Modifies x
      // int& anotherRef; // Error: must be initialized
      

11.What are virtual functions and pure virtual functions?

These are crucial for achieving run-time polymorphism in C++.

  • Virtual Function:
    • A member function declared in a base class with the virtual keyword.
    • When called through a pointer or reference to a base class, the actual function executed is determined at run-time based on the object's actual type (dynamic dispatch).
    • If a derived class overrides a virtual function, it's good practice to use override keyword (C++11) for clarity and compiler checks.
    • Example:
      class Base {
      public:
          virtual void greet() { std::cout << "Hello from Base"; }
      };
      class Derived : public Base {
      public:
          void greet() override { std::cout << "Hello from Derived"; }
      };
      Base* b = new Derived();
      b->greet(); // Calls Derived::greet()
      
  • Pure Virtual Function:
    • A virtual function declared in a base class with = 0; (e.g., virtual void draw() = 0;).
    • A class containing one or more pure virtual functions is an abstract class and cannot be instantiated directly.
    • Derived classes must provide an implementation for all pure virtual functions to become concrete (instantiable).
    • They are used to define an interface that derived classes must adhere to.
    • Example:
      class Shape {
      public:
          virtual void calculateArea() = 0; // Pure virtual function
      };
      class Circle : public Shape {
      public:
          void calculateArea() { /* Circle area logic */ }
      };
      

12.What is an abstract class and an interface in C++?

These concepts are related to abstraction and define contracts for derived classes.

  • Abstract Class:
    • A class that contains at least one pure virtual function.
    • Cannot be instantiated directly. You can only create pointers or references to an abstract class.
    • Designed to be a base class for other classes, providing a common interface and potentially some implemented methods.
    • Derived classes must implement all pure virtual functions to become concrete and instantiable.
    • Can have constructors, data members, and regular (non-virtual) methods.
  • Interface (in C++):
    • In C++, there isn't an explicit interface keyword like in Java or C#.
    • An interface is achieved by creating a pure abstract class — a class that contains only pure virtual functions (and possibly a virtual destructor).
    • All member functions are pure virtual, ensuring that derived classes must implement every function, thus defining a complete contract.
    • Example (C++ Interface):
      class IPrintable { // Conventionally prefixed with 'I'
      public:
          virtual void print() = 0; // Pure virtual function
          virtual ~IPrintable() {} // Virtual destructor is good practice
      };
      class Document : public IPrintable {
      public:
          void print() { /* Document printing logic */ }
      };
      

13.Explain the `const` keyword in C++.

The const keyword is used to declare entities (variables, pointers, references, member functions) as immutable.

  • const Variables: Makes a variable read-only after initialization.
    const int MAX_VALUE = 100;
    // MAX_VALUE = 200; // Error
    
  • const Pointers:
    • const int* ptr; (Pointer to const int): The data pointed to cannot be changed, but the pointer itself can point to something else.
    • int* const ptr; (const pointer to int): The pointer cannot point to another address, but the data it points to can be changed.
    • const int* const ptr; (const pointer to const int): Neither the data nor the pointer can be changed.
  • const References: A reference to a const value cannot be used to modify the value it refers to.
    const int x = 10;
    const int& ref = x;
    // ref = 20; // Error
    
  • const Member Functions: A member function declared const guarantees that it will not modify any data members of the object on which it is called.
    • Can be called on both const and non-const objects.
    • Good for ensuring const correctness.
    class MyClass {
        int data;
    public:
        int getValue() const { return data; } // const member function
        // void setValue(int v) const { data = v; } // Error: modifies data
    };
    

14.What are smart pointers in C++ and why are they used?

Smart pointers are wrapper classes for raw pointers that manage the lifetime of dynamically allocated memory, preventing memory leaks and dangling pointers.

  • Why use them?
    • Automatic Memory Management: They automatically delete the pointed-to object when they go out of scope, adhering to the RAII (Resource Acquisition Is Initialization) principle.
    • Prevent Memory Leaks: No need for manual delete calls, reducing common programming errors.
    • Prevent Dangling Pointers: Some smart pointers (like unique_ptr) manage ownership explicitly, others (like shared_ptr) track references, helping avoid situations where a pointer points to deallocated memory.
  • Types of Smart Pointers (C++11 onwards):
    • std::unique_ptr: Provides exclusive ownership. Only one unique_ptr can own a resource at a time. When the unique_ptr is destroyed, the resource is automatically deleted. Non-copyable, but movable.
      std::unique_ptr<int> ptr1(new int(10));
      std::unique_ptr<int> ptr2 = std::move(ptr1); // Ownership transferred
      // ptr1 is now null
      
    • std::shared_ptr: Provides shared ownership. Multiple shared_ptr objects can own the same resource. It maintains a reference count. The resource is deleted only when the last shared_ptr owning it is destroyed.
      std::shared_ptr<int> ptr1(new int(10));
      std::shared_ptr<int> ptr2 = ptr1; // Both ptr1 and ptr2 own the resource
      // Resource deleted when both ptr1 and ptr2 are out of scope
      
    • std::weak_ptr: A non-owning smart pointer. It points to an object managed by a shared_ptr but does not increment the reference count. Used to break circular references between shared_ptrs.
      • To access the resource, a weak_ptr must first be converted to a shared_ptr using the lock() method. If the resource has been deleted, lock() returns a null shared_ptr.

15.What is `std::vector` and how does it work internally?

std::vector is a dynamic array provided by the C++ Standard Library, part of the <vector> header.

  • Features:
    • Stores elements in contiguous memory locations.
    • Allows random access to elements (like a plain array) with O(1) complexity.
    • Can grow or shrink in size dynamically at runtime.
    • Provides methods for common array operations (push_back, pop_back, insert, erase, size, empty, etc.).
  • Internal Working (Growth Strategy):
    • When a vector runs out of allocated space (its capacity is reached) and an element is added (e.g., via push_back):
      1. It allocates a new, larger block of memory (typically 1.5x or 2x the current capacity).
      2. It copies (or moves) all existing elements from the old memory location to the new one.
      3. It deallocates the old memory block.
      4. The new element is then added.
    • This reallocation process can be expensive (O(N) for N elements), but due to the growth strategy, amortized complexity for push_back is O(1).

16.What is `std::map` and how does it work internally?

std::map is an associative container that stores elements formed by a combination of a key value and a mapped value.

  • Features:
    • Elements are stored in key-sorted order (ascending by default).
    • Each key must be unique.
    • Key-value pairs: Efficiently retrieve a value using its associated key.
    • Provides O(log N) complexity for insertion, deletion, and lookup operations, where N is the number of elements.
  • Internal Working:
    • Typically implemented as a self-balancing binary search tree, most commonly a Red-Black Tree.
    • Red-Black Tree properties ensure that the tree remains balanced, guaranteeing logarithmic time complexity for operations.
    • When an element is inserted, the tree structure is updated to maintain sorted order and balance.
    • Example:
      std::map<std::string, int> ages;
      ages["Alice"] = 30;
      ages["Bob"] = 25;
      std::cout << ages["Alice"]; // Output: 30
      // Iteration is in key-sorted order
      for (const auto& pair : ages) {
          std::cout << pair.first << ": " << pair.second << "\n";
      }
      

17.Explain C++ templates and their benefits.

Templates are a powerful feature in C++ that allow writing generic programs.

  • Purpose: To enable functions and classes to operate with arbitrary types without being rewritten for each specific type.
  • Types of Templates:
    • Function Templates: Define a family of functions that can operate on different data types.
      template <typename T>
      T add(T a, T b) {
          return a + b;
      }
      int sum_int = add(5, 3);
      double sum_double = add(5.5, 3.3);
      
    • Class Templates: Define a family of classes that can store or operate on objects of different data types.
      template <typename T>
      class Box {
      public:
          T content;
          Box(T c) : content(c) {}
      };
      Box<int> intBox(10);
      Box<std::string> strBox("Hello");
      
  • Benefits:
    • Code Reusability: Write a single code block that works for multiple data types.
    • Type Safety: The compiler generates type-specific code for each instantiation, ensuring type correctness at compile time (unlike void* in C).
    • Flexibility and Genericity: Build data structures (like std::vector, std::map) and algorithms that are generic and widely applicable.

18.What is RAII (Resource Acquisition Is Initialization) in C++?

RAII is a programming idiom in C++ used for managing resources automatically and safely.

  • Principle: Resources (like memory, file handles, mutexes, network sockets) are acquired during object initialization (in the constructor) and released when the object is destroyed (in the destructor).
  • Mechanism:
    • When an object with a resource is created, its constructor acquires the resource.
    • When the object goes out of scope (e.g., function returns, exception is thrown), its destructor is automatically called.
    • The destructor then releases the acquired resource.
  • Benefits:
    • Automatic Cleanup: Guarantees that resources are always released, even if exceptions occur, preventing resource leaks.
    • Exception Safety: Simplifies error handling because cleanup is automatic.
    • Code Clarity: Reduces boilerplate code for resource management.
  • Examples:
    • Smart Pointers (std::unique_ptr, std::shared_ptr) are prime examples of RAII for dynamic memory.
    • std::fstream for file handling.
    • std::lock_guard for mutexes (acquires lock in constructor, releases in destructor).
    void func() {
        std::unique_ptr<int> p(new int(5)); // Memory acquired
        // ... do something with p
    } // p goes out of scope, destructor called, memory freed automatically
    

19.Explain move semantics (C++11) and `std::move`.

Move semantics, introduced in C++11, allow for efficient transfer of resources (like dynamically allocated memory) from one object to another, avoiding costly deep copies.

  • Problem it solves: Before C++11, passing objects by value or returning objects by value would often involve expensive copying (copy constructor, copy assignment operator), especially for objects managing large resources (e.g., std::vector, std::string).
  • Core Idea: Instead of copying, we can move the resources. If an object is an rvalue (a temporary object or one whose resources are no longer needed), its resources can be "stolen" by another object, leaving the source object in a valid but unspecified state.
  • Key Components:
    • Rvalue References (&&): A new type of reference that binds only to rvalues.
      void process(int&& val) { /* ... */ } // Accepts only rvalues
      int x = 10;
      // process(x); // Error, x is an lvalue
      process(10); // OK, 10 is an rvalue
      
    • Move Constructor / Move Assignment Operator: Special member functions that take an rvalue reference to another object and transfer its resources instead of copying them.
      class MyVector {
          int* data;
          size_t size;
      public:
          // Move constructor
          MyVector(MyVector&& other) noexcept : data(other.data), size(other.size) {
              other.data = nullptr; // Nullify source to prevent double-free
              other.size = 0;
          }
          // Move assignment operator
          MyVector& operator=(MyVector&& other) noexcept {
              if (this != &other) {
                  delete[] data;
                  data = other.data; size = other.size;
                  other.data = nullptr; other.size = 0;
              }
              return *this;
          }
      };
      
  • std::move:
    • A function in <utility> that casts an lvalue to an rvalue reference.
    • It does not actually move anything; it only signals the intent that the object's resources can be moved (i.e., that it's okay to steal its resources).
    • Allows you to explicitly invoke move constructors/assignment operators for lvalues.
    std::vector<int> v1 = {1, 2, 3};
    std::vector<int> v2 = std::move(v1); // v1's resources are moved to v2
    // v1 is now in a valid but unspecified state (e.g., empty)
    

20.What is the `nullptr` keyword (C++11) and why is it preferred over `NULL` or `0`?

The nullptr keyword, introduced in C++11, provides a type-safe way to represent a null pointer.

  • Problem with NULL and 0:
    • Historically, NULL was typically defined as 0 or (void*)0.
    • This led to ambiguity because 0 is also an integer literal, which could cause issues with function overloading.
    void foo(int i) { /* ... */ }
    void foo(char* p) { /* ... */ }
    foo(NULL); // Ambiguous call! Might call foo(int) if NULL is 0.
    
  • nullptr Solution:
    • nullptr is a prvalue of type std::nullptr_t.
    • It is implicitly convertible to any pointer type, but not to integral types (except bool).
    • This eliminates ambiguity in function overloading and makes the intent clear.
    void foo(int i) { std::cout << "foo(int)"; }
    void foo(char* p) { std::cout << "foo(char*)"; }
    void foo(std::nullptr_t np) { std::cout << "foo(nullptr_t)"; }
    
    // foo(0); // Calls foo(int)
    // foo(NULL); // Still ambiguous on some systems/compilers
    foo(nullptr); // Clearly calls foo(nullptr_t) (or foo(char*) if foo(nullptr_t) not defined)
    
  • Benefits:
    • Type Safety: Prevents unintended conversions to integral types.
    • Clarity: Explicitly conveys that the value is a null pointer, not an integer zero.
    • Consistency: Standardized across C++ implementations.

Therefore, nullptr should always be preferred for representing null pointers in modern C++.

21.How is memory managed in C++? Explain Stack and Heap memory.

C++ primarily uses two types of memory for data storage during program execution:

  • Stack Memory:

    • Used for static memory allocation and for storing local variables, function call frames, and return addresses.
    • Memory is allocated and deallocated automatically by the compiler in a LIFO (Last-In, First-Out) manner.
    • Fast access and fixed size at compile time.
    • Limited in size.
  • Heap Memory (Free Store):

    • Used for dynamic memory allocation, primarily through new and delete operators.
    • Memory is allocated and deallocated manually by the programmer at runtime.
    • Offers flexible size and persistence beyond function scope.
    • Slower access compared to stack and susceptible to memory leaks if not managed correctly.
    • Larger capacity than stack memory.

22.What are the `new` and `delete` operators in C++?

The new and delete operators are used for dynamic memory management in C++.

  • The new operator is used to allocate memory on the heap (free store) for an object or an array of objects.

    • It returns a pointer to the allocated memory.
    • If memory allocation fails, it throws a std::bad_alloc exception by default.
    int* myInt = new int; // Allocates memory for a single int
    int* myArr = new int[10]; // Allocates memory for an array of 10 ints
    
  • The delete operator is used to deallocate memory previously allocated by new.

    • It frees the memory, making it available for other parts of the program, preventing memory leaks.
    • Using delete on memory not allocated by new or deleting the same memory twice leads to undefined behavior.
    delete myInt; // Deallocates memory for a single int
    delete[] myArr; // Deallocates memory for an array of ints
    

23.Explain the `static` keyword in C++.

The static keyword in C++ has different meanings depending on its context:

  • Static Local Variables:

    • A local variable declared static retains its value between multiple function calls.
    • It's initialized only once, when the function is first called.
    • Its lifetime is the entire program duration, but its scope remains local to the function.
    void func() {
        static int count = 0; 
        count++;
        // count will increment on each call
    }
    
  • Static Global Variables/Functions:

    • When used with global variables or functions, static restricts their scope to the current translation unit (file).
    • They cannot be accessed from other files, preventing naming conflicts.
  • Static Member Variables (in a class):

    • A static member variable is shared by all objects of the class.
    • There is only one copy of the static member variable for the entire class, regardless of how many objects are created.
    • It must be defined (initialized) outside the class definition.
    class MyClass {
    public:
        static int commonValue;
    };
    int MyClass::commonValue = 0; // Definition and initialization
    
  • Static Member Functions (in a class):

    • A static member function belongs to the class itself, not to any specific object.
    • It can be called directly using the class name (e.g., MyClass::myStaticFunc()).
    • It can only access static member variables and static member functions of the class.
    • It does not have a this pointer.

24.What is the `this` pointer in C++?

The this pointer is a prvalue expression in C++ that implicitly points to the object for which a member function is called.

  • It is an implicit parameter to all non-static member functions.
  • It's a const pointer, meaning you cannot change the address it holds, but you can modify the object it points to.
  • It allows an object to refer to itself within its own member functions.
  • Commonly used to:
    • Distinguish between member variables and local variables (e.g., in constructors or setters).
    • Return a reference to the current object (e.g., for chaining method calls).
class MyClass {
public:
    int value;
    void setValue(int value) {
        this->value = value; // 'this->value' refers to the member variable
    }
    MyClass& operator=(const MyClass& other) {
        if (this != &other) { // Check for self-assignment
            value = other.value;
        }
        return *this; // Return reference to current object
    }
};

25.Distinguish between function overloading and function overriding in C++.

Both function overloading and function overriding are forms of polymorphism, but they apply in different contexts.

  • Function Overloading (Compile-time Polymorphism):

    • Occurs when multiple functions in the same scope (usually same class or namespace) have the same name but different parameter lists (different number, types, or order of arguments).
    • The compiler decides which function to call based on the arguments provided at compile time.
    • It's a way to provide different implementations for a function based on the input types.
    class Printer {
    public:
        void print(int i) { /* ... */ }
        void print(double f) { /* ... */ }
        void print(const char* s) { /* ... */ }
    };
    
  • Function Overriding (Run-time Polymorphism):

    • Occurs in inheritance hierarchies when a derived class provides its own implementation for a member function that is already present in its base class.
    • The base class function must be declared as virtual.
    • The decision of which function to call is made at run time based on the actual type of the object pointed to by a base class pointer or reference.
    • The signature (name, return type, and parameters) of the overriding function in the derived class must be identical to the base class function.
    class Base {
    public:
        virtual void show() { /* Base implementation */ }
    };
    class Derived : public Base {
    public:
        void show() override { /* Derived implementation */ } // 'override' keyword (C++11) is good practice
    };
    

26.Explain default arguments in C++.

Default arguments allow a function to be called with fewer arguments than it is defined to accept. If an argument is omitted during a function call, its default value is used.

  • Definition: Default arguments are specified in the function declaration or definition (but usually in the declaration).
  • Placement: All default arguments must be placed at the rightmost position in the parameter list. Once you specify a default argument, all subsequent arguments to its right must also have default values.
  • Flexibility: They provide flexibility by allowing a function to have multiple ways of being called without requiring overloading.
// Function declaration with default arguments
int add(int a, int b = 5, int c = 10);

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

// Example usage:
// add(10);        // a=10, b=5 (default), c=10 (default) -> 25
// add(10, 20);    // a=10, b=20, c=10 (default)           -> 40
// add(10, 20, 30); // a=10, b=20, c=30                   -> 60

27.What are friend functions and friend classes in C++?

Friend functions and friend classes provide a mechanism to allow non-member functions or other classes to access the private and protected members of a class.

  • Friend Function:

    • A non-member function declared as friend inside a class can access all private and protected members of that class.
    • It is not a member function of the class it is befriending.
    • Friendship is not mutual (if A is friend of B, B is not necessarily friend of A).
    • Friendship is not inherited.
    class MyClass {
    private:
        int privateData;
    public:
        MyClass() : privateData(10) {}
        friend void showPrivateData(const MyClass& obj); // Friend declaration
    };
    
    void showPrivateData(const MyClass& obj) {
        // Can access privateData because it's a friend
        // std::cout << obj.privateData << std::endl;
    }
    
  • Friend Class:

    • If a class B is declared as a friend of class A, then all member functions of class B can access the private and protected members of class A.
    • Similar to friend functions, friendship is not mutual or inherited.
    class A {
    private:
        int secret;
        friend class B; // Friend declaration
    public:
        A() : secret(42) {}
    };
    
    class B {
    public:
        void accessA(const A& obj) {
            // Can access obj.secret because B is a friend of A
            // std::cout << obj.secret << std::endl;
        }
    };
    
  • Caution: Use friend declarations judiciously as they can break encapsulation if overused.

28.What is operator overloading in C++?

Operator overloading is a C++ feature that allows you to redefine the behavior of C++ operators (like +, -, *, /, ==, etc.) when applied to user-defined types (objects).

  • It allows operators to be used with objects in a way that is intuitive and meaningful for that type, making code more readable and expressive.
  • You cannot create new operators, nor can you change the arity (number of operands) or precedence of existing operators.
  • Operators can be overloaded as member functions or non-member (friend) functions.
    • Member function: The left-hand operand must be an object of the class. It implicitly takes this as the left operand and one explicit argument for binary operators.
    • Friend function: Required when you want the left-hand operand to be a non-class type, or if you need to access private members and prefer a non-member function.
class Complex {
public:
    double real, imag;
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}

    // Overload the '+' operator as a member function
    Complex operator+(const Complex& other) const {
        return Complex(real + other.real, imag + other.imag);
    }

    // Example of overloading '<<' as a friend function (common for stream operators)
    friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
        os << c.real << " + " << c.imag << "i";
        return os;
    }
};

// Complex c1(1, 2), c2(3, 4);
// Complex c3 = c1 + c2; // Calls c1.operator+(c2)
// std::cout << c3;     // Calls operator<<(std::cout, c3)

29.Explain the purpose of a copy constructor and a copy assignment operator. When are they implicitly generated?

Copy Constructor and Copy Assignment Operator are special member functions that manage how objects are copied.

  • Copy Constructor:

    • A constructor that takes a single argument: a reference to an object of the same class (typically const reference).
    • Used to create a new object as a copy of an existing object.
    • Called when:
      • An object is initialized with another object of the same type (MyClass obj2 = obj1; or MyClass obj2(obj1);).
      • An object is passed by value to a function.
      • An object is returned by value from a function.
    class MyClass {
    public:
        int* data;
        MyClass(const MyClass& other) : data(new int(*other.data)) { /* Deep copy */ }
        // ...
    };
    
  • Copy Assignment Operator (operator=):

    • A member function that overloads the assignment operator (=).
    • Used to assign the values of an existing object to another existing object.
    • Called when:
      • One existing object is assigned to another (obj2 = obj1;).
    class MyClass {
    public:
        int* data;
        MyClass& operator=(const MyClass& other) {
            if (this != &other) { // Self-assignment check
                delete data; // Deallocate old memory
                data = new int(*other.data); // Allocate new memory and copy
            }
            return *this;
        }
        // ...
    };
    
  • Implicit Generation:

    • If you don't declare them, the compiler provides a default copy constructor and a default copy assignment operator.
    • These default versions perform a member-wise (shallow) copy of all non-static data members.
    • If your class manages resources (e.g., dynamic memory, file handles), the default shallow copy can lead to issues like double-free errors or memory leaks. In such cases, you must define your own (deep copy) versions.

30.Distinguish between shallow copy and deep copy.

The distinction between shallow and deep copy becomes critical when objects contain pointers or manage dynamically allocated resources.

  • Shallow Copy:

    • Performs a bit-wise copy of the object's data members.
    • If an object contains pointers, only the addresses pointed to by these pointers are copied, not the data itself.
    • Both the original and the copied object will point to the same underlying data on the heap.
    • Problem: Modifying data through one object will affect the other. Deleting data through one object will leave the other with a dangling pointer and can lead to double-free errors.
    • The default copy constructor and assignment operator perform shallow copies.
    class Shallow {
    public:
        int* data;
        Shallow(int val) : data(new int(val)) {}
        // Default copy constructor will just copy 'data' pointer value
        // Shallow(const Shallow& other) { data = other.data; } // Implicit default
    };
    // Shallow s1(10); Shallow s2 = s1; // Both s1.data and s2.data point to the same memory
    
  • Deep Copy:

    • Creates a completely independent copy of the object.
    • If an object contains pointers to dynamically allocated memory, the deep copy allocates new memory for the copied object and copies the actual data from the original object into this new memory.
    • Both objects will have their own distinct copies of the data.
    • Solution: Prevents dangling pointers, double-free issues, and unintended modifications.
    • Requires a user-defined copy constructor and copy assignment operator.
    class Deep {
    public:
        int* data;
        Deep(int val) : data(new int(val)) {}
        Deep(const Deep& other) : data(new int(*other.data)) { /* Deep copy */ }
        Deep& operator=(const Deep& other) {
            if (this != &other) {
                delete data;
                data = new int(*other.data);
            }
            return *this;
        }
        ~Deep() { delete data; }
    };
    // Deep d1(10); Deep d2 = d1; // d1.data and d2.data point to independent memory blocks
    

31.Explain exception handling in C++ using `try`, `catch`, and `throw`.

Exception handling in C++ provides a structured way to deal with runtime errors or exceptional conditions that disrupt the normal flow of a program.

  • try Block:

    • Encloses the code segment that might throw an exception.
    • If an exception occurs within the try block, control immediately transfers to the appropriate catch block.
    try {
        // Code that might throw an exception
    }
    
  • throw Keyword:

    • Used to signal an exception.
    • When throw is executed, it creates an anonymous temporary object of the exception type and passes it to the catch handler.
    • The thrown object can be of any type (e.g., int, char*, std::string, or a custom exception class).
    if (divisor == 0) {
        throw std::runtime_error("Division by zero!"); // Throws an exception
    }
    
  • catch Block:

    • Follows a try block and specifies the type of exception it can handle.
    • If an exception of that type (or a type derived from it) is thrown in the try block, the catch block's code is executed.
    • Multiple catch blocks can follow a single try block to handle different exception types.
    • A catch(...) block can catch any type of exception (a general catch-all).
    try {
        // ... code ...
    } catch (const std::runtime_error& e) {
        // Handle runtime_error exceptions
        // std::cerr << "Error: " << e.what() << std::endl;
    } catch (int e) {
        // Handle integer exceptions
        // std::cerr << "Error code: " << e << std::endl;
    } catch (...) {
        // Catch all other exceptions
    }
    
  • Benefits: Separates error-handling code from normal logic, improves readability, and makes code more robust.

32.What are namespaces in C++ and why are they used?

Namespaces in C++ are a declarative region that provide a way to organize code and prevent name collisions.

  • Purpose:

    • To group related entities (classes, functions, variables, etc.) under a common name.
    • To allow identifiers with the same name to coexist in a program as long as they are in different namespaces.
    • This is especially important in large projects or when using multiple libraries, where name conflicts are likely.
  • Syntax:

    • Defined using the namespace keyword.
    • Members are accessed using the scope resolution operator (::).
    namespace MyProject {
        int globalVar = 10;
        void func() { /* ... */ }
        class MyClass { /* ... */ };
    }
    
    // Accessing members:
    // MyProject::globalVar;
    // MyProject::func();
    
  • using directive:

    • using namespace MyProject; brings all names from MyProject into the current scope, making them accessible without the MyProject:: prefix. Use with caution in header files or large scopes to avoid reintroducing name conflicts.
    • using MyProject::func; brings only the specific name func into the current scope.
  • The std namespace is the standard namespace in C++ that contains all the elements of the Standard C++ Library (e.g., std::cout, std::vector).

33.What is the Standard Template Library (STL) in C++?

The Standard Template Library (STL) is a powerful set of C++ template classes and functions that provides generic algorithms and data structures.

  • It is a core part of the C++ Standard Library, designed to be highly efficient and reusable.

  • The STL is built upon four main components:

    • Containers: Objects that store collections of data.
      • Sequence Containers: std::vector, std::deque, std::list, std::array.
      • Associative Containers: std::set, std::map, std::multiset, std::multimap (sorted).
      • Unordered Associative Containers (C++11): std::unordered_set, std::unordered_map, etc. (hash-based).
      • Container Adapters: std::stack, std::queue, std::priority_queue (provide specific interfaces).
    • Algorithms: Functions that perform common operations on containers, such as searching, sorting, manipulating, and transforming elements.
      • Examples: std::sort, std::find, std::copy, std::for_each.
      • They typically operate on iterators rather than directly on containers.
    • Iterators: Objects that act like pointers, providing a way to access elements of containers one by one without exposing the container's internal structure.
      • They generalize the concept of pointers and allow algorithms to work with different container types.
    • Functors (Function Objects): Classes that overload the function call operator (operator()), allowing objects to be treated as functions.
  • Benefits: Code reusability, efficiency, generic programming, and consistency across different data structures.

34.What are iterators in C++ and what are their categories?

Iterators in C++ are objects that provide a way to access elements of containers (like vector, list, map) sequentially, much like pointers, but in a generic and abstract manner.

  • They act as an interface between containers and algorithms, allowing algorithms to work with various container types without needing to know their internal structure.

  • Iterators decouple algorithms from containers.

  • They support operations like * (dereference), ++ (increment), and sometimes -- (decrement) or + (offset).

  • Categories of Iterators (from least to most powerful):

    • Input Iterators: Can read elements sequentially in a forward direction. Used for single-pass algorithms (e.g., std::find). Supports *iter, ++iter, iter == iter2, iter != iter2.
    • Output Iterators: Can write elements sequentially in a forward direction. Used for single-pass algorithms (e.g., std::copy to an output stream). Supports *iter = value, ++iter.
    • Forward Iterators: Combine capabilities of input and output iterators, allowing reading and writing in a forward direction. Can be multipass (e.g., std::replace).
    • Bidirectional Iterators: Extend forward iterators by allowing traversal in both forward and backward directions (e.g., std::list iterators, std::set iterators). Supports --iter.
    • Random Access Iterators: The most powerful. Extend bidirectional iterators with pointer arithmetic capabilities, allowing direct access to elements (e.g., std::vector iterators, raw pointers). Supports iter[n], iter + n, iter - n, iter < iter2, etc.
std::vector<int> nums = {10, 20, 30};
// Iterator declaration
std::vector<int>::iterator it = nums.begin();

// Using an iterator
// *it = 10;
// ++it; // it now points to 20
// *it = 20;

35.How can you achieve multithreading in C++? Explain `std::thread`.

C++11 introduced native support for multithreading through the <thread> header, primarily using std::thread.

  • Multithreading allows a program to execute multiple parts (threads) concurrently, potentially improving performance and responsiveness.

  • std::thread:

    • A std::thread object represents a single thread of execution.
    • To create a new thread, you construct a std::thread object, passing a callable object (function pointer, function object, or lambda) and its arguments.
    • The std::thread constructor launches the new thread immediately.
    • Thread Joining (join()):
      • thread_object.join() blocks the calling thread (usually main) until the thread represented by thread_object finishes its execution.
      • It's essential to join or detach a thread before its std::thread object goes out of scope to prevent program termination or resource leaks (std::terminate is called by the destructor if not joined/detached).
    • Thread Detaching (detach()):
      • thread_object.detach() separates the thread of execution from the std::thread object.
      • The detached thread becomes a daemon thread and runs independently in the background.
      • Resources are reclaimed automatically when the thread finishes, but there's no way to communicate with or wait for it.
#include <thread>
#include <iostream>

void taskFunction() {
    std::cout << "Hello from thread!" << std::endl;
}

int main() {
    // Create a new thread, executing taskFunction
    std::thread myThread(taskFunction);

    // Wait for myThread to finish execution
    myThread.join(); 

    std::cout << "Hello from main!" << std::endl;
    return 0;
}

36.Explain `std::future` and `std::async` for asynchronous operations in C++.

std::future and std::async (C++11) provide a higher-level abstraction for managing asynchronous tasks compared to direct std::thread usage.

  • std::async:

    • A function template that runs a callable object asynchronously, potentially in a new thread.
    • It takes a policy argument (std::launch::async for new thread, std::launch::deferred for lazy evaluation, or std::launch::async | std::launch::deferred for compiler choice) and the callable with its arguments.
    • It immediately returns an std::future object.
    • Simplifies thread management: you don't explicitly create std::threads or call join().
    #include <future>
    #include <iostream>
    #include <chrono>
    
    int calculate_sum(int a, int b) {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        return a + b;
    }
    
    // std::future<int> fut = std::async(std::launch::async, calculate_sum, 10, 20);
    
  • std::future:

    • An object that holds the result of an asynchronous operation.
    • It acts as a handle to the value that will be available in the future.
    • You can retrieve the result using its get() method.
    • get() blocks the calling thread until the asynchronous operation completes and the result is available.
    • get() can only be called once per std::future object.
    // From the above example:
    // int result = fut.get(); // Blocks until calculate_sum finishes, then gets result (30)
    // std::cout << "Result: " << result << std::endl;
    
  • Benefits: Simplifies error handling and result retrieval for parallel tasks, making concurrency easier to manage without manual thread creation and synchronization primitives (like mutexes).

37.What are lambda expressions in C++11 and what are their benefits?

Lambda expressions (or lambdas) are a powerful C++11 feature that allows you to define anonymous function objects (functors) inline, typically for short, localized operations.

  • Syntax: [capture_list](parameters) -> return_type { function_body }
    • [capture_list]: Specifies variables from the enclosing scope that the lambda can access.
      • []: No capture.
      • [var]: Capture var by value.
      • [&var]: Capture var by reference.
      • [=]: Capture all used variables by value.
      • [&]: Capture all used variables by reference.
      • [this]: Capture the this pointer (by value).
    • (parameters): Argument list, similar to a regular function.
    • -> return_type: Optional return type specification (often deduced by the compiler).
    • { function_body }: The code to be executed.
#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    int factor = 2;

    // Lambda capturing 'factor' by value
    std::for_each(numbers.begin(), numbers.end(), [factor](int n) {
        std::cout << n * factor << " ";
    }); // Output: 2 4 6 8 10

    int sum = 0;
    // Lambda capturing 'sum' by reference to modify it
    std::for_each(numbers.begin(), numbers.end(), [&](int n) {
        sum += n;
    });
    // std::cout << "Sum: " << sum << std::endl; // Output: 15
    return 0;
}
  • Benefits:
    • Conciseness: Reduces boilerplate code for simple function objects.
    • Readability: Keeps the function definition close to its point of use, improving context.
    • Flexibility: Easily capture variables from the surrounding scope, making them highly adaptable.
    • Power: Essential for modern C++ idioms, especially with STL algorithms.

38.Explain the `auto` keyword in C++11.

The auto keyword in C++11 allows for type deduction for variable declarations, significantly reducing verbosity and improving code readability, especially with complex types.

  • Type Deduction:

    • When auto is used, the compiler automatically deduces the type of the variable from its initializer at compile time.
    • The variable must be initialized at the point of declaration.
    auto i = 10;          // i is deduced as int
    auto d = 3.14;        // d is deduced as double
    auto s = "hello";     // s is deduced as const char*
    
    std::vector<int> vec = {1, 2, 3};
    // Before auto:
    // std::vector<int>::iterator it = vec.begin();
    // With auto:
    auto it = vec.begin(); // it is deduced as std::vector<int>::iterator
    
  • Benefits:

    • Reduces boilerplate: Especially useful with long, complex type names (e.g., iterator types, template instantiations, lambda types).
    • Increases readability: Focuses on the purpose of the variable rather than its exact type, especially when the type is obvious from the initializer.
    • Facilitates refactoring: If the type of an initializer changes, auto declarations will automatically adapt without requiring manual updates.
  • Limitations:

    • Cannot be used for function parameters or return types (prior to C++14/17).
    • Cannot declare uninitialized variables.
    • Sometimes, explicit type declaration is clearer for maintainability.

39.Explain rvalue references (C++11) and their purpose.

Rvalue references (&&) are a C++11 feature designed to enable move semantics, which is a powerful optimization technique for efficiently transferring resources from temporary objects.

  • Lvalues vs. Rvalues:

    • An lvalue is an expression that identifies a persistent object (has a name and an address) and can appear on the left side of an assignment (e.g., int x;, x).
    • An rvalue is an expression that identifies a temporary object or a value that does not have a persistent identity and cannot appear on the left side of an assignment (e.g., 10, x + y, a temporary object returned by a function).
  • Rvalue Reference (&&):

    • An rvalue reference is a reference that binds only to rvalues.
    • It allows you to distinguish between lvalues and rvalues, enabling special handling for temporary objects.
    void func(int& lvalue_ref) { /* Binds to lvalues */ }
    void func(int&& rvalue_ref) { /* Binds to rvalues */ }
    
    int x = 10;
    func(x);      // Calls func(int&)
    func(20);     // Calls func(int&&)
    func(x + 5);  // Calls func(int&&)
    
  • Purpose - Move Semantics:

    • The primary purpose of rvalue references is to implement move constructors and move assignment operators.
    • Instead of making a deep copy of resources from a temporary object (which is about to be destroyed anyway), move semantics allows the new object to steal or transfer ownership of the resources from the temporary object.
    • This avoids expensive copy operations, leading to significant performance improvements for large objects or objects managing dynamic memory.
    • std::move is used to cast an lvalue to an rvalue, enabling it to be

40.What is `std::move` and how is it used in C++11?

std::move (C++11) is a function template in the <utility> header that unconditionally casts its argument to an rvalue reference.

  • Not a 'Move': Despite its name, std::move does not actually move anything.

    • It only changes the type of its argument from an lvalue to an rvalue reference.
    • This rvalue reference then allows move constructors or move assignment operators to be invoked, which then perform the actual resource transfer.
  • Syntax and Usage:

    #include <utility> // For std::move
    #include <vector>
    #include <string>
    #include <iostream>
    
    int main() {
        std::string s1 = "Hello";
        std::string s2 = std::move(s1); // s1 is cast to rvalue, s2's move constructor is called
                                       // s1 is now in a valid but unspecified state (e.g., empty)
        // std::cout << "s1: " << s1 << std::endl; // s1: (likely empty or valid but unspecified)
        // std::cout << "s2: " << s2 << std::endl; // s2: Hello
    
        std::vector<int> v1 = {1, 2, 3};
        std::vector<int> v2;
        v2 = std::move(v1); // v1 is cast to rvalue, v2's move assignment operator is called
                           // v1 is now in a valid but unspecified state
        // std::cout << "v1 size: " << v1.size() << std::endl; // v1 size: 0 (or some valid state)
        // std::cout << "v2 size: " << v2.size() << std::endl; // v2 size: 3
        return 0;
    }
    
  • Purpose:

    • To explicitly indicate that an object's resources can be stolen or transferred rather than copied.
    • Enables move semantics, which is a crucial optimization for performance-critical applications involving large objects or dynamic resource management.
    • After std::move(obj) is called, obj should be considered to be in a valid but unspecified state, meaning you should not rely on its value or contents. You can safely assign to it or destroy it.

41.What is the diamond problem in C++ multiple inheritance, and how does virtual inheritance solve it?

The diamond problem occurs when a class inherits from two classes that both derive from the same base class, creating ambiguous duplicate copies of that base.

  • Without a fix, the derived class ends up with two separate copies of the common base's members, causing ambiguity when accessed.
  • Virtual inheritance (class B : virtual public A) ensures only one shared copy of the base class exists in the final derived object.
class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {}; // only one A subobject

42.What are the different types of casting operators in C++?

C++ provides four explicit cast operators, safer and more specific than a C-style cast.

  • static_cast: for well-defined conversions checked at compile time (e.g., int to float, related class pointers).
  • dynamic_cast: safely casts pointers/references within a polymorphic class hierarchy, returning nullptr (or throwing) if invalid.
  • const_cast: adds or removes const/volatile qualifiers.
  • reinterpret_cast: reinterprets the bit pattern of one type as another — the most dangerous, used sparingly for low-level code.
Base* b = ...;
Derived* d = dynamic_cast<Derived*>(b);

43.Why should a base class destructor be declared `virtual` in C++?

Declaring the base class destructor virtual ensures correct cleanup when deleting a derived object through a base class pointer.

  • Without virtual, deleting via a base pointer only calls the base destructor, leaking any resources owned by the derived part of the object.
  • This is a common source of subtle memory leaks in polymorphic class hierarchies.
class Base {
public:
    virtual ~Base() {}
};

44.What is object slicing in C++, and how can it be avoided?

Object slicing happens when a derived object is assigned to a base class object by value, "slicing off" the derived-specific members.

  • Only the base part of the object is copied — polymorphic behavior is lost since the result is a true base object, not a derived one wearing a base "view".
  • Avoid it by using pointers or references to the base class instead of passing/storing objects by value.
void print(Base b) { ... }   // slices!
void print(const Base& b) { ... } // safe

45.What is the difference between `std::unique_ptr`, `std::shared_ptr`, and `std::weak_ptr`?

All three are smart pointers (<memory>) that manage dynamic object lifetime, but with different ownership models.

  • unique_ptr: exclusive ownership — cannot be copied, only moved. Zero overhead compared to a raw pointer.
  • shared_ptr: shared ownership via reference counting — the object is destroyed when the last shared_ptr goes out of scope.
  • weak_ptr: a non-owning reference to an object managed by a shared_ptr, used to break reference cycles without affecting the reference count.
std::shared_ptr<int> sp = std::make_shared<int>(5);
std::weak_ptr<int> wp = sp; // does not increase ref count

46.What are structured bindings in C++17?

Structured bindings let you unpack multiple values from a tuple, pair, struct, or array into named variables in a single statement.

  • Improves readability compared to manually calling std::get<0>(), std::get<1>(), etc.
  • Works with any type that supports tuple-like access, including custom structs.
std::pair<int, std::string> p = {1, "one"};
auto [id, name] = p; // id = 1, name = "one"

47.What is `std::optional` in C++17, and why is it useful?

std::optional<T> (<optional>) represents a value that may or may not be present, without resorting to sentinel values like -1 or a null pointer.

  • Makes "no value" explicit and type-safe, avoiding ambiguity between a legitimate value and a missing one.
  • Check presence with has_value() (or a boolean context) before accessing via value() or *.
std::optional<int> find(int id) {
    if (!found) return std::nullopt;
    return 42;
}

48.What is a virtual table (vtable) in C++, and how does it enable runtime polymorphism?

A vtable is a hidden array of function pointers the compiler generates for classes with virtual functions.

  • Each object of a polymorphic class stores a hidden pointer (vptr) to its class's vtable.
  • A virtual function call is resolved at runtime by looking up the correct function through the vtable, which is how a base pointer can call the correct derived override.
  • This indirection is what makes runtime (dynamic) polymorphism possible, at the small cost of one extra pointer dereference per virtual call.

49.What is the One Definition Rule (ODR) in C++?

The One Definition Rule states that any variable, function, class, or template must have exactly one definition across the entire program (not per file).

  • Declarations can appear multiple times (e.g., in headers included by many files), but the actual definition must exist only once at link time.
  • Violating ODR (e.g., defining a non-inline function in a header included by multiple .cpp files) typically causes a linker error: "multiple definition of...".
  • inline functions and templates are exempt, since the compiler ensures the linker treats identical definitions across translation units as one.

50.What is `constexpr` in C++, and how does it differ from `const`?

constexpr indicates a value or function can be evaluated at compile time, not just that it's unmodifiable.

  • const only guarantees the value won't change after initialization — the value itself may still be determined at runtime.
  • constexpr requires the value (or function result, given constant arguments) to be computable at compile time, enabling it to be used in contexts like array sizes or template arguments.
constexpr int square(int x) { return x * x; }
int arr[square(4)]; // valid: computed at compile time