Speichern einer Liste beliebiger Objekte in C ++

7

In Java können Sie eine Liste von Objekten haben. Sie können Objekte verschiedener Typen hinzufügen, sie dann abrufen, ihren Typ prüfen und die entsprechende Aktion für diesen Typ ausführen.
Zum Beispiel: (Entschuldigung, wenn der Code nicht genau stimmt, gehe ich aus dem Speicher)

%Vor%

Was ist der beste Weg, um dieses Verhalten in C ++ zu replizieren?

    
Whatsit 03.03.2009, 18:01
quelle

13 Antworten

14

Ihr Beispiel mit Boost.Variant und einem Besucher:

%Vor%

Eine gute Sache bei der Verwendung dieser Technik ist, dass, wenn Sie später einen anderen Typ zu der Variante hinzufügen und Sie vergessen, einen Besucher zu ändern, um diesen Typ einzuschließen, wird er nicht kompiliert. Sie müssen jeden möglichen Fall unterstützen. Wenn Sie jedoch einen Schalter verwenden oder if-Anweisungen kaskadieren, ist es leicht zu vergessen, die Änderung überall vorzunehmen und einen Fehler einzuführen.

    
Ferruccio 03.03.2009, 18:51
quelle
20

boost::variant ist ähnlich dem Vorschlag von boost::any , unterstützt jedoch das Visitor-Muster, dh es ist einfacher, später typspezifischen Code hinzuzufügen. Außerdem werden die Werte auf dem Stack zugewiesen, anstatt die dynamische Zuordnung zu verwenden, was zu etwas effizienterem Code führt.

BEARBEITEN: Wie litb in den Kommentaren darauf hinweist, bedeutet die Verwendung von variant anstelle von any , dass Sie nur Werte aus einer vordefinierten Typenliste speichern können. Dies ist oft eine Stärke, obwohl es im Fall des Fragestellers eine Schwäche sein könnte.

Hier ist ein Beispiel (ohne das Besuchermuster zu verwenden):

%Vor%

(Und ja, Sie sollten technisch vector<T>::size_type anstelle von int für i verwenden, und Sie sollten stattdessen vector<T>::iterator verwenden, aber ich versuche es einfach zu halten.)

    
j_random_hacker 03.03.2009 18:09
quelle
13

C ++ unterstützt keine heterogenen Container.

Wenn Sie boost nicht verwenden wollen, müssen Sie eine Dummy-Klasse erstellen und alle anderen Klassen von dieser Dummy-Klasse ableiten. Erstellen Sie einen Container Ihrer Wahl, um Dummy-Klassenobjekte zu halten, und Sie können loslegen.

%Vor%

Wenn Sie boost verwenden möchten, können Sie boost::any . Hier ist ein Beispiel für die Verwendung von boost::any .

Sie finden diesen ausgezeichneten Artikel von zwei führenden C ++ Experten, die von Interesse sind.

Nun, boost::variant ist eine andere Sache, auf die Sie achten sollten, da j_random_hacker erwähnt wird. Also, hier ist ein Vergleich um einen zu bekommen eine gute Idee, was zu verwenden ist.

Mit einem boost::variant würde der obige Code ungefähr so ​​aussehen:

%Vor%     
dirkgently 03.03.2009 18:04
quelle
12

Wie oft ist diese Art von Dingen wirklich nützlich? Ich programmiere schon seit einigen Jahren in C ++, in verschiedenen Projekten und wollte eigentlich nie einen heterogenen Container. Es kann in Java aus irgendeinem Grund üblich sein (ich habe viel weniger Java-Erfahrung), aber für jede gegebene Verwendung in einem Java-Projekt könnte es eine Möglichkeit geben, etwas anderes zu tun, das besser in C ++ funktioniert.

C ++ hat eine schwerere Betonung der Typensicherheit als Java und dies ist sehr typunsicher.

Wenn die Objekte nichts gemeinsam haben, warum speichern Sie sie zusammen?

Wenn sie Dinge gemeinsam haben, können Sie eine Klasse erstellen, von der sie erben; Verwenden Sie alternativ boost :: any. Wenn sie erben, virtuelle Funktionen aufrufen oder dynamic_cast & lt; & gt; wenn du wirklich musst.

    
David Thornley 03.03.2009 18:13
quelle
3

Ich möchte nur darauf hinweisen, dass die Verwendung von dynamischem Typ-Casting, um nach Typ zu verzweigen, oft auf Fehler in der Architektur hinweist. In den meisten Fällen können Sie mit virtuellen Funktionen denselben Effekt erzielen:

%Vor%     
SmoCoder 05.03.2009 10:26
quelle
2

Leider gibt es keine einfache Möglichkeit, dies in C ++ zu tun. Sie müssen selbst eine Basisklasse anlegen und alle anderen Klassen aus dieser Klasse ableiten. Erstellen Sie einen Vektor von Basisklassenzeigern und verwenden Sie dann dynamic_cast (was mit einem eigenen Laufzeitaufwand verbunden ist), um den tatsächlichen Typ zu finden.

    
Naveen 03.03.2009 18:08
quelle
2

Nur zur Vollständigkeit dieses Themas möchte ich erwähnen, dass Sie dies tatsächlich mit reinem C tun können, indem Sie void * verwenden und es dann in alles umwandeln, was es sein muss (ok, mein Beispiel ist kein reines C, da es Vektoren verwendet aber das spart mir etwas Code). Dies funktioniert, wenn Sie wissen, welchen Typ Ihre Objekte haben oder ob Sie ein Feld irgendwo speichern, das sich daran erinnert. Das wollen Sie sicher NICHT, aber hier ist ein Beispiel, um zu zeigen, dass es möglich ist:

%Vor%     
naumcho 03.03.2009 19:32
quelle
2

Obwohl Sie primitive Typen nicht in Containern speichern können, können Sie Wrapper-Klassen vom primitiven Typ erstellen, die den autoboxierten primitiven Typen von Java ähneln (in Ihrem Beispiel werden die primitiven typisierten Literale tatsächlich autoboxiert); Beispiele davon erscheinen in C ++ - Code (und können (fast) verwendet werden) wie primitive Variablen / Datenelemente.

Siehe Objekt-Wrapper für die integrierten Typen von Datenstrukturen und Algorithmen mit objektorientierten Entwurfsmustern in C ++ .

Mit dem eingepackten Objekt können Sie den Operator c ++ typeid () verwenden, um den Typ zu vergleichen. Ich bin mir ziemlich sicher, dass der folgende Vergleich funktioniert: if (typeid(o) == typeid(Int)) [wobei Int die eingepackte Klasse für den primitiven Typ int usw. wäre ...] (Ansonsten fügen Sie Ihren primitiven Wrappern einfach eine Funktion hinzu, die eine Typid zurückgibt und somit: if (o.get_typeid() == typeid(Int)) ...

In Bezug auf Ihr Beispiel hat das Code-Geruch für mich. Es sei denn, dies ist der einzige Ort, wo Sie den Typ des Objekts überprüfen, Ich würde geneigt sein, Polymorphie (vor allem wenn Sie andere Methoden / Funktionen in Bezug auf Typ haben) zu verwenden. In diesem Fall würde ich die primitiven Wrapper verwenden, indem ich eine Interface-Klasse hinzufüge, in der die deferred-Methode deklariert wird (um 'do stuff' zu machen), die von jeder der umgebrochenen primitiven Klassen implementiert wird. Damit wären Sie in der Lage, Ihren Container-Iterator zu verwenden und Ihre if-Anweisung zu eliminieren (wenn Sie nur diesen einen Vergleich des Typs haben, wäre die Einrichtung der verzögerten Methode mit Polymorphismus nur zu diesem Zweck ein Overkill).

    
Roger Nelson 03.03.2009 23:59
quelle
1

Ich bin ziemlich unerfahren, aber hier würde ich mitgehen -

  1. Erstellen Sie eine Basisklasse für alle Klassen, die Sie bearbeiten müssen.
  2. Schreiben Sie Containerklasse / Wiederverwendungscontainerklasse. (Überarbeitet, nachdem ich andere Antworten gesehen habe - Mein vorheriger Punkt war zu kryptisch.)
  3. Schreiben Sie einen ähnlichen Code.

Ich bin mir sicher, dass eine viel bessere Lösung möglich ist. Ich bin mir auch sicher, dass eine bessere Erklärung möglich ist. Ich habe gelernt, dass ich einige schlechte C ++ Programmiergewohnheiten habe, also habe ich versucht, meine Idee zu vermitteln, ohne in Code zu kommen.

Ich hoffe, das hilft.

    
batbrat 03.03.2009 18:06
quelle
1

Neben der Tatsache, wie die meisten darauf hingewiesen haben, können Sie das nicht tun, oder, was noch wichtiger ist, mehr als wahrscheinlich, wollen Sie wirklich nicht.

Lassen Sie uns Ihr Beispiel abtun und betrachten Sie etwas, das näher an einem realen Beispiel liegt. Genauer gesagt sah ich Code in einem echten Open-Source-Projekt. Es wurde versucht, eine CPU in einem Zeichenarray zu emulieren. Daher würde es in das Array einen 1-Byte- "Op-Code" einfügen, gefolgt von 0, 1 oder 2 Bytes, die basierend auf dem Op-Code ein Zeichen, eine Ganzzahl oder ein Zeiger auf eine Zeichenkette sein könnten. Um damit umgehen zu können, war viel Kleingeld nötig.

Meine einfache Lösung: 4 separate Stacks: Eins für die "Opcode" -Enumerierung und jeweils eine für Chars, Ints und String. Nimm den nächsten vom Opcode-Stack, und der würde dir den anderen Operanden nehmen, um den Operanden zu bekommen.

Es besteht eine sehr gute Chance, dass Ihr tatsächliches Problem auf ähnliche Weise behandelt werden kann.

    
James Curran 03.03.2009 18:32
quelle
0

Nun, Sie könnten eine Basisklasse erstellen und dann Klassen erstellen, die davon erben. Speichern Sie sie dann in einem std :: vector.

    
xian 03.03.2009 18:04
quelle
0

Die kurze Antwort ist ... du kannst nicht.

Die lange Antwort lautet ... Sie müssten Ihre eigene neue Hierarchie von Objekten definieren, die alle von einem Basisobjekt erben. In Java fallen schließlich alle Objekte von "Object" ab, was Ihnen erlaubt, dies zu tun.

    
Bryan 03.03.2009 18:11
quelle
0

RTTI (Laufzeittyp Info) in C ++ war schon immer schwierig, besonders Cross-Compiler.

Am besten ist es, STL zu verwenden und eine Schnittstelle zu definieren, um den Objekttyp zu bestimmen:

%Vor%

Mike

    
Mike Marshall 03.03.2009 18:31
quelle

Tags und Links