Example of C++ Interview Questions

author image Hirely
at 06 Jan, 2025

Question: What are the types of polymorphism in C++?

Answer:

Polymorphism is one of the four fundamental principles of Object-Oriented Programming (OOP), the others being encapsulation, inheritance, and abstraction. In C++, polymorphism allows you to use a single interface or function to represent different underlying types. It helps in creating flexible and reusable code.

There are two main types of polymorphism in C++:

  1. Compile-time Polymorphism (Static Polymorphism)
  2. Run-time Polymorphism (Dynamic Polymorphism)

1. Compile-time Polymorphism (Static Polymorphism):

Compile-time polymorphism occurs when the function to be called is determined at compile time. This is achieved primarily through function overloading and operator overloading.

a) Function Overloading:

Function overloading is a technique in C++ where you can define multiple functions with the same name but with different parameters (either in number or type). The appropriate function is selected at compile time based on the number and type of arguments passed to the function.

Example:

#include <iostream>
using namespace std;

class Print {
public:
    void display(int i) {
        cout << "Integer: " << i << endl;
    }

    void display(double d) {
        cout << "Double: " << d << endl;
    }

    void display(string s) {
        cout << "String: " << s << endl;
    }
};

int main() {
    Print obj;
    obj.display(10);         // Calls display(int)
    obj.display(3.14);       // Calls display(double)
    obj.display("Hello");    // Calls display(string)
    return 0;
}

In this example, the display function is overloaded to accept int, double, and string types. The compiler determines the correct version of the display function to call based on the type of the argument.

b) Operator Overloading:

Operator overloading allows you to redefine the behavior of operators (such as +, -, *, etc.) for user-defined types (like classes). The operator to be invoked is determined at compile time based on the type of the operands.

Example:

#include <iostream>
using namespace std;

class Complex {
private:
    int real, imag;

public:
    Complex(int r, int i) : real(r), imag(i) {}

    // Overloading the + operator
    Complex operator+(const Complex& obj) {
        return Complex(real + obj.real, imag + obj.imag);
    }

    void display() {
        cout << real << " + " << imag << "i" << endl;
    }
};

int main() {
    Complex c1(3, 4), c2(1, 2);
    Complex c3 = c1 + c2;  // Calls overloaded + operator
    c3.display();
    return 0;
}

Here, the + operator is overloaded to add two Complex numbers. The appropriate operator overload is resolved at compile time.

2. Run-time Polymorphism (Dynamic Polymorphism):

Run-time polymorphism occurs when the function to be called is determined at run time. This type of polymorphism is achieved using inheritance and virtual functions.

a) Virtual Functions:

In C++, run-time polymorphism is primarily achieved through virtual functions. When a base class function is declared as virtual, C++ ensures that the correct function (either from the base or derived class) is invoked when called via a pointer or reference to the base class. This decision is made at run time, hence it’s called dynamic binding.

Example:

#include <iostream>
using namespace std;

class Base {
public:
    virtual void show() {   // Virtual function
        cout << "Base class" << endl;
    }
};

class Derived : public Base {
public:
    void show() override {  // Override the virtual function
        cout << "Derived class" << endl;
    }
};

int main() {
    Base* basePtr;
    Derived derivedObj;

    basePtr = &derivedObj;

    basePtr->show();  // Calls Derived class's show() method due to run-time polymorphism

    return 0;
}

In this example, the function show() is declared as virtual in the base class. Even though the base class pointer (basePtr) is used, the derived class’s show() method is called. This is because the decision of which function to call (base or derived) is made at run time.

b) Pure Virtual Functions and Abstract Classes:

A pure virtual function is a virtual function that is declared in a base class but does not have any implementation. A class that contains at least one pure virtual function is called an abstract class. Abstract classes cannot be instantiated directly, but they can be used to define interfaces that derived classes must implement.

Example:

#include <iostream>
using namespace std;

class Shape {
public:
    virtual void draw() = 0;  // Pure virtual function (abstract class)
};

class Circle : public Shape {
public:
    void draw() override {
        cout << "Drawing Circle" << endl;
    }
};

class Square : public Shape {
public:
    void draw() override {
        cout << "Drawing Square" << endl;
    }
};

int main() {
    Shape* shape1 = new Circle();
    Shape* shape2 = new Square();

    shape1->draw();  // Calls Circle's draw()
    shape2->draw();  // Calls Square's draw()

    delete shape1;
    delete shape2;
    return 0;
}

In this example, the base class Shape contains a pure virtual function draw(). The derived classes Circle and Square must provide their own implementation of the draw() function. At runtime, the appropriate function is called based on the type of the object.

Summary of Polymorphism Types:

TypeOccurs atMechanismExamples
Compile-timeCompile timeFunction overloading, Operator overloadingOverloading the + operator, function overloading
Run-timeRun timeVirtual functions, Dynamic bindingUsing pointers/references to base classes with virtual functions

Key Differences:

  • Compile-time polymorphism (static binding) is resolved at the time of compilation, while run-time polymorphism (dynamic binding) is resolved at runtime.
  • Compile-time polymorphism is achieved through function overloading and operator overloading, whereas run-time polymorphism is achieved through inheritance and virtual functions.
  • Run-time polymorphism enables more flexibility, allowing for more dynamic and adaptable code, especially in cases where you need to work with objects of different derived types through base class pointers or references.

In conclusion, polymorphism in C++ is a powerful feature that helps in creating reusable, flexible, and extensible code, and it can be implemented through compile-time (static) or run-time (dynamic) techniques.

Question: What is the difference between a reference and a pointer in C++?

Answer:

In C++, both references and pointers are used to refer to other variables, but they behave differently in terms of syntax, functionality, and memory management. Below are the key differences:


1. Definition:

  • Pointer: A pointer is a variable that holds the memory address of another variable. It can be reassigned to point to different variables.
  • Reference: A reference is an alias for an existing variable. Once initialized, a reference cannot be changed to refer to a different variable.

2. Syntax:

  • Pointer: A pointer is declared using the asterisk * symbol.

    int num = 10;
    int* ptr = &num;  // Pointer to num

    Here, ptr is a pointer to num, storing the memory address of num.

  • Reference: A reference is declared using the ampersand & symbol.

    int num = 10;
    int& ref = num;  // Reference to num

    Here, ref is a reference to num, and ref can be used just like num.


3. Reassignment:

  • Pointer: A pointer can be reassigned to point to different variables at any time.

    int a = 10, b = 20;
    int* ptr = &a;
    ptr = &b;  // Now ptr points to b
  • Reference: A reference, once initialized to a variable, cannot be reassigned to refer to another variable.

    int a = 10, b = 20;
    int& ref = a;
    ref = b;  // ref is still an alias for a, but a's value becomes 20, no reference to b

4. Memory Address:

  • Pointer: A pointer stores the actual memory address of a variable, and you can manipulate it using pointer arithmetic.

    int num = 10;
    int* ptr = &num;
    cout << *ptr;  // Dereferencing the pointer to get the value of num
  • Reference: A reference is essentially an alias to an existing variable and does not store a separate memory address.

    int num = 10;
    int& ref = num;
    cout << ref;  // ref behaves exactly like num

5. Nullability:

  • Pointer: A pointer can be null, meaning it doesn’t point to any valid memory location.

    int* ptr = nullptr;  // A pointer can be null
  • Reference: A reference cannot be null. It must always be initialized to refer to an existing variable.

    int num = 10;
    int& ref = num;  // Always valid, cannot be null

6. Dereferencing:

  • Pointer: To access the value pointed to by the pointer, you use the dereference operator *.

    int num = 10;
    int* ptr = &num;
    cout << *ptr;  // Dereferencing the pointer to get the value
  • Reference: A reference can be used directly, and it behaves just like the original variable.

    int num = 10;
    int& ref = num;
    cout << ref;  // No need to dereference, directly accessing the value

7. Memory Management:

  • Pointer: Pointers require explicit memory management (like new and delete), especially when used with dynamic memory allocation.

    int* ptr = new int(10);
    delete ptr;  // Need to manually release memory
  • Reference: References do not require explicit memory management. They cannot be used for dynamic memory allocation or manual deallocation.


8. Use Cases:

  • Pointer: Pointers are useful when you need to manipulate memory directly, perform dynamic memory allocation, or deal with arrays or objects in dynamic memory.

  • Reference: References are typically used when you want to pass arguments by reference (to avoid copying large objects), or when you want to create an alias for an existing variable. References are simpler and safer to use compared to pointers.


9. Const Qualifiers:

  • Pointer: A pointer can be constant, i.e., the pointer itself can be constant (it cannot point to another address), or the data it points to can be constant (you cannot modify the data).

    int num = 10;
    int* const ptr = &num;  // ptr cannot be reassigned
    const int* ptr = &num;  // Data pointed by ptr cannot be modified
  • Reference: A reference cannot be constant, but the data it refers to can be constant.

    int num = 10;
    const int& ref = num;  // The data referred by ref cannot be modified

Summary Table:

FeaturePointerReference
Declarationint* ptr = &num;int& ref = num;
ReassignmentCan be reassigned to point to different variablesCannot be reassigned to another variable
NullabilityCan be null (nullptr)Cannot be null
DereferencingNeeds * to dereferenceDirect access to the value (no dereferencing needed)
Memory ManagementRequires explicit management (new, delete)No explicit memory management required
Use CasesDynamic memory allocation, arrays, pointers to functionsAliases, function parameter passing by reference

Conclusion:

  • Pointers offer more flexibility and control over memory, but they require more careful handling (especially with dynamic memory). They are ideal for cases where you need to deal with raw memory or manage objects dynamically.
  • References are simpler and safer to use, and are ideal when you just need to create an alias for an existing variable or pass data by reference in functions.

In general, if you don’t need the full power of pointers, it’s often better to use references in C++ to avoid unnecessary complexity and potential errors.

Read More

If you can’t get enough from this article, Aihirely has plenty more related information, such as C++ interview questions, C++ interview experiences, and details about various C++ job positions. Click here to check it out.

Related Posts

Trace Job opportunities

Hirely, your exclusive interview companion, empowers your competence and facilitates your interviews.

Get Started Now