What is a + operator in C++?

Overload af ++ og -- Operatorer i C++

09/11/1999

Rating: 3.93 (2134 votes)

I C++ er operator overloading en kraftfuld funktion, der giver programmører mulighed for at omdefinere, hvordan standardoperatorer opfører sig med brugerdefinerede typer (klasser). Dette kan gøre koden mere læsbar og intuitiv, da det lader vores egne klasser opføre sig på samme måde som primitive datatyper som int eller double. Blandt de mest almindeligt overloadede operatorer er inkrement- (++) og dekrement- (--) operatorerne. At forstå, hvordan man overloader dem korrekt, er afgørende for at skrive elegant og effektiv C++ kode.

How to overload increment (++) and decrement (--) operators in C++?
The increment (++) and decrement (--) operators are two important unary operators available in C++. Following example explain how increment (++) operator can be overloaded for prefix as well as postfix usage. Similar way, you can overload operator (--). hours = 0; minutes = 0; } Time(int h, int m) { hours = h;

Denne artikel vil guide dig igennem processen med at overloade både prefix- og postfix-versionerne af inkrement- og dekrement-operatorerne. Vi vil se på den syntaktiske forskel, implementeringsdetaljer og de bedste praksisser for at sikre, at dine klasser er både robuste og nemme at bruge.

Indholdsfortegnelse

Hvad er Inkrement- (++) og Dekrement- (--) Operatorerne?

Inkrement- (++) og dekrement- (--) operatorerne er unære operatorer, hvilket betyder, at de opererer på en enkelt operand. Deres primære funktion er henholdsvis at øge eller mindske værdien af en variabel med én. Det unikke ved disse operatorer er, at de findes i to forskellige former:

  • Prefix-form: Operatoren placeres før operanden (f.eks. ++x eller --x). I denne form bliver værdien af operanden først ændret (inkrementeret eller dekrementeret), og derefter bruges den nye værdi i det omgivende udtryk.
  • Postfix-form: Operatoren placeres efter operanden (f.eks. x++ eller y--). Her bliver den oprindelige værdi af operanden brugt i udtrykket, og først derefter bliver værdien ændret.

Denne subtile forskel er afgørende for, hvordan udtryk evalueres, og det er vigtigt at bevare denne adfærd, når vi overloader operatorerne for vores egne klasser.

Hvorfor Overloade disse Operatorer?

Forestil dig, at du har designet en klasse til at repræsentere en tæller, en dato eller måske en iterator for en brugerdefineret datastruktur. I alle disse tilfælde giver det intuitiv mening at kunne "gå til den næste". For en tæller betyder det at øge dens værdi. For en dato betyder det at gå til næste dag. For en iterator betyder det at pege på det næste element i en samling.

What are the requirements for overloaded operators?
Overloaded operators must either be a nonstatic class member function or a global function. A global function that needs access to private or protected class members must be declared as a friend of that class. A global function must take at least one argument that is of class or enumerated type or that is a reference to a class or enumerated type.

Ved at overloade inkrement-operatoren kan du skrive kode som myCounter++; eller ++currentDate;. Dette er ikke kun kortere og mere elegant end at skulle kalde en metode som myCounter.increment();, men det gør også din klasse mere brugervenlig og integreret i sprogets naturlige flow. Det reducerer den kognitive belastning for andre udviklere, der skal bruge din klasse, da de kan anvende velkendte operatorer.

Overloading af Prefix-Versionen (++x og --x)

Prefix-versionen er den mest ligefremme at overloade. Da den modificerer objektet selv og derefter returnerer det modificerede objekt, implementeres den typisk som en medlemsfunktion, der returnerer en reference til det nuværende objekt (*this).

Syntaksen for at erklære en prefix-inkrement-operator i en klasse ser således ud:

ClassName& operator++();

Lad os se på et eksempel med en simpel Tæller klasse:

#include <iostream> class Tæller { private: int værdi; public: Tæller(int startVærdi = 0): værdi(startVærdi) {} // Overload af prefix inkrement (++tæller) Tæller& operator++() { // Inkrementer den interne værdi ++værdi; // Returner en reference til det modificerede objekt return *this; } void visVærdi() const { std::cout << "Værdi: " << værdi << std::endl; } }; int main() { Tæller minTæller(5); std::cout << "Før inkrement: "; minTæller.visVærdi(); ++minTæller; std::cout << "Efter prefix inkrement: "; minTæller.visVærdi(); // Viser Værdi: 6 Tæller& ref = ++minTæller; std::cout << "Efter endnu et prefix inkrement: "; ref.visVærdi(); // Viser Værdi: 7 minTæller.visVærdi(); // Viser også Værdi: 7 return 0; }

I dette eksempel øger operator++() den private medlemsvariabel værdi og returnerer derefter *this. At returnere en reference (Tæller&) er vigtigt af to grunde:

  1. Effektivitet: Det undgår at skabe en unødvendig kopi af objektet.
  2. Kædning: Det tillader kædning af operatorer (f.eks. ++(++minTæller)), hvilket matcher opførslen for indbyggede typer.

Overloading af prefix dekrement (--x) følger nøjagtig samme mønster, blot ved at dekrementere værdien i stedet for at inkrementere den.

Overloading af Postfix-Versionen (x++ og x--)

Postfix-versionen er en smule mere kompliceret. Den skal returnere objektets tilstand, *før* det blev ændret, men stadig udføre ændringen. Hvordan kan vi både returnere den gamle værdi og opdatere objektet? Løsningen involverer at oprette en midlertidig kopi.

What is operator overloading in C++?
Through these enhancements, the Point class becomes more versatile and easier to use, illustrating the power of operator overloading in C++. Embracing operator overloading as a global function in C++ is a powerful way to define how operators interact with custom types. This method doesn’t just streamline your code; it also makes it smarter.

For at skelne mellem prefix og postfix i C++, har sproget en speciel regel: Postfix-versionen af operatoren deklareres med en ekstra dummy-parameter af typen int. Denne parameter bruges ikke, men dens tilstedeværelse signalerer til compileren, at det er postfix-versionen.

Syntaksen for postfix-inkrement er:

ClassName operator++(int);

Bemærk to vigtige forskelle fra prefix-versionen:

  • Tilstedeværelsen af int-parameteren.
  • Returtypen er ClassName (en værdi/kopi), ikke ClassName& (en reference). Dette er nødvendigt, fordi vi returnerer en midlertidig variabel, som vil blive destrueret, når funktionen afsluttes. Det er usikkert at returnere en reference til en lokal variabel.

Lad os udvide vores Tæller-klasse med postfix-overloads:

#include <iostream> class Tæller { private: int værdi; public: Tæller(int startVærdi = 0): værdi(startVærdi) {} // Prefix ++tæller Tæller& operator++() { ++værdi; return *this; } // Postfix tæller++ Tæller operator++(int) { // 1. Gem den nuværende tilstand i en midlertidig kopi Tæller temp = *this; // 2. Inkrementer det faktiske objekt (genbrug af prefix-logik) ++(*this); // 3. Returner den gemte, gamle tilstand return temp; } void visVærdi() const { std::cout << "Værdi: " << værdi << std::endl; } }; int main() { Tæller t1(10), t2(20); std::cout << "Prefix demonstration:\n"; Tæller resultat_prefix = ++t1; std::cout << "Resultat prefix: "; resultat_prefix.visVærdi(); // Viser 11 std::cout << "t1 efter prefix: "; t1.visVærdi(); // Viser 11 std::cout << "\nPostfix demonstration:\n"; Tæller resultat_postfix = t2++; std::cout << "Resultat postfix: "; resultat_postfix.visVærdi(); // Viser 20 (den gamle værdi) std::cout << "t2 efter postfix: "; t2.visVærdi(); // Viser 21 (er nu blevet inkrementeret) return 0; } 

I operator++(int) ser vi den klassiske implementering: En kopi (temp) oprettes, det nuværende objekt (*this) inkrementeres (her genbruger vi smart nok vores prefix-operator), og til sidst returneres den oprindelige kopi. Dette sikrer, at t2++ returnerer værdien 20, mens t2 selv bliver til 21.

Sammenligningstabel: Prefix vs. Postfix Overloading

For at opsummere er her en direkte sammenligning af de to former:

EgenskabPrefix (++x)Postfix (x++)
FunktionssignaturClassName& operator++()ClassName operator++(int)
ReturværdiReference til det modificerede objekt (*this)Kopi af objektet før modifikation
EffektivitetHøjere (ingen kopiering)Lavere (kræver oprettelse af midlertidig kopi)
HandlingsrækkefølgeModificerer først, returnerer derefter den nye værdiGemmer først, modificerer derefter, returnerer den gemte (gamle) værdi

Ofte Stillede Spørgsmål (FAQ)

Hvorfor har postfix-versionen en 'int'-parameter, som ikke bruges?

Det er udelukkende en syntaktisk konvention i C++ til at skelne mellem de to versioner af operatoren. Da begge ellers ville have samme navn (operator++) og ingen parametre (de er unære), var der brug for en måde at adskille dem på for compileren. Parameteren har typisk intet navn, da den aldrig anvendes i funktionens krop.

Er postfix-operatoren altid langsommere end prefix?

Ja, generelt set. For brugerdefinerede typer indebærer postfix-versionen oprettelse og returnering af et midlertidigt objekt, hvilket er en ekstra omkostning sammenlignet med prefix-versionens direkte modifikation og returnering af en reference. For simple, indbyggede typer (som int) kan moderne compilere ofte optimere forskellen væk, men for komplekse klasser er det en god praksis at foretrække prefix-operatoren, medmindre du specifikt har brug for den gamle værdi.

What happens if overloading is done outside a class?
If overloading is done outside a class (i.e. it is not a member function of a class), the overloaded ‘new’ and ‘delete’ will be called anytime you make use of these operators (within classes or outside classes). This is global overloading. The syntax for overloading the new operator :

Skal jeg implementere begge versioner?

Det afhænger af din klasses formål. Hvis det giver mening for brugere af din klasse at anvende både ++x og x++, bør du implementere begge for at give en komplet og forventet brugerflade. Hvis kun én af dem er logisk relevant, kan du nøjes med den. En god praksis er at implementere postfix-versionen ved hjælp af prefix-versionen for at undgå kodegentagelse.

Konklusion og Bedste Praksis

At overloade inkrement- og dekrement-operatorerne er et glimrende eksempel på, hvordan C++ giver udviklere mulighed for at skabe dybt intuitive og udtryksfulde klasser. Ved at følge de etablerede konventioner kan dine brugerdefinerede typer integreres problemfrit med resten af sproget.

Husk disse nøglepunkter:

  • Konsistens: Få dine overloadede operatorer til at opføre sig så tæt på de indbyggede typer som muligt for at undgå forvirring.
  • Prefix returnerer reference: Returner altid en reference (&) fra prefix-versionen for at forbedre ydeevnen og tillade kædning. Dette er et eksempel på at return *this.
  • Postfix bruger dummy-parameter og returnerer kopi: Brug int-dummy-parameteren til at definere postfix-versionen og returner en kopi af objektets tilstand før ændringen.
  • Foretræk prefix: Når du skriver kode, der bruger disse operatorer (f.eks. i loops), bør du foretrække prefix-versionen, medmindre du specifikt har brug for postfix-adfærden, da det ofte er mere effektivt for komplekse typer.

Ved at mestre disse teknikker tager du et vigtigt skridt mod at skrive mere professionel, læsbar og effektiv C++ kode.

Hvis du vil læse andre artikler, der ligner Overload af ++ og -- Operatorer i C++, kan du besøge kategorien Sundhed.

Go up