Unit 4: Operator Overloading, Type Casting and Re-usability - Subjective Questions
CAP455 — Object Oriented Programming Using C++ • Practice Questions with Detailed Answers
20 questions
Define operator overloading in C++. Why is it important in object-oriented programming?
Operator overloading is the process of giving an existing C++ operator an additional meaning when it is applied to objects of a user-defined class.
For example, the + operator can be overloaded to add two objects representing complex numbers.
Importance:
- It allows user-defined objects to be manipulated like built-in data types.
- It improves the readability and clarity of programs.
- It provides a natural and concise notation for operations on objects.
- It supports compile-time polymorphism because the compiler selects the appropriate operator function according to its operands.
- It improves class abstraction by hiding the internal implementation of an operation.
Operator overloading does not create new operators or change operator precedence, associativity, or the number of operands accepted by an operator.
Explain how a unary operator is overloaded using a member function. Illustrate your answer by overloading unary minus for a class.
A unary operator works with one operand. When overloaded as a member function, it does not require an explicit argument because the calling object acts as the operand.
General syntax:
return_type operator op();
Example:
class Number {
int value;
public:
Number(int v = 0) : value(v) {}
Number operator-() const {
return Number(-value);
}
int getValue() const {
return value;
}
};
If n is an object, the expression -n is internally interpreted as n.operator-().
Key points:
- Unary minus does not modify the original object in this example.
- A new object containing the negated value is returned.
- No explicit parameter is needed when the operator is a member function.
- Operators such as unary
-, unary+,!,++, and--can be overloaded.
Differentiate between the prefix and postfix forms of overloaded increment operator ++. Explain with suitable function declarations.
Both prefix and postfix increment operators use the symbol ++, but C++ distinguishes them through their function signatures.
Prefix increment:
Counter& operator++();
- It increments the object before its value is used.
- It normally returns the modified object by reference.
- The expression
++cbecomesc.operator++().
Postfix increment:
Counter operator++(int);
- It uses an unused
intparameter to distinguish it from prefix increment. - It normally returns a copy of the original value.
- The expression
c++becomesc.operator++(0).
Typical implementation:
Counter& operator++() {
++value;
return *this;
}
Counter operator++(int) {
Counter old = *this;
++value;
return old;
}
Thus, prefix increment returns the updated value, whereas postfix increment returns the value that existed before incrementing.
Describe the overloading of a binary operator using both a member function and a non-member friend function.
A binary operator operates on two operands. It may be overloaded as either a member function or a non-member function.
As a member function:
Complex operator+(const Complex& other) const;
For the expression a + b, the compiler interprets the call as a.operator+(b). The left operand is the calling object, and the right operand is passed as an argument.
As a non-member friend function:
friend Complex operator+(const Complex& a, const Complex& b);
For a + b, both operands are passed explicitly to operator+(a, b). Declaring the function as a friend allows it to access private and protected members of the class.
Comparison:
- A member binary operator takes one explicit parameter.
- A non-member binary operator takes two explicit parameters.
- A non-member form is useful when the left operand is not an object of the class.
- Operators
=,[],(), and->must be overloaded as member functions.
Write and explain a C++ class that overloads the binary + operator to add two complex numbers.
A complex number can be represented as , where is the real part and is the imaginary part.
class Complex {
double real;
double imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
Complex operator+(const Complex& other) const {
return Complex(real + other.real,
imag + other.imag);
}
void display() const {
std::cout << real << " + " << imag << "i";
}
};
Usage:
Complex c1(2, 3), c2(4, 5);
Complex c3 = c1 + c2;
The expression is translated into c1.operator+(c2). The resulting values are calculated as:
Therefore, c3 represents . The parameters and member function are marked const where appropriate to prevent accidental modification.
State the major rules and restrictions associated with operator overloading in C++.
The principal rules of operator overloading are:
- Only existing C++ operators can be overloaded; a new operator symbol cannot be created.
- At least one operand must be an object of a user-defined type.
- The precedence and associativity of an operator cannot be changed.
- The original number of operands cannot be changed. A unary operator remains unary, and a binary operator remains binary.
- The original meaning of an operator for built-in types cannot be redefined.
- Operators
.,.*,::,?:, andsizeofcannot be overloaded. - Operators
=,[],(), and->must be overloaded as member functions. - Overloading does not guarantee short-circuit evaluation for overloaded
&&and||. - An overloaded operator should preserve the conventional meaning of that operator whenever possible.
These restrictions keep overloaded operators predictable and consistent with the C++ language model.
Explain basic type to class type conversion using a converting constructor. Give an appropriate example.
A conversion from a basic type to a class type can be performed by a constructor that accepts one argument of the basic type. Such a constructor is called a converting constructor.
class Distance {
double metres;
public:
Distance(double m) : metres(m) {}
double getMetres() const {
return metres;
}
};
The statement:
Distance d = 15.5;
causes the compiler to invoke Distance(15.5), converting the double value into a Distance object.
Important points:
- A single-argument constructor can allow implicit conversion.
- Writing the constructor as
explicit Distance(double m)prevents unintended implicit conversions. - With an
explicitconstructor, direct initialization such asDistance d(15.5);is still valid. - Converting constructors make interaction between built-in values and class objects convenient, but implicit conversion should be permitted only when it has a clear meaning.
Explain class type to basic type conversion using a conversion function. Illustrate conversion of a class object to double.
A class object can be converted to a basic type by defining a conversion function, also called a conversion operator, inside the class.
Syntax:
operator type() const;
Example:
class Distance {
double metres;
public:
Distance(double m = 0) : metres(m) {}
operator double() const {
return metres;
}
};
Usage:
Distance d(25.75);
double x = d;
The compiler invokes d.operator double() and stores 25.75 in x.
Characteristics:
- A conversion function has no declared return type.
- Its name is the keyword
operatorfollowed by the destination type. - It takes no explicit argument.
- It is normally declared
constbecause conversion need not modify the object. - It can be marked
explicitin modern C++ to prevent accidental implicit conversions.
Compare basic-to-class and class-to-basic type conversions in C++.
Basic-to-class conversion:
- Converts a built-in value into a class object.
- It is normally implemented using a single-argument converting constructor.
- Example:
Distance d = 10.0; - The class constructor receives the source value.
Class-to-basic conversion:
- Converts a class object into a built-in value.
- It is implemented using a conversion function such as
operator double(). - Example:
double x = d; - The conversion function returns the destination basic value.
Common considerations:
- Both conversions may be implicit unless restricted with
explicit. - Excessive implicit conversion can cause ambiguity and unexpected function selection.
- A conversion should be provided only when it has a natural semantic meaning.
- Explicit conversions improve type safety when information may be lost.
Thus, a constructor places the conversion responsibility on the destination class, while a conversion operator allows the source class to describe how it becomes another type.
What is inheritance? Explain its role in software re-usability and identify the base class and derived class.
Inheritance is an object-oriented mechanism through which a new class acquires the accessible data members and member functions of an existing class.
- The existing class is called the base class, parent class, or superclass.
- The newly created class is called the derived class, child class, or subclass.
Syntax:
class Derived : public Base { /* additional members */ };
Role in re-usability:
- Common attributes and operations are written once in the base class.
- Derived classes reuse tested base-class code.
- A derived class can add new features without changing the base class.
- It reduces duplication and simplifies maintenance.
- It supports specialization and represents an is-a relationship.
- Together with overriding and virtual functions, it supports runtime polymorphism.
For example, Car may inherit common properties such as speed and movement operations from a Vehicle class.
Explain simple inheritance and multilevel inheritance with suitable class relationships.
Simple inheritance occurs when one derived class inherits from exactly one base class.
class Person { };
class Student : public Person { };
Here, Student directly inherits the accessible members of Person.
Multilevel inheritance occurs when a class is derived from another derived class, forming an inheritance chain.
class Person { };
class Employee : public Person { };
class Manager : public Employee { };
The relationship is:
Person Employee Manager
Manager can use accessible members inherited through Employee from Person. However, private members of Person remain inaccessible directly in both derived classes.
Difference:
- Simple inheritance has one direct base-to-derived relationship.
- Multilevel inheritance contains two or more levels.
- In multilevel inheritance, constructors execute from the highest base class down to the most derived class, while destructors execute in reverse order.
Describe multiple inheritance. Discuss its advantages and possible ambiguity problems.
Multiple inheritance occurs when one derived class inherits from two or more base classes.
class Printer { };
class Scanner { };
class AllInOne : public Printer, public Scanner { };
Here, AllInOne combines the capabilities of both Printer and Scanner.
Advantages:
- It combines independent functionalities in one class.
- It encourages reuse of code from several sources.
- It can model an entity that logically has multiple roles.
Possible problems:
- If both base classes contain a member with the same name, an unqualified reference becomes ambiguous.
- If both base classes inherit from the same ancestor, the derived class may receive duplicate copies of that ancestor.
- Construction, destruction, and maintenance may become more complex.
A name ambiguity can be resolved using scope resolution, such as Printer::show() or Scanner::show(). Duplicate common-base objects in a diamond hierarchy can be avoided by using a virtual base class.
Explain hierarchical inheritance and hybrid inheritance with examples.
Hierarchical inheritance occurs when two or more derived classes inherit from the same base class.
class Shape { };
class Circle : public Shape { };
class Rectangle : public Shape { };
Both Circle and Rectangle reuse the common members of Shape while adding their own specialized features.
Hybrid inheritance is a combination of two or more inheritance forms, such as hierarchical, multilevel, and multiple inheritance.
For example:
class Person { };
class Student : virtual public Person { };
class Employee : virtual public Person { };
class TeachingAssistant : public Student, public Employee { };
This combines hierarchical inheritance from Person with multiple inheritance in TeachingAssistant.
Key issue: A hybrid structure may create a diamond-shaped hierarchy and duplicate the common base. Declaring Person as a virtual base ensures that TeachingAssistant contains only one shared Person subobject.
Compare public, protected, and private modes of inheritance in C++.
The inheritance mode determines how accessible members of a base class appear in the derived class.
| Base member | Public inheritance | Protected inheritance | Private inheritance |
|---|---|---|---|
public |
Remains public |
Becomes protected |
Becomes private |
protected |
Remains protected |
Remains protected |
Becomes private |
private |
Not directly accessible | Not directly accessible | Not directly accessible |
Public inheritance:
- Represents an is-a relationship.
- Public base members remain available through derived objects.
Protected inheritance:
- Public and protected base members become protected.
- They are available within the derived class and its subclasses, but not through ordinary objects.
Private inheritance:
- Public and protected base members become private in the derived class.
- Further derived classes cannot directly access them.
Base-class private members still exist inside a derived object, but they can be accessed only through accessible base-class member functions.
Distinguish between private members, protected members, and the private mode of inheritance.
Private members:
- Declared under the
privateaccess specifier. - Accessible only inside the declaring class and its friends.
- Not directly accessible in derived classes.
Protected members:
- Declared under the
protectedaccess specifier. - Accessible inside the declaring class, its friends, and derived classes.
- Not ordinarily accessible through an object outside the hierarchy.
Private mode of inheritance:
- Written as
class D : private B. - It does not change the original declaration of members in
B. - It causes public and protected members inherited from
Bto be treated as private members ofD. - Classes derived further from
Dcannot directly access those inherited members.
Therefore, member access specifiers control access at declaration, whereas the inheritance mode controls how inherited public and protected members are exposed by the derived class.
What is member function overriding? Differentiate it from function overloading and explain the role of virtual and override.
Function overriding occurs when a derived class supplies a new implementation of a base-class virtual function with a matching signature.
class Base {
public:
virtual void display() const { }
virtual ~Base() = default;
};
class Derived : public Base {
public:
void display() const override { }
};
If a base pointer refers to a Derived object, calling display() invokes Derived::display() because the function is virtual.
Overriding versus overloading:
- Overriding occurs across a base-derived relationship; overloading normally occurs in the same scope.
- Overridden functions have matching signatures; overloaded functions have different parameter lists.
- Overriding supports runtime polymorphism; overloading is resolved at compile time.
- A return type for an override must be compatible with that of the base function.
The virtual keyword enables dynamic dispatch. The override specifier asks the compiler to verify that the derived function actually overrides a base virtual function, helping detect signature errors.
Explain the order of execution of constructors and destructors in simple, multilevel, and multiple inheritance.
General principle: A base-class part must be constructed before the derived-class part can use it. During destruction, the derived part is destroyed before its bases.
Simple inheritance:
- Construction: base constructor, then derived constructor.
- Destruction: derived destructor, then base destructor.
Multilevel inheritance: For A B C:
- Construction order:
A,B,C. - Destruction order:
C,B,A.
Multiple inheritance:
class D : public B1, public B2 { };
- Construction order:
B1, thenB2, thenD. - Destruction order:
D, thenB2, thenB1. - Base classes are constructed in the order in which they appear in the class declaration, not the order used in the constructor initializer list.
Before the constructor body executes, virtual bases are constructed first, followed by direct non-virtual bases and then data members in declaration order. A polymorphic base class should usually have a virtual destructor so deleting through a base pointer destroys the complete derived object correctly.
How are member-name ambiguities resolved in multiple inheritance? Explain using the scope resolution operator.
A member-name ambiguity occurs when two base classes provide members with the same name and the derived class attempts to use that name without qualification.
class A {
public:
void show() { }
};
class B {
public:
void show() { }
};
class C : public A, public B {
public:
void test() {
A::show();
B::show();
}
};
Calling show() directly inside C would be ambiguous because the compiler cannot determine whether A::show() or B::show() is intended.
Resolution methods:
- Qualify the member name using the base-class scope, such as
A::show(). - Define a new
show()function in the derived class and explicitly select or combine the base implementations. - Use a
usingdeclaration when one base implementation should be introduced deliberately.
Scope qualification resolves a name conflict, but it does not solve duplication of a common base subobject in diamond inheritance. That issue requires virtual inheritance.
What is a virtual base class? Explain how it solves the diamond inheritance problem.
A virtual base class is a base inherited with the keyword virtual so that only one shared instance of that base exists in the most-derived object.
Consider the diamond hierarchy:
class Person { };
class Student : virtual public Person { };
class Employee : virtual public Person { };
class TeachingAssistant : public Student, public Employee { };
Without virtual inheritance, TeachingAssistant would contain two Person subobjects: one through Student and another through Employee. This causes:
- Duplicate base-class data.
- Ambiguous access to
Personmembers. - Possible inconsistency between the two copies.
With virtual inheritance, both paths share one Person subobject. The most-derived class, TeachingAssistant, is responsible for initializing the virtual base.
Construction order:
- Virtual base classes are constructed before non-virtual base classes.
- The shared virtual base is constructed only once.
- Destruction occurs in the reverse order.
Thus, virtual inheritance solves common-base duplication while preserving multiple and hybrid inheritance.
Design and explain a C++ inheritance hierarchy that demonstrates multiple inheritance, overriding, ambiguity resolution, and a virtual base class.
The following diamond hierarchy demonstrates the required concepts:
class Person {
protected:
std::string name;
public:
Person(std::string n = "") : name(n) {}
virtual void role() const {
std::cout << "Person";
}
virtual ~Person() = default;
};
class Student : virtual public Person {
public:
void role() const override {
std::cout << "Student";
}
void details() const {
std::cout << "Student details";
}
};
class Employee : virtual public Person {
public:
void role() const override {
std::cout << "Employee";
}
void details() const {
std::cout << "Employee details";
}
};
class TeachingAssistant : public Student, public Employee {
public:
TeachingAssistant(std::string n) : Person(n) {}
void role() const override {
std::cout << "Teaching Assistant";
}
void showDetails() const {
Student::details();
Employee::details();
}
};
Explanation:
TeachingAssistantuses multiple inheritance by inheriting fromStudentandEmployee.- Both intermediate classes virtually inherit
Person, so there is only onePersonsubobject. role()is overridden at each relevant level and supports runtime polymorphism.- The two
details()functions create a name ambiguity, resolved withStudent::details()andEmployee::details(). - The most-derived class initializes the virtual base through
Person(n). - The virtual destructor ensures correct destruction when an object is deleted through a
Person*pointer.
Define operator overloading in C++. Why is it important in object-oriented programming?
Operator overloading is the process of giving an existing C++ operator an additional meaning when it is applied to objects of a user-defined class.
For example, the + operator can be overloaded to add two objects representing complex numbers.
Importance:
- It allows user-defined objects to be manipulated like built-in data types.
- It improves the readability and clarity of programs.
- It provides a natural and concise notation for operations on objects.
- It supports compile-time polymorphism because the compiler selects the appropriate operator function according to its operands.
- It improves class abstraction by hiding the internal implementation of an operation.
Operator overloading does not create new operators or change operator precedence, associativity, or the number of operands accepted by an operator.
Did this save you a night before the exam?
LPU Notes is free, and it stays free. Ads cover part of the server bill. The rest comes out of a student's own pocket: the domain, the storage, and keeping the site up through the weeks everyone needs it at once.
The payment button didn't load. An ad blocker or a filtered network is the usual reason. to try again.
Nothing here is ever locked, and nothing unlocks. Chip in only if it was worth it. What it pays for →