Frage zu C++ Programm (Operator-Überladung) [gelöst]

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Frage zu C++ Programm (Operator-Überladung) [gelöst]

Beitrag von Duff » 08.12.2007 11:15:10

Hallo,

ich bin dabei mir die Sprache C++ anzueignen und versuche nun ein kleines Programm zur Operator-Überladung zu schreiben. Doch leider komme ich nicht weiter.

Was ich bisher gemacht habe:

Die Klasse Bruch:

Code: Alles auswählen

// bruch.h
#include <iostream>

#ifndef _BRUCH_H_
#define _BRUCH_H_
using namespace std;

class Bruch {
private:
        // Eigenschaften der Klasse Bruch
        double zaehler;
        double nenner;

public:
        // Deklaration der Konstruktoren
        Bruch( double, double );
        Bruch();
        Bruch operator*( Bruch b1, Bruch b2 );

        // Fähigkeiten (Methoden) der Klasse Bruch
        double value( void );

        // Zugriffsmethoden zum Abfragen der Eigenschaften
        double get_zaehler( void );
        double get_nenner( void );

        // Zugriffsmethoden zum Setzen der Eigenschaften
        void set_zaehler( double z );
        void set_nenner( double n );

        // Ausgabe aller Eigenschaften
        void print(void);
};
#endif
Datei mit den Methoden und Definitionen:

Code: Alles auswählen

// bruch.cpp
#include <iostream>
#include <cstring>
#include "bruch.h"
using namespace std;

// Definition der Konstruktoren - Anfang
Bruch::Bruch( double z, double n ) {
        zaehler=z;
        nenner=n;
}

// Der Standardkonstruktor
Bruch::Bruch() {
        zaehler=0;
        nenner=1;
}

Bruch operator*( Bruch b1, Bruch b2 ) {
        Bruch tmp;
        tmp.set_zaehler(b1.get_zaehler() * b2.get_zaehler());
        tmp.set_nenner(b1.get_nenner() * b2.get_nenner());
        return tmp;
}

// Zugriffsmethoden zum Abfragen der Eigenschaften
double Bruch::get_zaehler( void ) {
        return zaehler;
}

double Bruch::get_nenner( void ) {
        return nenner;
}

double Bruch::value( void ) {
        return (zaehler/nenner);
}


// Zugriffsmethoden zum Setzen der Eigenschaften
void Bruch::set_zaehler( double z ) {
        zaehler=z;
}

void Bruch::set_nenner( double n ) {
        nenner=n;
}
Hauptdatei:

Code: Alles auswählen

// main.cpp
#include "bruch.h"

int main(void) {
        Bruch b1( 3, 5);
        Bruch b2( 10, 2);
        Bruch sum;
        sum=operator*( b1, b2);

        cout << "b1 " << b1.value() << "\n";
        cout << "b2 " << b2.value() << "\n";
        cout << sum.value() << "\n";

        return 0;
}
Doch ich bekomme die Dateien nicht kompiliert aber weiß nicht, was ich nun verändern muss.

Code: Alles auswählen

daniel@daniel-laptop:~/scripts/C++$ g++ -Wall -o Bruch bruch.cpp main_bruch.cpp
bruch.h:18: error: 'Bruch Bruch::operator*(Bruch, Bruch)' must take either zero or one argument
bruch.h:18: error: 'Bruch Bruch::operator*(Bruch, Bruch)' must take either zero or one argument
main_bruch.cpp: In function 'int main()':
main_bruch.cpp:8: error: 'operator*' not defined
Zuletzt geändert von Duff am 08.12.2007 11:32:20, insgesamt 1-mal geändert.
Oh, yeah!

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 08.12.2007 11:31:49

Habe den Fehler gefunden.

In der bruch.h fehlte die Deklaration für die Operator-Überladung:

Code: Alles auswählen

...
friend Bruch operator*( Bruch b1, Bruch b2 );
...
Wenn ich dass Ganze nun richtig verstanden habe, benötige ich die friend-Funktion, damit ich auf die Eigenschaften (Daten) der Klasse Bruch zugreifen kann, die als private gekennzeichnet sind (double _zaehler, double _nenner).
Oh, yeah!

Benutzeravatar
GoKi
Beiträge: 2068
Registriert: 04.07.2003 23:08:56
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von GoKi » 08.12.2007 11:39:53

Du kannst den Operator auch in der Klasse selbst als Methode definieren. Oder außerhalb, im Beispiel der operator<<, der auch nicht zwingend nen friend sein muss.

Der Code auf nopaste http://nopaste.debianforum.de/7103
MfG GoKi
:wq

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 08.12.2007 11:50:40

Danke für die Antwort, aber soweit reichen meine C++ Kenntnisse noch nicht, dass ich das wirklich verstehe, was du da gemacht hast.

(Lese noch das Buch C++ von Jürgen Wolf und bin zur Zeit mitten in Kapitel 4)

"Programiere/Schreibe" zur Zeit alles im vi.
Womit schreibst du C++-Programme?
Oh, yeah!

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 08.12.2007 11:53:51

erstmal war der Operator "Bruch operator*( Bruch b1, Bruch b2 )" nicht deklariert, daß hast du jezt durch "friend..." nachgeholt. Das Schlüsselwort "friend" brauchst du hier allerdings überhaupt nicht.

In bruch.h hast du den Operator als Member von der Klasse Bruch definiert:
"Bruch operator*( Bruch b1, Bruch b2 );"
nachdem der Multiplikations-Operator jedoch ein Binary-Operator ( also mit zwei Operanden ) ist, hättest du hier jedoch 3 Operanden ( b1, b2 und die aktuelle Instanz von Bruch ( this ) ).
Zusammenfassung den Operator deklarierst du entweder zur Klasse Bruch, also z.B
"Bruch Bruch::operator*( const Bruch& b)" oder außerhalb als "Bruch operator* ( const Bruch& b1, const Bruch& b2)"

Dann noch zu dieser Zeile "sum=operator*( b1, b2);"
Wenn du Operator überladest, dann kannst du diese auch so verwenden "sum= b1 * b2". ( Warum heißt die Variable "sum", wenn damit ein Produkt gemeint ist ? )

Gruß
gms

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 08.12.2007 12:01:50

gms hat geschrieben: ( Warum heißt die Variable "sum", wenn damit ein Produkt gemeint ist ? )
:lol: ...da hast du natürlich Recht. Ist wohl ein wenig ungünstig gewählt :wink:
gms hat geschrieben: Zusammenfassung den Operator deklarierst du entweder zur Klasse Bruch, also z.B
"Bruch Bruch::operator*( const Bruch& b)" oder außerhalb als "Bruch operator* ( const Bruch& b1, const Bruch& b2)"
Sorry, aber dass verstehe ich wieder nicht so ganz.
Warum sollte ich const (für einen konstanten, nicht veränderbaren Wert) nehmen und wieso muss ich über die Speicheradressen der Objekte (Bruch& b1, Bruch& b2) gehen?
Oh, yeah!

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 08.12.2007 12:11:21

Duff hat geschrieben:Sorry, aber dass verstehe ich wieder nicht so ganz.
Warum sollte ich const (für einen konstanten, nicht veränderbaren Wert) nehmen und wieso muss ich über die Speicheradressen der Objekte (Bruch& b1, Bruch& b2) gehen?
für die Input Parameter dieses Operators solltest du const wählen, weil diese Parameter von der Multiplikation nicht verändert werden ( das ist allerdings keine Mußbedingung !)
Die Speicheradressen belegen weniger Platz am Stack, daher ist es besser hier Referenzen zu verwenden ( Ist auch keine Mußbedingung )
wenn du dein Programm trotzdem entsprechend anpassen möchtest, müßtest du die Funktionen "get_zahler" und "get_nenner" auch als const definieren
gms hat geschrieben:Zusammenfassung den Operator deklarierst du entweder zur Klasse Bruch, also z.B
"Bruch Bruch::operator*( const Bruch& b)" oder außerhalb als "Bruch operator* ( const Bruch& b1, const Bruch& b2)"
Ob du hier "const" oder Referenzen verwendest, war mir eher unwichtig, wichtig ist folgendes:
Wenn du den Operator innerhalb der Klasse definierst, dann ist der erste Operand die Instanz der Klasse, daher brauchst du für die Multiplikation nur noch einen zusätzlichen Operanden bzw Parameter.
Wenn du den Operator außerhalb der Klasse definierst, dann brauchst du natürlich zwei Operanden bzw Paramenter

Gruß
gms

Benutzeravatar
GoKi
Beiträge: 2068
Registriert: 04.07.2003 23:08:56
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von GoKi » 08.12.2007 12:14:34

Duff hat geschrieben:Warum sollte ich const (für einen konstanten, nicht veränderbaren Wert) nehmen und wieso muss ich über die Speicheradressen der Objekte (Bruch& b1, Bruch& b2) gehen?
Das Stichwort für die Benutzung von const lautet const correctness, siehe z.B.
http://parashift.com/c++-faq-lite/const ... tness.html
Wie von gms angesprochen, habe ich daher die getter Methoden als const definiert.

Bruch& - also eine Referenz - ist in diesem Falle auch kein Muss. Dadurch arbeitet die Funktion direkt auf dem übergebenen Objekt und nicht auf einer Kopie. Zur kompletten Verwirrung hier auch ein Link auf die FAQ
http://parashift.com/c++-faq-lite/references.html
Oder auch auf nen deutsches C++ Buch
Referenzen und Parameterübergabe
MfG GoKi
:wq

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 08.12.2007 12:32:08

Ok, nochmals Danke für die Hinweise.

Habe das zwar schon mal gelesen zu call by value und call by reference, aber dass ganze dann wieder in der Praxis an eigenen Beispielen anzuwenden ist doch was anderes.

Nochmals vielen Danke für die guten Erklärungen!!!

Was mir bisher auch noch nicht so wirklich Verständlich ist, sind die Methoden, die einmal mit void anfangen und die als Parameter (void) haben.
Oh, yeah!

Benutzeravatar
armin
Beiträge: 2682
Registriert: 17.03.2005 11:49:14

Beitrag von armin » 08.12.2007 12:35:47

Methoden die mit void Anfangen geben einfach keinen Wert zurück.
Ist der Parameter (void) heißt das, dass einfach keine Parameter übergeben werden. Darf man das heutzutage überhaupt noch benutzen? Eigentlich lässt man das einfach weg.
Es gibt dann auch noch void*, aber dass ist dann wieder eine andere Geschichte...
Formerly known as Trigger.
HP 8510p - Debian Sid
Mitglied des Debian-KDE-Teams

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 08.12.2007 13:07:41

Trigger. hat geschrieben:Darf man das heutzutage überhaupt noch benutzen?
Ich lasse das auch immer weg, es würde aber auch nichts dagegen sprechen diesen zu verwenden:
http://www.kuzbass.ru:8086/docs/isocpp/decl.html#dcl.fct.def hat geschrieben: The parameter list (void) is equivalent to the empty parameter list. Except for this special case, void shall not be a parameter type (though types derived from void, such as void*, can).
Gruß
gms

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 08.12.2007 13:50:56

Danke für die kurze, aber gute und verständliche Erklärung.

Mit welchem Editor oder IDE schreibt ihr denn C++ Programme?
Oh, yeah!

Benutzeravatar
armin
Beiträge: 2682
Registriert: 17.03.2005 11:49:14

Beitrag von armin » 08.12.2007 13:53:12

Wenn ich nur mal schnell eine Datei bearbeiten will vim, für größere Projekte bevorzuge ich dann aber doch Eclipse mit dem CDT-Plugin.
Formerly known as Trigger.
HP 8510p - Debian Sid
Mitglied des Debian-KDE-Teams

Benutzeravatar
TRex
Moderator
Beiträge: 8343
Registriert: 23.11.2006 12:23:54
Wohnort: KA

Beitrag von TRex » 08.12.2007 13:59:05

geany, das ist für mich der beste Mittelweg zwischen spartanischer Ausstattung (mousepad), haarsträubender Eingewöhnungszeit (vi) und schwerem Geschütz (Eclipse).
Jesus saves. Buddha does incremental backups.
Windows ist doof, Linux funktioniert nichtDon't break debian!Wie man widerspricht

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 08.12.2007 14:19:57

Ok, danke.
Oh, yeah!

Antworten