09 Uiv Operatorverloading
09 Uiv Operatorverloading
UNIT IV
Operator Overloading
Operator overloading is a feature in C++ that allows developers to redefine the behavior of
operators for user-defined types, such as classes. This means you can make objects of your
classes respond to operators like +, -, ==, and even << in custom ways, similar to how built-
in data types behave.
Why Use Operator Overloading?
1. Intuitive Code: With operator overloading, you can write code that’s more readable
and closer to natural language, which is especially useful in cases where you’re
dealing with mathematical or logical objects.
2. Consistency: Overloading makes using custom data types feel consistent with using
built-in types, which can reduce errors and make the code easier to understand and
maintain.
3. Encapsulation: By embedding operator functionality within classes, you keep
operations related to a specific data type encapsulated, following object-oriented
principles.
Basic Concepts and Rules of Operator Overloading
1. Operators that can be Overloaded:
o Almost all operators can be overloaded, including arithmetic (+, -, *, /),
relational (==, !=, <, >), assignment (=), and stream (<<, >>) operators.
o Some operators, however, cannot be overloaded. These include:
▪ . (member access)
▪ .* (pointer-to-member access)
▪ :: (scope resolution)
▪ ?: (ternary conditional)
▪ sizeof (object size calculation)
2. Syntax of Operator Overloading:
o Operator overloading is done by defining a special function using the operator
keyword followed by the operator symbol, for example: operator+,
operator==, etc.
o Overloaded operators can be implemented as either member functions or
friend functions.
3. Operator Overloading with Member Functions vs. Friend Functions:
o Member functions: The left operand must be an object of the class. For
example, to overload + as a member function, you might write:
SOMESH KUMAR DEWANGAN DEPARTMENT OF CSE 1
SHRI SHANKARACHARYA TECHNICAL CAMPUS
class Complex {
double real, imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
int main() {
Complex c1(3.5, 2.5), c2(1.5, 4.0);
Complex c3 = c1 + c2; // Uses overloaded '+' operator
c3.display(); // Output: 5 + 6.5i
}
the + operator is overloaded to add the real and imag parts of Complex numbers. This makes
adding complex numbers as intuitive as adding primitive types.
class Vector {
double x, y, z;
public:
Vector(double x_val = 0, double y_val = 0, double z_val = 0) : x(x_val), y(y_val), z(z_val)
{}
int main() {
Vector v1(5.0, 3.0, 2.0), v2(1.0, 1.5, 0.5);
Vector v3 = v1 - v2; // Uses overloaded '-' operator
v3.display(); // Output: Vector(4, 1.5, 1.5)
}
the - operator is overloaded to perform vector subtraction, allowing for intuitive vector
manipulation.
int main() {
Distance d1(5, 9.5), d2(5, 9.5), d3(6, 4.0);
if (d1 == d2) {
cout << "d1 is equal to d2" << endl;
}
if (!(d1 == d3)) {
cout << "d1 is not equal to d3" << endl;
}
}
the == operator to compare if two Distance objects are equal, enhancing readability when
comparing complex data types.
class Point {
int x, y;
public:
Point(int x_val = 0, int y_val = 0) : x(x_val), y(y_val) {}
return out;
}
};
int main() {
Point p1(3, 4), p2(5, 7);
cout << "Point p1: " << p1 << endl; // Uses overloaded '<<' operator
cout << "Point p2: " << p2 << endl;
}
By overloading the << operator, we can print Point objects in a more readable format, making
it simpler to debug and display information.
class IntArray {
int arr[5];
public:
IntArray() {
for (int i = 0; i < 5; ++i)
arr[i] = 0;
}
return arr[index];
else {
cout << "Index out of bounds" << endl;
exit(1);
}
}
int main() {
IntArray array;
array[0] = 10; // Uses overloaded '[]' operator
array[1] = 20;
array.display(); // Output: 10 20 0 0 0
}
the [] operator is overloaded to access and modify elements of a custom array class, giving it
array-like behavior and improving code readability.
Overloading the ++ Operator for Incrementing
The ++ operator can be overloaded to increment values, similar to its behavior with built-in
types.
#include <iostream>
using namespace std;
class Counter {
int value;
public:
Counter(int v = 0) : value(v) {}
int main() {
Counter c(5);
++c; // Uses overloaded prefix '++'
c.display(); // Output: Counter value: 6
c++; // Uses overloaded postfix '++'
c.display(); // Output: Counter value: 7
}
Type Conversion:
Type conversion in C++ refers to converting one data type into another. This can involve both
basic types (e.g., int, float) and user-defined types (e.g., classes). There are different types of
conversions in C++:
int main() {
Distance d = 37; // 37 inches automatically converts to Distance object
d.display(); // Output: 3 feet 1 inches
}
Here, the constructor Distance(int totalInches) allows int to be automatically converted to a
Distance object. When 37 is assigned to d, this constructor is invoked to convert it into a
Distance instance.
2. Class Type to Basic Type Conversion
For this conversion, you define a conversion operator in the class. The operator keyword is
used to define the type to which the class object should be converted.
#include <iostream>
using namespace std;
class Distance {
int feet;
double inches;
public:
Distance(int f, double i) : feet(f), inches(i) {}
// Conversion operator to convert Distance to int
operator int() const {
return feet * 12 + inches; // Converts Distance object to total inches
}
};
int main() {
Distance d(3, 4.5);
int inches = d; // Converts Distance object to int
cout << "Distance in inches: " << inches << endl; // Output: Distance in inches: 40
}
operator int() allows the Distance object d to be converted to int. When assigned to inches,
the compiler uses this conversion operator to obtain the equivalent integer.
3. Class Type to Another Class Type Conversion
To convert an object from one class to another, you can either define a conversion constructor
in the destination class or define a conversion operator in the source class.
the MetricDistance class has a conversion constructor that takes a Distance object. This
constructor converts Distance to MetricDistance by calculating the equivalent metric value in
meters.
Inheritance
Inheritance in C++ is a powerful object-oriented programming (OOP) feature that lets one
class inherit the properties and behaviors of another class. This enables code reuse,
modularity, and the formation of hierarchical class structures. Here’s a detailed explanation of
inheritance concepts, including defining derived classes, forms of inheritance, handling
ambiguity in multiple and multipath inheritance, virtual base classes, object slicing, function
overriding, and constructor/destructor execution order.
1. Introduction to Inheritance
Inheritance allows a derived class to acquire properties and behaviors (data members and
methods) of a base class. This establishes an "is-a" relationship between classes, meaning a
derived class can be considered a more specific version of a base class.
For example:
• A Car class can inherit from a Vehicle class, because a car "is-a" vehicle.
• Inheritance enables the derived class to extend or modify the base class functionality.
class Animal {
public:
void eat() { cout << "Eating..." << endl; }
};
int main() {
Dog d;
d.eat(); // Inherited from Animal
d.bark(); // Defined in Dog
return 0;
}
3. Forms of Inheritance
There are several forms of inheritance in C++:
• Single Inheritance: A derived class inherits from one base class.
• Multiple Inheritance: A derived class inherits from more than one base class.
• Multilevel Inheritance: A class derives from a class, which is also derived from
another class, creating a chain.
• Hierarchical Inheritance: Multiple classes inherit from a single base class.
• Hybrid Inheritance: A combination of multiple and multilevel inheritance (can lead
to ambiguity, resolved by virtual inheritance).
In C++, inheritance can take several forms, each representing a different kind of
relationship between base and derived classes. Here’s a breakdown of the different forms
of inheritance, along with examples for clarity:
1. Single Inheritance
Single inheritance is when a derived class inherits from only one base class. This is the
simplest form of inheritance.
Example:
cpp
Copy code
#include <iostream>
using namespace std;
class Animal {
public:
void eat() { cout << "Eating..." << endl; }
};
int main() {
Dog d;
d.eat(); // Inherited from Animal
d.bark(); // Defined in Dog
return 0;
}
In this example:
• Dog is the derived class and Animal is the base class.
• Dog inherits the eat() method from Animal and adds its own method, bark().
2. Multiple Inheritance
Multiple inheritance occurs when a derived class inherits from more than one base class.
This allows the derived class to combine the functionalities of multiple base classes but
can also introduce complexity due to potential conflicts (e.g., methods with the same
name in different base classes).
Example:
cpp
Copy code
#include <iostream>
using namespace std;
class Animal {
public:
void eat() { cout << "Eating..." << endl; }
};
class Mammal {
public:
void walk() { cout << "Walking..." << endl; }
};
class Dog : public Animal, public Mammal { // Dog inherits from both Animal and
Mammal
public:
void bark() { cout << "Barking..." << endl; }
};
int main() {
Dog d;
d.eat(); // From Animal
d.walk(); // From Mammal
d.bark(); // Defined in Dog
return 0;
}
In this example:
• Dog inherits both eat() from Animal and walk() from Mammal.
• This showcases how Dog can combine functionality from multiple base classes.
3. Multilevel Inheritance
Multilevel inheritance occurs when a derived class serves as a base class for another
class, creating a chain of inheritance. It establishes a hierarchy where each level builds
upon the previous level.
Example:
cpp
Copy code
#include <iostream>
using namespace std;
class Animal {
public:
void eat() { cout << "Eating..." << endl; }
};
class Puppy : public Dog { // Puppy inherits from Dog, which inherits from Animal
public:
void weep() { cout << "Weeping..." << endl; }
};
int main() {
Puppy p;
4. Hierarchical Inheritance
Hierarchical inheritance occurs when multiple derived classes inherit from a single base
class. This allows each derived class to share and possibly extend the properties and
behaviors of the base class.
Example:
cpp
Copy code
#include <iostream>
using namespace std;
class Animal {
public:
void eat() { cout << "Eating..." << endl; }
};
public:
void meow() { cout << "Meowing..." << endl; }
};
int main() {
Dog d;
Cat c;
d.eat(); // Inherited from Animal
d.bark(); // Defined in Dog
c.eat(); // Inherited from Animal
c.meow(); // Defined in Cat
return 0;
}
In this example:
• Both Dog and Cat are derived classes that inherit from the same base class, Animal.
• Dog and Cat share the eat() method but have their own unique methods: bark() for
Dog and meow() for Cat.
5. Hybrid Inheritance
Hybrid inheritance is a combination of two or more of the above types of inheritance. It
often involves both multiple and multilevel inheritance and can lead to the diamond
problem.
Diamond Problem
The diamond problem occurs in hybrid inheritance when two derived classes inherit from
the same base class, and a further class inherits from both of them. This creates ambiguity
because the final derived class gets two copies of the base class members. Virtual
inheritance solves this problem by ensuring only one instance of the base class exists.
Example:
cpp
Copy code
#include <iostream>
using namespace std;
class Animal {
public:
void eat() { cout << "Eating..." << endl; }
};
int main() {
Bat b;
b.eat(); // No ambiguity due to virtual inheritance
return 0;
}
In this example:
• Animal is the base class, with Mammal and Bird as derived classes using virtual
inheritance.
• Bat inherits from both Mammal and Bird, but Animal appears only once in Bat's
hierarchy due to virtual inheritance, avoiding ambiguity.
class A {
public:
void show() { cout << "A::show" << endl; }
};
class B : public A { };
class C : public A { };
int main() {
D d;
d.show(); // Error: ambiguous because of multiple inheritance from A
return 0;
}
Here, D inherits show() from both B and C, leading to ambiguity. To resolve this, you can
specify the path explicitly:
cpp
Copy code
d.B::show();
Virtual Base Class (Resolving the Diamond Problem):
To resolve ambiguity in complex inheritance structures, use virtual inheritance. By
declaring the base class as a virtual base class, C++ ensures only one instance of the base
class exists in the derived hierarchy.
cpp
Copy code
class A {
public:
void show() { cout << "A::show" << endl; }
};
int main() {
D d;
d.show(); // No ambiguity due to virtual inheritance
return 0;
}
5. Object Slicing
Object slicing occurs when an object of a derived class is assigned to an object of the base
class, "slicing" off the derived part.
cpp
Copy code
#include <iostream>
using namespace std;
class Base {
public:
int x;
};
int main() {
Derived d;
d.x = 1;
d.y = 2;
class Animal {
public:
virtual void sound() { cout << "Animal sound..." << endl; }
};
};
int main() {
Animal* a = new Dog();
a->sound(); // Calls Dog's sound() due to virtual function
delete a;
return 0;
}
class Base {
public:
Base() { cout << "Base Constructor" << endl; }
~Base() { cout << "Base Destructor" << endl; }
};
int main() {
Derived d;
return 0;
}
Output:
Copy code
Base Constructor
Derived Constructor
Derived Destructor
Base Destructor
In this example:
• The Base constructor is called before the Derived constructor.
• The Derived destructor is called before the Base destructor.