Operator Overloading¶
Motivation¶
Returning back to the venerable class point
,
class point
{
public:
point(int x, int y) : _x{x}, _y{y} {}
int x() const { return _x; }
int y() const { return _y; }
void move(point vec);
private:
int _x;
int _y;
};
Equality of two points? I don’t want to have another function (or static method) like,
if (points_equal(p1, p2))
...
Enter operators
I want to write something like
if (p1 == p2)
...
What else?
Arithmetic operators: 2D space os full of arithmetic, so why not write for example …
point vec{2,3} p += vec;
Stream operators: this is rather clumsy,
std::cout << '(' << p.x() << ',' << p.y() << ')' << std::endl;
I want to shift a point out,
std::cout << p << std::endl;
Implementing (In)Equality¶
Operators are ordinary functions
Only named a bit oddly:
operator==()
Most operators can be defined in two way
As global function (equality would then take two
point
parameters)As object method: “hey left point, are you equal to this other point?”
Implementing (In)Equality: Global Function¶
Global function: a third party authority (global function, not belonging to a class) examines two points for equality
Makes use of overloading: there might be other
==
operators defined for different typesNo access to private members of operands (could use the
friend
keyword though)
Makes sense to define equality and inequality together
⟶ one can be implemented in terms of the other
#include <gtest/gtest.h>
class point
{
public:
point(int x, int y) : _x{x}, _y{y} {}
int x() const { return _x; }
int y() const { return _y; }
private:
int _x;
int _y;
};
static inline bool operator==(point lhs, point rhs) // <--- global function (not inside class definition) ("static inline" is another story)
{
return
lhs.x() == rhs.x() && // <--- using public access methods
lhs.y() == rhs.y(); // <--- using public access methods
}
static inline bool operator!=(point lhs, point rhs) // <--- global function (not inside class definition) ("static inline" is another story)
{
return !operator==(lhs, rhs); // <--- defined in terms of "=="
}
TEST(operators_suite, equals_global)
{
point p1{2, 3};
point p2{3, 4};
ASSERT_NE(p1, p2);
ASSERT_EQ(p1, p1);
// operators are ordinary functions:
ASSERT_EQ(operator==(p1, p2), false);
ASSERT_EQ(operator!=(p1, p2), true);
}
Implementing (In)Equality: Object Method¶
As a matter of taste: why not ask a point object if it is equal to another point object
Preferred by most: does not have the difficulties from above
Usage no different between both ways
Equality check does not normally modify objects ⟶
const
#include <gtest/gtest.h>
class point
{
public:
point(int x, int y) : _x{x}, _y{y} {}
int x() const { return _x; }
int y() const { return _y; }
bool operator==(point rhs) const // <--- object method (const!)
{
return
_x == rhs._x && // <--- direct member access possible
_y == rhs._y; // <--- direct member access possible
}
bool operator!=(point rhs) const // <--- object method (const!)
{
return !operator==(rhs); // <--- again, defined in terms of "=="
}
private:
int _x;
int _y;
};
TEST(operators_suite, equals_object)
{
point p1{2, 3};
point p2{3, 4};
ASSERT_NE(p1, p2);
ASSERT_EQ(p1, p1);
// operators are ordinary methods:
ASSERT_EQ(p1.operator==(p2), false);
ASSERT_EQ(p1.operator!=(p2), true);
}
Implementing Arithmetic: +
(Vector Addition)¶
As with
operator==()
, there are two waysGlobal function
operator+(point lhs, point rsh)
Preferred: object method
operator+(point rhs) const
Implementing Arithmetic: +
(Vector Addition): Global Function¶
#include <gtest/gtest.h>
class point
{
public:
point(int x, int y) : _x{x}, _y{y} {}
int x() const { return _x; }
int y() const { return _y; }
private:
int _x;
int _y;
};
point operator+(point lhs, point rhs) // <--- global function (not inside class definition) ("static inline" is another story)
{
int x = lhs.x() + rhs.x(); // <--- using public access methods
int y = lhs.y() + rhs.y(); // <--- using public access methods
return point{x, y};
}
TEST(operators_suite, vector_addition_global)
{
point p1{2, 3};
point p2{3, 4};
point sum = p1 + p2;
ASSERT_EQ(sum.x(), 5);
ASSERT_EQ(sum.y(), 7);
}
Implementing Arithmetic: +
(Vector Addition): Object Method¶
#include <gtest/gtest.h>
class point
{
public:
point(int x, int y) : _x{x}, _y{y} {}
int x() const { return _x; }
int y() const { return _y; }
point operator+(point rhs) const // <--- object method (const!)
{
int x = _x + rhs._x; // <--- direct member access possible
int y = _y + rhs._y; // <--- direct member access possible
return point{x, y};
}
private:
int _x;
int _y;
};
TEST(operators_suite, vector_addition_object)
{
point p1{2, 3};
point p2{3, 4};
point sum = p1 + p2;
ASSERT_EQ(sum.x(), 5);
ASSERT_EQ(sum.y(), 7);
}
Implementing Arithmetic: +=
(Moving A Point)¶
+=
can only be an object method (it is there to modify an object, alas)Things are little confusing otherwise
⟶ Beginning in C,
+=
has a value#include <gtest/gtest.h> class point { public: point(int x, int y) : _x{x}, _y{y} {} int x() const { return _x; } int y() const { return _y; } point operator+(point rhs) const // <--- object method (const!) { int x = _x + rhs._x; // <--- direct member access possible int y = _y + rhs._y; // <--- direct member access possible return point{x, y}; } private: int _x; int _y; }; TEST(operators_suite, vector_addition_object) { point p1{2, 3}; point p2{3, 4}; point sum = p1 + p2; ASSERT_EQ(sum.x(), 5); ASSERT_EQ(sum.y(), 7); }
When overloading
operator+=()
, usually (a reference to) the modified object is the valueCould be a copy just as well ⟶ possibly expensive
⟶
*this
🤔
#include <gtest/gtest.h>
class point
{
public:
point(int x, int y) : _x{x}, _y{y} {}
int x() const { return _x; }
int y() const { return _y; }
point& operator+=(point rhs) // <--- object method (non-const, obviously)
{
_x += rhs._x;
_y += rhs._y;
return *this;
}
private:
int _x;
int _y;
};
TEST(operators_suite, point_plus_equal)
{
point p{2, 3};
point vec{1, 2};
point result = p += vec;
ASSERT_EQ(p.x(), 3);
ASSERT_EQ(p.y(), 5);
ASSERT_EQ(p.x(), result.x());
ASSERT_EQ(p.y(), result.y());
}
Implementing ostream
Shift: std::cout << ...
¶
Make use of overloading again
Overload (global)
std::ostream& operator<<(std::ostream&, point)
#include <iostream>
class point
{
public:
point(int x, int y) : _x{x}, _y{y} {}
int x() const { return _x; }
int y() const { return _y; }
private:
int _x;
int _y;
};
static std::ostream& operator<<(std::ostream& s, point p) // <--- "static inline" is another story
{
s << '(' << p.x() << ',' << p.y() << ')';
return s;
}
int main()
{
point p1{1,2};
point p2{3,4};
std::cout << "p1: " << p1 << ", p2: " << p2 << std::endl;
return 0;
}
$ ./c++03-ostream-shift-operator
p1: (1,2), p2: (3,4)