Operatoren überladen

Hallo Leute,

Ich will einen Operator für die eigene Klasse(X) überladen, damit ich z.B. schreiben kann

X a(1,2),b(3,4),c;

c= a+b; //(a und b sollen nicht veraendert werden, c -> (4,6))

Wenn ich das dann wie eigentlich beschrieben so formuliere:

class X; //forward Declaration
class X{
int x,y;

X operator+(const X& a,const X& b)
{
x=a.x+b.x;
y=a.y+b.y;
return *this;
}
}

spuckt der Compiler das aus:
error C2804: Binärer Operator ‚+‘ hat zu viele Parameter
Wo liegt mein Fehler?

Danke schon mal
Gruß
husky

Hallo husky

Ich will einen Operator für die eigene Klasse(X) überladen,
damit ich z.B. schreiben kann
X a(1,2),b(3,4),c;
c= a+b; //(a und b sollen nicht veraendert werden, c ->

ok

Wenn ich das dann wie eigentlich beschrieben so formuliere:

Wo liegt mein Fehler?

Du brauchst einen ±Operator
ausserhalb der Klasse X, z.B. so:

 class X {
 int x,y;
 public: 
 X () { // default ctor
 x = y = 0;
 }
 X (int x0, int y0) { // 2-Param ctor
 x = x0;
 y = y0;
 }
 X operator += (const X& v) {
 x += v.x;
 y += v.y;
 return \*this;
 }
 };
 
 **X operator+ (const X& x1, const X& x2)**
**{**
 X **x** (x1);
 **x** += x2; // ruft c . X::operator+=(const X& v)
 **return x** ;
**}**

 int main(void)
{
 X a(1,2), b(3,4), c;
 c = a + b; 

 return 0;
}

Alles klar :wink:

Grüße

CMБ

Danke!
Hi CMБ,

Dank auch recht schön. Ich glaub, ich habs geschnallt.
Der += Operator war mir schon klar. Aber das der + Operator außerhalb der Definition stehen muss, wusste ich nicht.

Ciao,
husky

Dank auch recht schön. Ich glaub, ich habs geschnallt.
Der += Operator war mir schon klar. Aber das der + Operator
außerhalb der Definition stehen muss, wusste ich nicht.

Muss er nicht. Alle Operatoren, die als linken Operand ein Objekt derselben Klasse erwarten können innerhalb der Klasse definiert werden wie alle anderen Methoden auch, bei binären Operationen wird als parameter dann das rechte Argument angegeben.

Notwendig wird eine Definition außerhalb der Klasse lediglich dann, wenn der linke Operand einen anderen Typ als den den Klasse hat.

Notwendig wird eine Definition außerhalb der Klasse lediglich
dann, wenn der linke Operand einen anderen Typ als den den
Klasse hat.

Ist schon klar jetzt. Hab da schon ein bißchen rumprobiert und es klappt jetzt eigentlich alles so, wie es soll.
Meine Klasse ist natürlich wesentlich komplexer. Da sind Bitmaps drin, die modifiziert bzw. umkopiert werden müssen, auf sich selbst verkette Listen, und div. anderes Gelumpe. D.h. hinter C= A|B verbirgt sich eine ganze Menge Programmierarbeit, von der der End-User der Dll nichts mitbekommen soll, bzw. ich selbst mir einen Haufen Schreibarbeit sparen kann.
Also nochmal Danke,
husky

Hallo Nicos,

Alle Operatoren, die als linken Operand ein
Objekt derselben Klasse erwarten können innerhalb der Klasse
definiert werden wie alle anderen Methoden auch, bei binären
Operationen wird als parameter dann das rechte Argument
angegeben.

Wie würde man das beim X::operator+ realisieren,
ohne dass er seine „überlieferte“ Bedeutung
verliert bzw. der C+±Quelltext an expressiver
Kraft einbüßt?

Denke auch gerade darüber nach …

Grüße

CMБ

Alle Operatoren, die als linken Operand ein
Objekt derselben Klasse erwarten können innerhalb der Klasse
definiert werden wie alle anderen Methoden auch, bei binären
Operationen wird als parameter dann das rechte Argument
angegeben.

Wie würde man das beim X::operator+ realisieren,
ohne dass er seine „überlieferte“ Bedeutung
verliert bzw. der C+±Quelltext an expressiver
Kraft einbüßt?

Ich weiß jetzt nicht, was du meinst. Wenn definieren tust du das einfach so:

class X {
 public:
 X operator+( const X& a ) {
 X temp;
 /\* temp 

Bei der Auswertung eines Ausdrucks x+y benutzt der Compiler dann den linken Operand x als Basis und ruft dessen operator+ mit y auf. Wenn y dabei zusammengesetzt ist wird mit den normalen Operator-Prioritäten implizit geklammert. Die Schreibweise ist äquivalent zu


    
    x.operator+(y)




Wäre der Ausdruck entsprechend komplexer, z.B. x+y\*z, so wird dies ausgewertet als


    
    x.operator+(y.operator\*(z))

Hallo Nicos,

Wie würde man das beim X::operator+ realisieren,
ohne dass er seine „überlieferte“ Bedeutung
verliert bzw. der C+±Quelltext an expressiver
Kraft einbüßt?

Ich weiß jetzt nicht, was du meinst.

Ich meine Stroustrups Ansicht, dass
Member-Operatoren nur gebildet werden
sollten, wenn sie Klassendaten verändern,
wie z.B. operator += ()

Wenn definieren tust du das einfach so:

class X {
public:
X operator+( const X& a ) {
X temp;
/* temp

Ja, schon klar, aber was passiert hier?

 class X {
_compound data_;
 public X operator+ (const& X) {
 temp class X; erzeugt nochmal "compound data" innerhalb X
 ...
 return temp
 }
 }

Alles klar?

Bei der Auswertung eines Ausdrucks x+y benutzt der Compiler
dann den linken Operand x als Basis und ruft dessen operator+
mit y auf. Wenn y dabei zusammengesetzt ist wird mit den
normalen Operator-Prioritäten implizit geklammert.

Schon klar, aber 2 Probleme bei
2-Para-Member-Operatoren, (wie Du weisst):

  • keine implizite Typkonvertierung bei trivialen Konvertierungen
  • bei mehrfacher Anwendung werden immer neue Klassen in
    der Klasse (auf dem Stack) erzeugt

Sowohl Stroustrup als auch Koenig/Moe (ISBN: 0201423391 Buch anschauen)
erklären es daher „zum guten Stil“, 2-Parameter-Operatoren
nicht als member und besser ggf. als friends zu deklarieren.

Grüße

CMБ

Hallo Nicos,

Wie würde man das beim X::operator+ realisieren,
ohne dass er seine „überlieferte“ Bedeutung
verliert bzw. der C+±Quelltext an expressiver
Kraft einbüßt?

Ich weiß jetzt nicht, was du meinst.

Ich meine Stroustrups Ansicht, dass
Member-Operatoren nur gebildet werden
sollten, wenn sie Klassendaten verändern,
wie z.B. operator += ()

Um das mal überspitzt auszudrücken hätte er dann vielleicht nicht die Möglichkeit einbauen dürfen, binäre const-Operatoren als Members zu definieren.

Ja, schon klar, aber was passiert hier?

class X {
compound data;
public X operator+ (const& X) {
temp class X; erzeugt nochmal „compound data“
innerhalb X

return temp
}
}

Alles klar?

Auch bei 2-Para Operatormethoden wird ein temporäres Objekt auf dem Stack erzeugt, anders könnte man ja z.B. Ausdrücke wie a=b+c schlecht realisieren. Von irgendwo muss ja das „=“ seinen rechten Operand bekommen, und wenn man von extrem spezialisierten Optimierungen (z.B. Chaining oder implizite Erzeugung von entsprechenden 3- oder höherwertigen Operatoren (viel Spass dabei :stuck_out_tongue: )) mal absieht, wird das wohl immer in einem neuen Objekt auf dem Stack resultieren. Lediglich der linke Operand ist in diesem Fall halt festgelegt, und man braucht noch nicht mal einen potentiell riesigen „friend“-Block. (Eine Klasse die ich grade vor mir habe hat 8 const-Operatoren, wenn man selber welche definieren könnte wären es 18 für diverse andere Operationen.)

Schon klar, aber 2 Probleme bei
2-Para-Member-Operatoren, (wie Du weisst):

  • keine implizite Typkonvertierung bei trivialen
    Konvertierungen

ACK für das linke Argument. Allerdings wird man in vielen Fällen sowieso wissen, mit welchem Typ man es zu tun hat und kann des Cast genauso gut explizit vornehmen. Das hat wohl auch Vor- und Nachteile, aber bei dem Typengewusel in C++ vertrete ich die Meinung, dass ein expliziter Cast zuviel besser ist als einmal falsche Hoffnung an der falschen Stelle.

  • bei mehrfacher Anwendung werden immer neue Klassen in
    der Klasse (auf dem Stack) erzeugt

Der Stack ist (normalerweise) nicht an die Klasse gebunden. Auch nicht-Memberoperatoren müssen wie erwähnt ein passendes temporäres Objekt zurückliefern, das dann genauso auf dem Stack der die Methode aufrufenden Funktion landet.

Sowohl Stroustrup als auch Koenig/Moe (ISBN: 0201423391 Buch anschauen)
erklären es daher „zum guten Stil“, 2-Parameter-Operatoren
nicht als member und besser ggf. als friends zu deklarieren.

Andere vertreten die Meinung, dass der „friend“-Mechanismus zumindest seltsam ist und in einigen Fällen die Wartbarkeit nicht unbedingt verbessert. Die einzige Verwendung die ich dafür bisher gefunden habe waren schnelle, hässliche Hacks wenn ich ums Verrecken nicht noch 20 Accessor-Methoden schreiben wollte.

Ohne Zusammenhang: in C++ friends access you but you cant access friends

Hallo Nicos,

Ich sehe erfreut, dass wir uns soweit
verstehen. Für meinen Geschmack (und mehr
Gründe als den Geschmack gibt es letztlich
dabei nicht) ist eine temporäre Klassen-
erzeugung innerhalb derselben Klasse
möglichst zu vermeiden. Ich bekomme da
immer Augenschmerzen, aber das ist wohl
mein Problem :wink: Es geht natürlich auch anders.

in C++ friends access you but you cant
access friends

:wink:

Übrigens, ich vermute (aus den Dokumenten zur C+±Enstehung)
stark, dass es exakt diese obige Problemstellung war,
die zur Einführung der „friends“ zwang. Dieses Design-
prinzip zieht sich ja ebenfalls durch die STL.

Grüße

CMБ