In C++, operators are implemented as functions. This chapter covers how to overload operators, implement overloaded typecasts, and define copy constructors.
class Cents {
private:
int m_cents {};
public:
Cents(int cents) : m_cents{ cents } { }
int getCents() const { return m_cents; }
friend Cents operator+(const Cents& c1, const Cents& c2); // add Cents + Cents using a friend function
friend Cents operator+(const Cents& c1, const Cents& c2) // overloading inside a class
{
return Cents { c1.m_cents + c2.m_cents };
}
};
Cents operator+(const Cents& c1, const Cents& c2) // note: this function is not a member function!
{
return Cents { c1.m_cents + c2.m_cents }; // access m_cents directly because this is a friend function
}
Cents operator+(const Cents& c1, int value)
{
return Cents { c1.m_cents + value };
}
Cents operator+(int value, const Cents& c1)
{
return Cents { c1.m_cents + value };
}
MinMax operator+(int value, const MinMax& m)
{
return m + value; // call operator+(MinMax, int)
}
Prefer overloading operators as normal functions instead of friends if it’s possible to do so without adding additional functions.
class Point {
private:
double m_x{};
double m_y{};
double m_z{};
public:
Point(double x=0.0, double y=0.0, double z=0.0): m_x{x}, m_y{y}, m_z{z} {}
friend std::ostream& operator<< (std::ostream& out, const Point& point);
};
std::ostream& operator<< (std::ostream& out, const Point& point)
{
out << "Point(" << point.m_x << ", " << point.m_y << ", " << point.m_z << ')';
{
double x{};
double y{};
double z{};
in >> x >> y >> z;
if (in)
point = Point{x, y, z}; // overwrite our existing point
return in;
}
Point point{};
std::cin >> point;
std::cout << "You entered: " << point;
class Point {
private:
double m_x{};
double m_y{};
double m_z{};
public:
Point(double x=0.0, double y=0.0, double z=0.0): m_x{x}, m_y{y}, m_z{z} {}
Point operator+ (int value) const
{
return Point { m_x + value, m_y, m_z };
}
};
class Cents {
private:
int m_cents {};
public:
Cents(int cents) : m_cents{ cents } { }
int getCents() const { return m_cents; }
Cents operator-() const
{
return Cents{-m_cents}; // return a new Cents object with the negated value
}
};
class Digit {
private:
int m_digit {};
public:
Digit(int digit) : m_digit{ digit } { }
Digit& operator++() // No parameter means this is prefix operator++
{
++m_digit;
return *this;
}
Digit operator++(int) // int parameter means this is postfix operator++
{
Digit temp{*this}; // Create a temporary variable with our current digit
++(*this); // Use prefix operator to increment this digit
return temp; // return saved state
}
};
class IntList {
private:
int m_list[10]{};
public:
int& operator[] (int index)
{
return m_list[index];
}
const int& operator[] (int index) const
{
return m_list[index];
}
};
class Matrix {
private:
double m_data[4][4]{};
public:
double& operator()(int row, int col)
{
assert(row >= 0 && row < 4);
assert(col >= 0 && col < 4);
return m_data[row][col];
}
};
class Cents {
private:
int m_cents {};
public:
Cents(int cents) : m_cents{ cents } { }
operator int() const { return m_cents; } // overloaded int cast
};
class Fraction {
private:
int m_numerator{};
int m_denominator{};
public:
Fraction(int numerator = 0, int denominator = 1)
: m_numerator{ numerator }, m_denominator{ denominator } { }
Fraction& operator= (const Fraction& fraction)
{
if (this == &fraction)
return *this;
m_numerator = fraction.m_numerator;
m_denominator = fraction.m_denominator;
return *this;
}
};
class Fraction {
public:
Fraction(const Fraction ©) = delete;
};
The default copy constructor and default assignment operators do shallow copies, which is fine for classes that contain no dynamically allocated variables. Classes with dynamically allocated variables need to have a copy constructor and assignment operator that do a deep copy.