blog
blog copied to clipboard
C++ Operator Overloading
Concept: C++ allows you to redefine how standard operators work when used with class objects.
1. Unary Operator Overloading
Overloading the unary minus (-
) operator to negate the data members of a class.
#include <iostream>
class Number {
private:
int value;
public:
// Parameterized constructor
// Initializes the value of the object to val
// Number(int val) : value(val) {}
// Same as:
Number(int val) {
value = val;
}
// Overload the unary minus operator to negate
// the value of the object (the data members of a class)
Number operator-() const {
return Number(-value);
}
void display() const {
std::cout << "Value: " << value << std::endl;
}
};
int main() {
// Create a num object of type Number with the parameterized constructor
Number num(5); // Initializes the num object to 5
Number negated = -num; // Calls the overloaded unary minus operator
negated.display(); // Displays "Value: -5"
// Create a num2 object of int type
int num2 = 5;
int negated2 = -num2; // Calls the built-in unary minus operator
std::cout << negated2 << std::endl; // -5
return 0;
}
2. Binary Operator Overloading
Overloading the binary plus (+
) operator to add objects of a class.
#include <iostream>
class MyNumber {
private:
int value;
public:
// Default constructor. Initializes the value of the object to 0
// MyNumber() : value(0) {}
// Parameterized constructor. Initializes the value of the object to val
// MyNumber(int val) : value(val) {}
// Same as:
MyNumber(int val) {
value = val;
}
/**
* Overloads the + operator to add two MyNumber objects.
*
* This member function returns a new MyNumber object whose value
* is the sum of the calling object's value and the passed object's value.
*
* @param other: A constant reference to another MyNumber object
* that we want to add to the current object.
*
* @return: Returns a new MyNumber object representing the sum
* of the current object and the passed object.
*/
MyNumber operator+(const MyNumber& other) const {
// The 'this' pointer points to the calling object.
// Through the this pointer, we can access all members
// (public, protected, and private) of that object.
// In the context of this overloaded operator, the calling object is the one
// on the left side of the + operator.
// For example, in the expression 'a + b', if 'a' and 'b' are MyNumber objects,
// 'a' is the calling object and 'b' is the 'other' object.
// 'this->value' gets the value of the calling object.
// 'other.value' gets the value of the 'other' object.
// Create a new MyNumber object with the combined value.
// return MyNumber(value + other.value);
// Using this-> to make it clear that we are accessing the value of
// the calling object (the member variable):
return MyNumber(this->value + other.value);
}
void display() const {
std::cout << "Value: " << value << std::endl;
}
};
int main() {
// Create two objects of type MyNumber with parameterized constructors
MyNumber num1(5); // Initializes the num1 object to 5
MyNumber num2(10); // Initializes the num2 object to 10
MyNumber sum = num1 + num2; // Calls the overloaded + operator
sum.display(); // Displays "Value: 15"
// Create two objects of type int
int num3 = 5;
int num4 = 10;
int sumOfNum3AndNum4 = num3 + num4; // Calls the built-in + operator
std::cout << sumOfNum3AndNum4 << std::endl; // 15
return 0;
}
3. Overloading the Stream Insertion Operator
Overloading the stream insertion operator (<<
) for easy display of objects.
#include <iostream>
class Point {
private:
int x, y;
public:
// Parameterized constructor.
// Initializes data members x and y to the values passed as arguments.
// Point(int x, int y) : x(x), y(y) {}
// Same as:
Point(int x, int y) {
this->x = x;
this->y = y;
}
// Make it as a friend function of the class Point so that it can access
// private data members x and y.
friend std::ostream& operator<<(std::ostream& os, const Point& p);
};
/**
* Overloading the stream insertion operator (<<) for the Point class.
* This allows objects of the Point class to be easily printed using
* standard C++ streams, such as std::cout.
* @param os: a reference to the output stream where we want to send our formatted data.
* @param p: a reference to the Point object we want to display.
* @return a reference to the output stream.
*/
std::ostream& operator<<(std::ostream& os, const Point& p) {
// Send a string to the output stream, 'os', with the format "Point(x, y)".
// Access the private members 'x' and 'y' of the Point 'p'.
// The use of 'p.x' and 'p.y' here is possible because this operator function
// is declared as a friend of the Point class, allowing it to access private members.
os << "Point(" << p.x << ", " << p.y << ")";
// Return the modified output stream to allow for chained insertion operations.
// For example: std::cout << point1 << point2;
return os;
}
int main() {
Point p(5, 10); // Instantiate a Point object (p) with x = 5 and y = 10
std::cout << p << std::endl; // Displays "Point(5, 10)"
return 0;
}
4. Overloading the Assignment Operator
Creating a custom assignment operator for deep copying.
Note: Deep copy duplicates the actual data an object references, while shallow copy only duplicates the references to the data.
#include <iostream>
class Integer {
private:
// A pointer to an integer.
// This represents the value of the Integer object.
int* value;
public:
// Parameterized constructor that initializes the 'value' pointer
// with a dynamically allocated integer.
// Integer(int val) : value(new int(val)) {}
// Same as:
Integer(int val) {
value = new int(val);
}
// Overloaded copy assignment operator to provide deep copy functionality.
Integer& operator=(const Integer& other) {
// First, check for self-assignment.
// Comparing 'this' pointer with the address of the 'other' object.
if (this != &other) {
// If this isn't a self-assignment, free the memory of the current object.
delete value;
// Allocate new memory and copy the value from the 'other' object.
value = new int(*(other.value));
}
// Return *this to allow for chained assignment like a = b = c.
return *this;
}
// Display the value pointed by 'value' pointer.
void display() const {
std::cout << "Value: " << *value << std::endl;
}
// Destructor to free the dynamically allocated memory for 'value'
// when the object is destroyed.
~Integer() {
delete value;
}
};
int main() {
// Create two Integer objects with values 5 and 10 respectively.
Integer num1(5);
Integer num2(10);
// Use the overloaded assignment operator to copy the value from 'num2' to 'num1'.
num1 = num2;
// Display the value of 'num1', which now should be 10 after the assignment.
num1.display(); // Expected output: "Value: 10"
return 0;
}