Cursusinhoud
C++ OOP
C++ OOP
Operatoroverbelasting
Operator-overloading is een krachtige functie in objectgeoriënteerde programmeertalen waarmee je het gedrag van operatoren voor zelfgedefinieerde classes
kunt herdefiniëren. Door operatoren te overladen, kun je aangepaste implementaties bieden voor bewerkingen met objecten van jouw class
, wat zorgt voor meer intuïtieve en expressieve code.
De syntaxis van operator-overloading
Overladen wordt bereikt door speciale lidfuncties of vriendfuncties te definiëren die het gewenste gedrag voor de operator implementeren. De syntaxis voor het overladen van operatoren varieert afhankelijk van de operator die je wilt overladen. De algemene vorm ziet er als volgt uit:
overloading.h
return_type operator operator_symbol(parameters);
De syntaxis, of meer specifiek, het aantal parameters en het retourtype, hangt ook af van de vraag of je operatoren overlaadt als lidfuncties of als vriendfuncties.
Member.h
Friend.h
class Example { public: Example operator+ (const Example& other) { // Define behavior for operator+ } };
De tabel van operatoren die kunnen worden overbelast
Hier is een tabel met alle operatoren die kunnen worden overbelast. Het is echter belangrijk op te merken dat, hoewel het mogelijk is om deze operatoren te overbelasten, het zeker niet noodzakelijk is om ze allemaal voor je classes
te overbelasten.
Overloaden van stream-invoegoperatoren
Om de operatoren <<
en >>
voor een class
te overloaden, definieer je doorgaans een friend-functie of een lidfunctie die een outputstream (std::ostream&
) of inputstream (std::istream&
) als linker operand neemt en een object van jouw class
als rechter operand. Deze functie formatteert vervolgens de gegevens van het object en streamt deze naar of van de stream.
Point.h
class Point { public: friend std::ostream& operator<<(std::ostream& out, const Point& point); friend std::istream& operator>>(std::istream& in, Point& point); private: int x, y; }; std::ostream& operator<<(std::ostream& out, const Point& point) { return out << "x: " << point.x << ", y: " << point.y << std::endl; } std::istream& operator>>(std::istream& in, Point& point) { return in >> point.x >> point.y; }
Het overloaden van stream-invoegoperatoren, zoals <<
, stelt je in staat om aangepast uitvoergedrag te definiëren voor objecten van jouw classes
wanneer deze naar een outputstream zoals std::cout
worden gestreamd. Deze functionaliteit is vooral nuttig voor het verbeteren van de leesbaarheid en bruikbaarheid van je code bij het werken met aangepaste datatypes.
Het overladen van de <<
-operator voor uitvoerstroom komt vaker voor dan het overladen van de >>
-operator voor invoerstroom, omdat invoerbewerkingen met >>
vaker foutgevoelig zijn.
Overladen van rekenkundige operatoren
Andere rekenkundige operatoren (-
, *
, /
, %
) kunnen op vergelijkbare wijze worden overladen om aangepaste bewerkingen met uw zelfgedefinieerde typen uit te voeren.
main.cpp
#include <iostream> class Point { public: Point(int xCoord, int yCoord) : x(xCoord), y(yCoord) {} Point operator+(const Point& other) { return Point(x + other.x, y + other.y); } int getX() { return x; } int getY() { return y; } private: int x, y; }; int main() { Point p = Point(2, 4) + Point(2, 6); std::cout << p.getX() << ' ' << p.getY() << std::endl; }
De operator wordt overbelast als een lidfunctie van de Point
-class
. Deze neemt een ander Point
-object als parameter en retourneert een nieuw Point
-object dat de som van de twee punten voorstelt. De +
-operator kan worden vervangen door -
, *
, /
of %
, met overeenkomstige aanpassingen in de implementatielogica.
Increment en Decrement
Zowel de prefix als postfix increment- en decrementoperators (++
en --
) kunnen worden overbelast voor aangepaste classes
. We beginnen met de prefixversie:
main.cpp
#include <iostream> class Point { public: Point(int xCoord, int yCoord) : x(xCoord), y(yCoord) {} // Prefix increment operator (++point) Point& operator++() { ++x; ++y; return *this; } int getX() { return x; } int getY() { return y; } private: int x, y; }; int main() { Point p(2, 2); ++p; std::cout << p.getX() << ' ' << p.getY() << std::endl; }
De decrement-operator wordt op vergelijkbare wijze als de increment-operator overbelast, met gebruik van de --
-operator en de aftrekbewerking.
Zoals je ziet is het overbelasten van de prefix increment- en decrement-operatoren eenvoudig. Het wordt echter lastiger bij de postfix varianten. Onthoud dat er een verschil is in de uitvoervolgorde tussen prefix- en postfix-bewerkingen.
Bij het overbelasten van postfix increment en decrement moet ook de uitvoervolgorde worden geïmplementeerd. Dit ziet er ongeveer zo uit:
main.cpp
#include <iostream> class Point { public: Point(int xCoord, int yCoord) : x(xCoord), y(yCoord) {} // Postfix increment operator (point++) Point operator++(int) { Point temp = *this; // Creating a temp variable ++(this->x); // Incrementing original Point's x ++(this->y); // Incrementing original Point's y return temp; // Returning created temp variable } int getX() { return x; } int getY() { return y; } private: int x, y; }; int main() { Point p(2, 2); p++; std::cout << p.getX() << ' ' << p.getY() << std::endl; }
De gehele parameter wordt zonder naam doorgegeven alleen om aan de compiler aan te geven dat de postfix-incrementoperator wordt overbelast. Dit is noodzakelijk omdat de declaraties voor prefix- en postfix-operatoren anders identiek zijn.
In de implementatie van de postfix-incrementoperator wordt de oorspronkelijke waarde van het object opgeslagen in een tijdelijke variabele (temp
). Het huidige object wordt vervolgens verhoogd, maar de operator retourneert de waarde die in temp
is opgeslagen. Dit betekent dat de variabele wordt verhoogd, maar de wijziging pas in de volgende expressie effect heeft, omdat de operator de oorspronkelijke waarde retourneert.
Bedankt voor je feedback!