C++ mehrer Klassen, die gegenseitig aufeinander zeigen

Gast #4680769
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
(Firma: Torrox.de) #4680807
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.
Gast #4680812
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
#4680826
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;}
Gast #4680828
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.
Gast #4680851
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
Gast #4680931
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
}
Gast #4680970
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).
Gast #4681078
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.
#4681272
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. ;-)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren