Example of C++ Interview Questions
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++:
- Compile-time Polymorphism (Static Polymorphism)
- 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:
Type | Occurs at | Mechanism | Examples |
---|---|---|---|
Compile-time | Compile time | Function overloading, Operator overloading | Overloading the + operator, function overloading |
Run-time | Run time | Virtual functions, Dynamic binding | Using 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 = # // Pointer to num
Here,
ptr
is a pointer tonum
, storing the memory address ofnum
. -
Reference: A reference is declared using the ampersand
&
symbol.int num = 10; int& ref = num; // Reference to num
Here,
ref
is a reference tonum
, andref
can be used just likenum
.
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 = # 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 = # 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
anddelete
), 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 = # // ptr cannot be reassigned const int* ptr = # // 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:
Feature | Pointer | Reference |
---|---|---|
Declaration | int* ptr = # | int& ref = num; |
Reassignment | Can be reassigned to point to different variables | Cannot be reassigned to another variable |
Nullability | Can be null (nullptr ) | Cannot be null |
Dereferencing | Needs * to dereference | Direct access to the value (no dereferencing needed) |
Memory Management | Requires explicit management (new , delete ) | No explicit memory management required |
Use Cases | Dynamic memory allocation, arrays, pointers to functions | Aliases, 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.
Tags
- C++
- C++ interview questions
- C++ constructors
- Inline keyword
- Reference in C++
- Class and object in C++
- Call by value vs call by reference
- Abstract class
- Virtual functions
- New vs malloc
- This pointer in C++
- C++ templates
- C++ struct vs class
- Exception handling in C++
- Operator overloading in C++
- Polymorphism in C++
- C++ pointers
- C++ iterators
- C++ STL
- Scope resolution operator
- C++ memory management
- C++ exception handling
- C++ overloading
- C++ function overriding