Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

09.01.2013, 13:52

[C++] Liste einer Basisklasse

Hey Leute!
Ich hoffe, dieses Thema wurde noch nicht allzu oft besprochen (ich habe mithilfe der Suchfunktion nichts brauchbares gefunden.)
Ich habe nämlich folgendes Problem: In meinem aktuellen Projekt wäre es optimal, wenn ich (ähnlich wie in Java) eine Liste einer Basisklasse verwenden könnte, die aber mit verschiedenen Child-Klassen gefüllt ist.
Zum Verständnis:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
class CObject
{
public:
    int DoSomething() { return 0;} 
};

class CChild: public CObject
{   
int DoSomething() { return 1;};
};

std::list<CObject> objects;


Wenn ich nun CChild-Objekte in die Liste packe, gibt es keine Probleme soweit, aber wenn ich mithilfe eines Iterators DoSomething() ausführen will, erhalte ich stets 0 und nie 1. Gibt es eine Möglichkeit dies in C++ umzusetzen?
Vielen lieben Dank schonmal :)

TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

2

09.01.2013, 13:58

Das Problem ist, dass deine abgeleitete Klasse nicht die methode DoSomething() überschreibt. Stichwort: Polymorphie, virtual :)

3

09.01.2013, 14:05

Du meinst, dass ich in CObject

C-/C++-Quelltext

1
virtual int DoSomething() {return 0;};

schreiben soll? Wenn ja, dann habe ich mich auch schon damit befasst, was aber leider keine Lösung des Problems ergab :/

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

4

09.01.2013, 14:22

Schlag dein C++ Buch doch einfach mal unter Vererbung und Polymorphie auf. Damit hast du doch schon 2 gute Stichworte. Wenn es bei dir nicht funktioniert hast du irgendwas bei der Vererbung falsch verstanden. Da hilft es dir ja weiter wenn du die Kapitel dazu noch mal ließt und das gelernte möglichst direkt testest. Dann siehst du ob es so funktioniert wie du dir vorstellst, oder doch anders läuft. Wenn du dann dabei konkrete Fragen hast kannst du sie ja hier stellen. Aber sowas wie "Ich verstehe Polymorphie nicht" ist keine wirklich konkrete Frage bei der man dir ordentlich helfen kann.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

5

09.01.2013, 14:27

Doch, das ist ein Teil der Lösung. Der andere Teil ist die Erkenntnis, dass in C++ Objekte auch per Kopie herumgereicht werden, während bei Java alles eine Referenz ist. (Vereinfacht ausgedrückt, bitte nicht korrigieren).

Was Du hier tust:

Quellcode

1
2
3
std::list<Object> objects;  
Child myLittleChild;
objects.push_back( myLittleChild);


Du speicherst in der Liste ein richtiges Objekt, in dem Fall ist es eine Kopie der übergebenen Instanz myLittleChild. Da myLittleChild eine Instanz der Ableitung ist, die Liste aber nur Instanzen der Basisklasse speichern kann, wird nur der Basisklassen-Teil der übergebenen Instanz kopiert. Was Du hier brauchst, sind Referenzen. Bzw. Zeiger, da Referenzen in C++ semantisch anders sind als die in Java - man kann sie nicht in einen Container packen.

Quellcode

1
2
3
std::list<Object*> objects;
Child* myLittleChild = new Child;
objects.push_back( myLittleChild);


Wenn Du jetzt durch objects durchgehst und eine virtuelle Funktion der Basisklasse aufrufst, wird korrekt je nach tatsächlichem Typ die richtige Funktion aufgerufen.

Quellcode

1
2
for( auto obj : objects )  
  obj->DoSomething(); 


... müsste 0 oder 1 ergeben, je nach Typ der hinterlegten Instanz.

Warnung hier: Du musst im Gegensatz zu Java die Instanzen wieder mit delete abräumen, wenn Du sie mit new erzeugt hast. Oder Du nutzt std::shared_ptr zur automatischen Verwaltung des Speichers, was in begrenztem Maße ok ist.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

NachoMan

Community-Fossil

Beiträge: 3 885

Wohnort: Berlin

Beruf: (Nachhilfe)Lehrer (Mathematik, C++, Java, C#)

  • Private Nachricht senden

6

09.01.2013, 14:39

Warnung hier: Du musst im Gegensatz zu Java die Instanzen wieder mit delete abräumen, wenn Du sie mit new erzeugt hast. Oder Du nutzt std::shared_ptr zur automatischen Verwaltung des Speichers, was in begrenztem Maße ok ist.

In dem Fall würde std::unique_ptr wahrscheinlich ausreichen.
"Der erste Trunk aus dem Becher der Erkenntnis macht einem zum Atheist, doch auf dem Grund des Bechers wartet Gott." - Werner Heisenberg
Biete Privatunterricht in Berlin und Online.
Kommt jemand mit Nach oMan?

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

7

09.01.2013, 17:24

Und damit dir so ein Fehler beim nächsten Mal sofort auffällt: Gib deiner Basisklasse einen privaten Kopierkonstruktor und Zuweisungsoperator (Deklaration alleine reicht in der Regel, außer natürlich die beiden werden in der Implementierung der Basisklasse irgendwo benötigt):

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Base
{
private:
  Base(const Base&);
  Base& operator =(const Base&);
public:
  ...
};

class Derived : public Base
{
  ...
};


std::vector<Base>;  // Erzeugt nun einen Compilerfehler

Dann kompiliert sowas gleich gar nicht mehr...

8

10.01.2013, 14:51

Hey :)
Ah, den Trick mit unique_ptr (bzw. shared_ptr) kannte ich noch nicht.
Danke Leute, mit

C-/C++-Quelltext

1
list<unique_ptr<Child>> objects;

läuft nun alles wie gewünscht. Mir war nicht bewusst, was genau intern passiert, wenn ich nur ein "richtiges" Objekt wie in

C-/C++-Quelltext

1
2
3
std::list<Object> objects;  
Child myLittleChild;
objects.push_back( myLittleChild);

verwende.
Danke für eure Hilfe und Denkanstöße!

NachoMan

Community-Fossil

Beiträge: 3 885

Wohnort: Berlin

Beruf: (Nachhilfe)Lehrer (Mathematik, C++, Java, C#)

  • Private Nachricht senden

9

10.01.2013, 17:53

Das ist kein Trick sondern gängige Praxis.
Du solltest ein Leerzeichen zwischen den beiden spitzen Klammern schreiben, sonst könnte es bei einigen kompilern Probleme geben und vermeide "using namespace".
Es sollte nachher also so auschauen:

C-/C++-Quelltext

1
std::list<std::unique_ptr<Child> > objects;
"Der erste Trunk aus dem Becher der Erkenntnis macht einem zum Atheist, doch auf dem Grund des Bechers wartet Gott." - Werner Heisenberg
Biete Privatunterricht in Berlin und Online.
Kommt jemand mit Nach oMan?

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

10

10.01.2013, 17:59

Du solltest ein Leerzeichen zwischen den beiden spitzen Klammern schreiben, sonst könnte es bei einigen kompilern Probleme geben [...]

Naja, std::unique_ptr setzt C++11 voraus und in C++11 gibt das keine Probleme... ;)

Werbeanzeige