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

NachoMan

Community-Fossil

Beiträge: 3 885

Wohnort: Berlin

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

  • Private Nachricht senden

21

22.08.2012, 21:20

Lern lieber die Werkzeuge, die dir zur Verfügung stehen, richtig zu nutzen, anstatt Argumente dafür zu suchen es falsch zu machen. Das ist nur auf dem ersten Blick mehr Arbeit ;)
"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?

22

22.08.2012, 21:26

Es ging ja nicht um die Arbeit, sondern um die Übersichtlichkeit und Wartbarkeit... ^^
Aber ja, ich habe verstanden, Meister. :P

CodingCat

1x Contest-Sieger

Beiträge: 420

Beruf: Student (KIT)

  • Private Nachricht senden

23

22.08.2012, 21:41

@CodingCat
Danke. Ich werde mir das ganze mal in ruhe genauer anschauen. :)

Tatsächlich war es ein wenig dumm von mir, diese Lösung zu posten. Zwar löst sie dein eingangs beschriebenes Problem auf umfassende und absolut generische Weise, schlussendlich ist dein eingangs beschriebenes Problem jedoch mit großer Sicherheit selbst nur die Folge von Unverständnis des eigentlichen Problemkontexts, in dem du diese Listen zu nutzen gedenkst. Schon das Bedürfnis, Objekte nach Typ zu trennen, ist praktisch immer Ausdruck schlechten Designs. Denn woimmer du Objekte in Listen aggregierst, müssen diese mit großer Sicherheit auch gemeinsame Eigenschaften aufweisen, um eine bestimmte Operation oder eine klar abgegrenzte Menge von Operationen auf diesen Objekten ausführen zu können. Diese Operationen lassen sich problemlos in einer Interface-Klasse deklarieren, von der dann die Klassen aller Objekte, auf denen diese Operationen ausgeführt werden sollen, erben.

Am Ende hast du eine einzige Liste von Interface-Zeigern auf Objekte, und du musst nichts weiter tun als über diese zu iterieren, um mit einem einzigen virtuellen Aufruf die richtige Operation auszuführen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Interface
{
   virtual void someCommonOperation() = 0;
};

struct MyCollection
{
   typedef std::list<Interface*> list_type;
   list_type list;

   void add(Interface *o) { list.push_back(o); }
   void remove(Interface *o) { list.remove(o); }

   void someCommonOperation()
   {
      for(list_type::iterator it = list.begin(); it != list.end(); ++it)
         (*it)->someCommonOperation();
   }
};
alphanew.net (last updated 2011-06-26) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite

drakon

Supermoderator

Beiträge: 6 513

Wohnort: Schweiz

Beruf: Entrepreneur

  • Private Nachricht senden

24

22.08.2012, 23:50

@drakon
... Mhh... Ich finde das Argument unbefriedigend, wenn ich jetzt stur wäre, würde ich immer noch darauf beharren das dass
Erstere besser aussieht und besser wartbar ist, außerdem ist der kurze Blick in die Logfile welcher das selbe Ergebnis wie der Blick auf
die Compiler Ausgaben bringt, auch wenn das vielleicht wirklich ein paar Sekunden länger dauert, hinnehmbar.

RTTI ist meistens kaum eine gute Idee, weil es eben oftmals aufgrund schlechten Designs benutzt wird. Wartbarkeit hat auch etwas mit Testing zu tun und wenn der Code bereits nicht kompiliert wenn etwas nicht korrekt benutzt wird, dann ist das eigentlich der beste Fehlerfall, den man haben kann. Sobald etwas zur Laufzeit analysiert werden muss ist das mit grossen Kosten verbunden. Nicht vergessen darfst du auch, dass nicht garantiert ist, dass der Fehler zur Laufzeit gefunden wird. Wenn die Ausführung da nicht hingelangt bekommst du auch keine Fehlermeldung. Der Compiler entdeckt den Fehler immer.

Du darfst dabei auch nicht nur an dein kleines Beispiel denken, sondern musst beachten, dass bei grossen Projekten die Module, die geändert wurden neu getestet werden müssen. Das heisst, dass deine Version alle Fälle mitgetestet werden müssen. Auch wenn nur Code von einem anderen Typen geändert wurde. Das ist wirklich nicht erwünscht, wenn man bedenkt, dass Testing aufwändig ist.

Das Overhead Argument wurde auch bereits genannt.

Was die Übersicht betrifft magst du recht haben, dass in dem Fall alles beieinander ist. Aber eben ein solche Typerkennung weisst auf schlechtes Design hin, da ja prinzipiell das gleiche gemacht werden muss. Da ist eben der Vorschlag von CodingCat das, was du eigentlich brauchst.

25

23.08.2012, 16:12

@drakon
Ok, du hast Recht, ich werde dann wohl bei solchen Problemen öfters auf dynamic binding setzen.

@CodingCat
Viel Dank, durch den Code ist bei mir der letzte Groschen gefallen, ich hab jetzt den Sinn hinter
dynamic binding gefunden und das Problem gelöst. Allerdings hätte ich noch zwei Fragen zu deinem Code:

Hat es einen höheren Sinn das "MyCollection" eine Struktur ist und nicht eine Klasse?

Und auf die Gefahr mich zu blamieren, aber wieso sind die Klammern bei "(*it)" nötig, damit
das ganze funktioniert? Was signalisieren die Klammern an dem Punkt genau? :whistling:

CodingCat

1x Contest-Sieger

Beiträge: 420

Beruf: Student (KIT)

  • Private Nachricht senden

26

23.08.2012, 16:37

Dass es eine Struktur ist, liegt nur daran, dass das Beispiel so einfach wie möglich sein sollte. Beim Interface war ich auch etwas nachlässig; so wie es dasteht, ist die darin deklarierte Methode private, was für ein Interface wenig Sinn ergibt. Die Klammern sind notwendig, weil -> stärker bindet als *. Ohne Klammern würde der Compiler also zuerst it->someCommonOperation() auswerten und dann die Dereferenzierung mit dem *. Dies macht jedoch keinen Sinn, weil it-> ein Doppelzeiger (Typ Interface**) wäre, mit dem wir someCommonOperation() nicht ohne Dereferenzierung ausführen könnten. Da die std::list Interface-Zeiger vom Typ Interface* enthält, liefert uns *it genau solche Zeiger, mit denen wir im Anschluss problemlos someCommonOperation() aufrufen können. Deshalb die Klammern: Zuerst Iterator dereferenzieren, um das Listenelement zu erhalten, dann mit dem Listenelement, also in diesem Fall dem Interface-Zeiger, die Methode aufrufen.
alphanew.net (last updated 2011-06-26) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite

27

23.08.2012, 16:43

Vielen Dank. Jetzt habe ich auch das verstanden! :)

28

23.08.2012, 17:15



C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
size_t allocateTypeID()
{
   // In multi-threaded Umgebungen müsste hier synchronisiert werden
   static size_t nextTypeID = 0;
   return nextTypeID++;
}

template <class T>
inline size_t getTypeID()
{
   // Vor C++11 müsste hier atomar auf Initialisierung geprüft werden
   static const size_t typeID = allocateTypeID();
   return typeID;
}
};


Dazu hätte ich jetzt auch mal eine Frage. Es ist klar das allocateTypeID() bei jedem Aufruf eine "neue" Zahl zurück gibt aber erzeugt der Compiler für jeden Typ der als Template übergeben wird eine eigene getTypeID() Funktion so das typeID immer das gleiche zurück gibt wenn man den entsprechenden Typ verwendet? Ich habe eigentlich gedacht das der Compiler das etwas generischer macht so das alle Template Typen noch im gleichen Scope agieren.
greate minds discuss ideas;
average minds discuss events;
small minds discuss people.

drakon

Supermoderator

Beiträge: 6 513

Wohnort: Schweiz

Beruf: Entrepreneur

  • Private Nachricht senden

29

23.08.2012, 17:39

Statische Variablen werden genau 1x für eine Klasse/Funktion initialisiert. Und da mit verschiedenen Typen verschiedene Funktionen generiert werden sind sie auch immer verschieden. Die generierten Klassen haben dann eigentlich nichts mehr miteinander zu tun. Das verhalten ist das gleiche, wie wenn du die Funktionen selbst von Hand hinschreibst.

Werbeanzeige