Unit 4: Operator Overloading, Type Casting and Re-usability
I. Orientation — Extending and Reusing C++ Types
C++ supports user-defined types that can behave like built-in types and can reuse existing class implementations. Operator overloading gives operators a class-specific meaning, type conversion connects class objects with other types, and inheritance creates new classes from established ones.
- Governing principle: Existing C++ syntax and tested class functionality should be extended without changing their fundamental meaning.
- Core mechanisms:
- Operator overloading: Defines operator behavior for class or enumeration operands.
- Type conversion: Transforms a value from a basic type to a class type or vice versa.
- Inheritance: Creates a derived class by acquiring and extending a base class.
- Important conventions:
- At least one operand of an overloaded operator must have a class or enumeration type.
- Operator precedence, associativity, and number of operands cannot be changed.
- Constructors initialize objects; destructors release resources.
- Access depends on both the member’s access specifier and the inheritance mode.
II. Operator Overloading — Natural Operations on Objects
Operator overloading assigns a class-specific implementation to an existing C++ operator through a specially named function such as operator+.
A. Importance of operator overloading
Operator overloading makes expressions involving objects concise and consistent with expressions involving built-in values.
- Readability:
c1 + c2is clearer thanc1.add(c2)for complex numbers. - Abstraction: The caller uses an operator without knowing its internal implementation.
- Consistency: Operators such as
+,==,[], and<<can express conventional class operations. - Restrictions:
- New operators cannot be created.
- Operators such as
::,.,.*,?:, andsizeofcannot be overloaded. - Overloading cannot change
+from a binary operator into a ternary operator.
- Good practice: Preserve expected meaning;
+should normally create a combined value rather than unexpectedly modify both operands.
B. Unary operator overloading
A unary overloaded operator acts on one object, as in negation, increment, or decrement.
- Member form: A unary member operator takes no explicit operand because
*thisis its operand. - Prefix form:
operator++()performs increment before the resulting value is used. - Postfix form:
operator++(int)uses an unusedintparameter to distinguish postfix syntax. - Example:
class Counter {
int value;
public:
Counter(int v = 0) : value(v) {}
Counter& operator++() { // prefix ++c
++value;
return *this;
}
Counter operator-() const { // unary -c
return Counter(-value);
}
};- Return choice: Prefix increment commonly returns
Counter&, while unary negation returns a newCounter.
C. Binary operator overloading
A binary overloaded operator combines or compares two operands, such as a + b or a == b.
- Member form: A binary member operator receives one explicit argument; its left operand is
*this. - Non-member form: A non-member operator receives both operands and is useful for symmetric conversions.
- Example:
class Complex {
double real, imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
Complex operator+(const Complex& rhs) const {
return Complex(real + rhs.real, imag + rhs.imag);
}
};- Expression: For
Complex c3 = c1 + c2;, the compiler interprets the member form asc1.operator+(c2). - Const correctness:
const Complex&avoids copyingrhs, and the finalconstguarantees that the left operand is unchanged.
III. Type Casting — Conversions Involving Class Objects
Class-related conversions are commonly implemented through converting constructors or conversion functions, subject to overload resolution and access control.
A. Basic type to class type conversion
A single-argument constructor can convert a basic value into an object of its class.
- Mechanism:
Distance(double)can construct aDistanceobject from adouble. - Example:
class Distance {
double metres;
public:
Distance(double m) : metres(m) {}
};
Distance d = 12.5; // double converted to Distance- Conversion sequence: The compiler evaluates
12.5, callsDistance(12.5), and initializesd. - Control with
explicit: Declaringexplicit Distance(double m)prevents implicit initialization such asDistance d = 12.5;Distance d(12.5)remains valid. - Design rule: Use implicit conversion only when the basic value has a clear, unambiguous class interpretation.
B. Class type to basic type conversion
A conversion function transforms an object into a basic type and has the form operator type().
- Syntax: It has no declared return type because the target type follows the
operatorkeyword. - Example:
class Distance {
double metres;
public:
Distance(double m) : metres(m) {}
explicit operator double() const {
return metres;
}
};
Distance d(8.4);
double m = static_cast<double>(d);- Meaning:
operator double()returns the stored value8.4as adouble. - Safety:
explicitprevents unintended conversions in arithmetic or function calls. - Const qualification: The final
constpermits conversion of constant objects and promises not to modify the source object.
IV. Inheritance Foundations — Reuse and Class Relationships
Inheritance defines a new class in terms of an existing class, representing an “is-a” relationship when used appropriately.
A. Importance of re-usability through inheritance
Inheritance promotes reuse by placing common state and behavior in a base class.
- Reduced duplication:
CarandTruckcan inherit shared operations such asstart()fromVehicle. - Extensibility: A derived class can add data or redefine behavior without rewriting the base implementation.
- Maintenance: A correction in an inherited base function becomes available to derived classes.
- Limitation: Inheritance creates coupling; composition is preferable for a “has-a” relationship such as
Carhaving anEngine.
B. Basics of inheritance
Inheritance syntax names a base class and an access mode in the derived-class declaration.
- General form:
class Derived : public Base {
// additional members
};- Acquisition: A derived object contains a base-class subobject.
- Visibility: Private base members exist in that subobject but cannot be accessed directly by derived member functions.
- Non-inherited operations: Constructors, destructors, and assignment operators are not inherited in the ordinary sense, although constructors can be exposed with
using Base::Base.
C. Base class and derived class
The base class supplies reusable features, while the derived class specializes or extends them.
- Base class: Defines general members, such as
Vehicle::move(). - Derived class: Adds specialized members, such as
Car::openBoot(). - Access: Protected members are directly usable in derived member functions; private members require public or protected base functions.
- Substitution: Under public inheritance, a
Derived*orDerived&can be used where an accessibleBase*orBase&is expected.
V. Forms of Inheritance — Class-Relationship Structures
Inheritance forms are classified by the number and arrangement of base and derived classes.
A. Simple inheritance
Simple inheritance has one derived class and one direct base class.
- Structure:
class SavingsAccount : public Account. - Purpose: The derived class reuses
Accountfeatures and adds savings-specific behavior. - Shape:
Account → SavingsAccount.
B. Multilevel inheritance
Multilevel inheritance forms a chain in which one derived class becomes another class’s base.
- Structure:
Person → Employee → Manager. - Effect:
Managercontains both anEmployeebase subobject and, within it, aPersonbase subobject. - Access condition: Features remain accessible only if each inheritance level permits access.
C. Multiple inheritance
Multiple inheritance gives one derived class two or more direct base classes.
- Structure:
class SmartPhone : public Camera, public Phone {};- Benefit:
SmartPhonecombines camera and telephone interfaces. - Risk: Identically named members or a repeated common ancestor can create ambiguity.
- Construction: Direct bases are initialized in their declaration order, here
CamerabeforePhone.
D. Hierarchical inheritance
Hierarchical inheritance derives multiple classes from one common base class.
- Structure:
Shape → Circle,Shape → Rectangle. - Reuse: Both derived classes can share
Shape::colour. - Specialization: Each class can provide its own implementation of an operation such as
area().
E. Hybrid inheritance
Hybrid inheritance combines two or more forms, often hierarchical and multiple inheritance.
- Diamond structure:
StudentandEmployeederive fromPerson, whileTeachingAssistantderives from both. - Problem: Without virtual inheritance,
TeachingAssistantcontains twoPersonsubobjects. - Solution: Declare
Personas a virtual base when only one sharedPersonsubobject is intended.
VI. Inheritance Modes — Controlling Acquired Access
The inheritance mode determines how accessible base-class members are treated inside and through the derived class.
A. Private mode of inheritance
Private inheritance makes inherited public and protected members private within the derived class.
- Mapping: Base
public → private; baseprotected → private. - External access: Users cannot implicitly treat the derived object as a publicly accessible base object.
- Meaning: It usually models “implemented in terms of” rather than a public “is-a” relationship.
- Default: Inheritance is private when a class declaration omits the mode.
B. Protected mode of inheritance
Protected inheritance converts inherited public and protected members into protected members.
- Mapping: Base
public → protected; baseprotected → protected. - Availability: The derived class and its descendants can use those members.
- Restriction: Ordinary external code cannot access the inherited interface through the derived object.
- Use: It supports controlled reuse across an inheritance hierarchy without exposing the base interface publicly.
C. Public mode of inheritance
Public inheritance preserves the accessible status of base public and protected members.
- Mapping: Base
public → public; baseprotected → protected. - Private members: Base-private data remains inaccessible directly in the derived class under every mode.
- Relationship: Public inheritance normally represents “Derived is a Base.”
- Polymorphic use: A derived object can be referenced through an accessible base pointer or reference.
VII. Inherited Behavior and Object Lifetime
Derived classes can replace inherited behavior, while construction and destruction follow a fixed hierarchy-safe sequence.
A. Overriding of member functions
Overriding occurs when a derived class provides a function matching a virtual base-class function.
- Requirement: The parameter list and relevant qualifiers must match; a covariant return type is allowed for pointers or references to related classes.
- Example:
class Shape {
public:
virtual void draw() const {}
virtual ~Shape() = default;
};
class Circle : public Shape {
public:
void draw() const override {}
};- Dynamic dispatch: Calling
draw()through aShape&bound to aCircleinvokesCircle::draw(). overridespecifier: It causes a compile-time error if the function does not actually override.- Virtual destructor: It ensures that deleting a derived object through a base pointer runs both destructors correctly.
B. Order of execution of constructors and destructor
Construction proceeds from base parts to derived parts, while destruction reverses that order.
- Construction order:
- Virtual base classes.
- Direct base classes in declaration order.
- Data members in declaration order.
- Derived constructor body.
- Destruction order: The derived destructor body runs first, followed by members and direct bases in reverse order; virtual bases are destroyed last.
- Reason: A derived constructor may safely rely on a fully initialized base subobject.
- Important detail: Initializer-list order does not override declaration order.
VIII. Ambiguity and Shared Bases — Managing Complex Hierarchies
Complex inheritance may introduce duplicate names or duplicate base subobjects, requiring explicit qualification or virtual inheritance.
A. Resolving ambiguities in inheritance
Ambiguity occurs when the compiler finds multiple equally valid inherited members.
- Qualification: If both
PrinterandScannerdefinestart(), useobj.Printer::start()orobj.Scanner::start(). - Derived wrapper: The derived class can define its own
start()and explicitly select or combine base implementations. - Declaration selection: A
using Base::function;declaration can expose a chosen overloaded base function. - Diamond ambiguity: A non-virtual diamond contains two common-base subobjects, making an unqualified common-base member ambiguous.
B. Virtual base class
A virtual base class ensures that the most-derived object contains only one shared subobject of a repeated base.
- Declaration:
class Person {};
class Student : virtual public Person {};
class Employee : virtual public Person {};
class Assistant : public Student, public Employee {};- Object structure:
Assistantcontains one sharedPerson, not separatePersonparts throughStudentandEmployee. - Initialization responsibility: The most-derived class,
Assistant, directly initializes the virtual base. - Benefit: It resolves duplicated state and common-base conversion ambiguity in diamond inheritance.
- Cost: Virtual inheritance adds implementation complexity and may require extra runtime layout information.
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 →