Forum: PC-Programmierung C++ mehrer Klassen, die gegenseitig aufeinander zeigen


von Markus_Bauer (Gast)


Lesenswert?

Hallo,
ich bin bei C++ absoluter Anfänger, also bitte nicht hauen für die dumme 
Frage...

Zur Übung soll ich ein Programm "Autovermietung" programmieren. Es gibt 
eine Klasse "Kunde" und eine Klasse "Fahrzeug", jeweils als doppelt 
verkettete Liste. Die Objekte der Klasse "Kunde" enthalten Zeiger vom 
Typ "Fahrzeug", der auf das gemietete Fahrzeug zeigt, wenn der Kunde 
eines gemietet hat. Die Objekte der Klasse "Fahrzeug" enthalten Zeiger 
vom Typ "Kunde", der auf den Kunden zeigt, der das Fahrzeug gemietet 
hat.

Ich habe die Klassendefinitionen und das Hauptprogramm in eine einzige 
Datei geschrieben. Der Compiler ist Dev C++ "Bloodshed", weil der 
Lehrstuhl den empfohlen hat.

Beim Kompilieren kriege ich Fehlermeldungen: Wenn die Kunden-Klasse vor 
der Fahrzeug-Klasse in der Datei kommt, kann der Compiler nichts mit dem 
Zeiger auf das Fahrzeug-Objekt anfangen, wenn die Fahrzeug-Klasse vor 
der Kunden-Klasse kommt, kann der Compiler nichts mit dem Zeiger auf das 
Kunden-Objekt anfangen.

Wenn ich erst Kunden-Klasse und dann Fahrzeug-Klasse definiere, und am 
Anfang der Datei, vor der Kunden-Klasse einfach deklariere:

class Fahrzeug{};

dann kriege ich eine Fehlermeldung "redefinition of class Fahrzeug".

Dürfen Klassen in C++ einfach nicht gegenseitig aufeinander zeigen? Oder 
gibt es einen Trick, wie man sowas kompilieren kann?

Danke, Philipp

von Jemand (Gast)


Lesenswert?

Du musst die geschweiften Klammern weglassen.
class x; ist eine Deklaration
class x{}; ist eine Definition

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Markus_Bauer schrieb:
> Ich habe die Klassendefinitionen und das Hauptprogramm in eine einzige
> Datei geschrieben. Der Compiler ist Dev C++ "Bloodshed", weil der
> Lehrstuhl den empfohlen hat.

das ist eine IDE, der Compiler wird wahrscheinlich der GCC sein.

> Dürfen Klassen in C++ einfach nicht gegenseitig aufeinander zeigen? Oder
> gibt es einen Trick, wie man sowas kompilieren kann?

dürfen sie. Genau, wie man bei Funktionen zwischen Deklaration und 
Definition unterscheidet, kann man das auch bei Klassen:
1
class Fahrzeug();

ohne {} wäre eine Deklaration (auch manchmal "forward declaration" 
genannt). Die reicht, damit Du den Typen im Zusammenhang mit Zeigern und 
Referenzen verwenden kannst.

mfg Torsten

p.s.: Auch wenn ihr es in der Schule lernt: verkette Listen schreibt 
keiner von Hand.

von Markus_Bauer (Gast)


Lesenswert?

Sorry, ich habe Unsinn geschrieben.
Der Compiler meckert erst, wenn ich in der Kunden-Klasse eine 
public-Methode der Fahrzeugklasse aufrufen will. Z.B. wenn ich in einer 
Methode der Kunden-Klasse eine publi-Methode der Fahrzeugklasse aufrufen 
will. Konkret will ich Kundendaten ausgeben und dabei den Typ des 
gemieteten Fahrezeugs ausgeben. Ich habe also einen Zeiger auf das 
gemietete Fahrzeug und möchte dann eine public-Methode des Fahrzeugs 
aufrufen, z.B mit

gemietet->getTyp()

"gemietet" ist der Name des Zeigers, "getTyp" der Name der 
public-Methode des Fahrzeug-Objekts. Sie soll einen string zurückgeben. 
Ich kriege leider den Compiler-Fehler "getTyp has not been declared".

Wie kann ich die Methode deklariere, ohne dass ich die 
"redefinition"-Fehlermeldung kriege?

Danke

von Daniel A. (daniel-a)


Lesenswert?

Teile den Code einfach richtig auf, Beispiel, ungetestet:
1
// a.hpp
2
class b; // b als klasse deklarieren
3
class a { // klasse a definieren
4
  private:
5
    b* v; // member definieren
6
  public:
7
    void setv(b*); // memberfunktion declarieren
8
}
9
10
// b.hpp
11
class a; // b als klasse deklarieren
12
class b { // klasse a definieren
13
  private:
14
    a* v; // member definieren
15
  public:
16
    void setv(a*); // memberfunktion declarieren
17
}
18
19
// a.cpp
20
#include<a.hpp>
21
#include<b.hpp>
22
void a::setv(b*x){v=x;}
23
24
// b.cpp
25
#include<b.hpp>
26
#include<a.hpp>
27
void b::setv(a*x){v=x;}

von Tom (Gast)


Lesenswert?

Um einen Zeiger auf etwas zu deklarieren, muss der Compiler nur wissen, 
dass es etwas gibt (forward declaration): class Fahrzeug;

So geht es nicht:
1
class Fahrzeug{}; // hiermit deklariere ich die Klasse Fahrzeug, die nichts enthält.
2
3
class  Garage
4
{
5
    Fahrzeug* bla;
6
};
7
8
class Fahrzeug  // Hiermit deklariere ich wieder die Klasse Fahrzeug, die 
9
{               // diesmal einen Garagen-Zeiger enthält. => Widerspruch.
10
    Garage* g;
11
};



So geht es:
1
class Baum; // Hallo Compiler, es gibt eine Klasse "Baum", Details folgen später
2
3
class Apfel
4
{
5
public:
6
    Apfel(Baum* b) : mein_baum(b)
7
    {
8
    }
9
10
    Baum* mein_baum; // Zeiger ist Zeiger, der Compiler muss dazu nicht wissen, 
11
                     // wie Baum intern aufgebaut ist, nur, dass die Klasse existiert
12
};
13
14
class Baum
15
{
16
public:
17
    Baum()
18
    {
19
    }
20
21
    Apfel* meine_frucht;
22
23
    void wachse()
24
    {
25
        meine_frucht = new Apfel(this);
26
    }
27
};

Torsten R. schrieb:
> Auch wenn ihr es in der Schule lernt: verkette Listen schreibt
> keiner von Hand.
Das sollte man aber trotzdem können.

von Tom (Gast)


Lesenswert?

Ersetze "deklariere ich die Klasse" jeweils durch "definiere ich die 
Klasse".

von Markus_Bauer (Gast)


Lesenswert?

Tom schrieb:
> Um einen Zeiger auf etwas zu deklarieren, muss der Compiler nur wissen,
> dass es etwas gibt (forward declaration): class Fahrzeug;

Das funktioniert, danke. Sieht so aus:

<c>
class Fahrzeug;

class Kunde
{
private:
Fahrzeug* gemietet;
....
public:
void ausgabe()
{cout << gemietet->getTyp();}    //hier kommt der Compilerfehler
...
};

class Fahrzeug
{
private:
Kunde* vermietet;
string typ;
...
public:
string getTyp()
{return typ;}
....};
</c>

Wahrscheinlich muss ich die Methode "getTyp" vor der Klasse "Kunde" 
irgendwie deklarieren, oder?

Vielen Dank euch allen

von Rolf Magnus (Gast)


Lesenswert?

Markus_Bauer schrieb:
> Tom schrieb:
>> Um einen Zeiger auf etwas zu deklarieren, muss der Compiler nur wissen,
>> dass es etwas gibt (forward declaration): class Fahrzeug;
>
> Das funktioniert, danke. Sieht so aus:
>
> <c>
> class Fahrzeug;
>
> class Kunde
> {
> private:
> Fahrzeug* gemietet;
> ....
> public:
> void ausgabe()
> {cout << gemietet->getTyp();}    //hier kommt der Compilerfehler
> ...
> };
>
> class Fahrzeug
> {
> private:
> Kunde* vermietet;
> string typ;
> ...
> public:
> string getTyp()
> {return typ;}
> ....};
> </c>
>
> Wahrscheinlich muss ich die Methode "getTyp" vor der Klasse "Kunde"
> irgendwie deklarieren, oder?

Ja, indem du die Klasse definierst, bevor du getTyp aufrufst. Wie schon 
gesagt wurde, wäre es sinnvoller, das sauber auf mehrere Dateien 
aufzutrennen und nicht alles in eine reinzuwurschteln.
1
class Fahrzeug;
2
3
class Kunde
4
{
5
private:
6
  Fahrzeug* gemietet;
7
  ....
8
public:
9
  void ausgabe();
10
  ...
11
};
12
 
13
class Fahrzeug
14
{
15
private:
16
    Kunde* vermietet;
17
    string typ;
18
    ...
19
public:
20
    string getTyp();
21
    {
22
        return typ;
23
    }
24
};
25
26
void Kunde::ausgabe()
27
{
28
    cout << gemietet->getTyp();
29
}

von Sebastian (Gast)


Lesenswert?

Wie werden Objekte deiner Klassen erzeugt und wieder freigegeben?

Wenn du so weit bist, dass das ganze compiliert, könnte das mit dem 
Freigeben zu einem Problem werden.
Weil, wenn die gegenseitig aufeinander zeigen, und du gibst ein Objekt 
frei, dann hat das andere Zeiger, die ins Leere zeigen. Wenn du nicht 
aufpasst. Ist nicht schlimm, du darfst diese Zeiger dann aber nicht mehr 
dereferenzieren (das Objekt, auf das sie mal gezeigt haben, benutzen).

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Sebastian schrieb:
> Wie werden Objekte deiner Klassen erzeugt und wieder freigegeben?

Würde mich nicht wundern, wenn selbst der Prof dabei Fehler macht ;-)

von guest (Gast)


Lesenswert?

Torsten R. schrieb:
> Würde mich nicht wundern, wenn selbst der Prof dabei Fehler macht ;-)

Mich würde eher wundern, wenn er keine macht :)

von Hans-Georg L. (h-g-l)


Lesenswert?

guest schrieb:
> Torsten R. schrieb:
>> Würde mich nicht wundern, wenn selbst der Prof dabei Fehler macht ;-)
>
> Mich würde eher wundern, wenn er keine macht :)

Und mich würde wundern wenn ein Prof auf die Idee käme das ein Kunde 
nicht nur ein Auto mieten kann :-))

von nicht“Gast„ (Gast)


Lesenswert?

Moin,

Anstatt irgend welche circularen Abhängigkeiten zu erzeugen, solltest du 
lieber eine Verwaltungsklasse erstellen, welche Autos und Miete zusammen 
bringt. Dann braucht das Auto selber nicht zu wissen, an wenn es gerade 
vermietet ist und der Mieter muss es sich auch nicht merken ;)


Bin ja positiv überrascht, das der dev-c++ mal wieder weiter entwickelt 
wird. Wobei 2016 ja offensichtlich nichts gemacht wurde.

von Sheeva P. (sheevaplug)


Lesenswert?

Markus_Bauer schrieb:
> Zur Übung soll ich ein Programm "Autovermietung" programmieren. Es gibt
> eine Klasse "Kunde" und eine Klasse "Fahrzeug", jeweils als doppelt
> verkettete Liste. Die Objekte der Klasse "Kunde" enthalten Zeiger vom
> Typ "Fahrzeug", der auf das gemietete Fahrzeug zeigt, wenn der Kunde
> eines gemietet hat. Die Objekte der Klasse "Fahrzeug" enthalten Zeiger
> vom Typ "Kunde", der auf den Kunden zeigt, der das Fahrzeug gemietet
> hat.

Im realen Leben fehlt hier eine Klasse "Vertrag", die jeweils eine 
Referenz (oder meinetwegen einen Pointer) auf den Kunden und auf das FZ 
beinhaltet. Schließlich kann ein Firmenkunde gleichzeitig mehrere 
Fahrzeuge mieten, und irgendwo sollten wohl auch die Vertragsdetails 
(Mietbeginn und -ende, Preis, inkludierte Kilometer, ...) gespeichert 
werden.

Letztlich können wir Dir bei Deinen Problemen sicher besser helfen, wenn 
Du uns Deinen Quellcode zeigst. ;-)

von Paul B. (paul_baumann)


Lesenswert?

>mehrer Klassen, die gegenseitig aufeinander zeigen

Wirf an einer Schule eine Scheibe ein und brülle: "Wer war das?". Du 
wirst sehen: mehrere Klassen, die gegenseitig aufeinander zeigen
:)
MfG Paul

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.