Ist es möglich, zwischen Konstruktoren in main zu wählen, ohne einen Kopierkonstruktor zu schreiben?

8

Wirkliches Beispiel ist offensichtlich viel länger, aber das fasst mein Problem zusammen:

%Vor%

Dann in main:

%Vor%

Hoppla! Das wird nicht funktionieren, die Instanz ist für das folgende Programm nicht verfügbar.

%Vor%

Aber jetzt laufe ich in Schwierigkeiten, weil ich keinen Kopierkonstruktor definiert habe. Ich würde wirklich lieber keinen Kopierkonstruktor schreiben, weil meine eigentliche Klasse Dutzende von Mitgliedern hat, von denen viele nicht-grundlegende Typen sind und möglicherweise noch mehr Arbeit zum Kopieren benötigen.

Genauer gesagt, bekomme ich

%Vor%     
mmdanziger 18.03.2014, 12:52
quelle

9 Antworten

6

Die universelle Möglichkeit, mit nicht kopierbaren Objekten umzugehen, besteht darin, sie in ein unique_ptr (oder auto_ptr, abhängig von Ihrem Compiler) zu werfen.

%Vor%

Die Verwendung von rohen Zeigern ist hier wirklich nicht sicher, denn sobald Sie anfangen, mit Ausnahmen oder interessanten Codepfaden umzugehen, wird es mühsam, sich über Lecks Gedanken zu machen. Vertrauen Sie mir, In 100% der Fälle werden Sie weniger Arbeit und Codezeilen haben, wenn Sie sie einfach in ein unique_ptr einfügen.

Eine optimale Lösung wäre, die Konstruktoren von Object neu zu gestalten, da das Objekt in einem unzulässigen Zustand verbleiben kann, wenn die Nichtkopierbarkeit umgangen wird. Im Allgemeinen möchten Sie die Nichtkopierbarkeit erhalten, wenn der Compiler dies für notwendig hält. Wir haben hier nicht die Details, um eine solche Lösung zu konkretisieren.

    
QuestionC 18.03.2014, 14:41
quelle
5

Wenn Sie keine dynamische Zuweisung wünschen, können Sie eine Initialize-Funktion verwenden:

%Vor%

Dann können Sie initialisieren, um den Typ auszuwählen.

%Vor%     
Lucian 18.03.2014 13:01
quelle
3

Hier ist eine Low-Level (ish) -Lösung, die das macht, was Sie wollen. Ich werde es Ihnen überlassen zu entscheiden, ob es eine gute Idee ist, es zu benutzen:

%Vor%

Kopier- / Verschiebeoperationen für die obigen werden als Übung für den Leser belassen ;-)

Die Klasse würde dann wie folgt verwendet:

%Vor%

Neben der Umwandlung in T können Sie auch die Klasse operator* und operator-> angeben, die das enthaltene Objekt zurückgibt, ähnlich wie bei boost::optional tut es.

    
Angew 18.03.2014 13:16
quelle
3

Es gibt ein paar Optionen, mit denen Sie dies tun können, während Sie den automatischen Speicher beibehalten, und welches Sie verwenden sollten, hängt von der Semantik des Typs Object ab.

Der POD

Wenn Sie einen Typ wie den in der Frage angegebenen haben, können Sie ihn auf einen POD-Typ reduzieren; Entfernen Sie im Prinzip alle vom Benutzer bereitgestellten Konstruktoren und weisen Sie allen denselben Zugriffsspezifizierer zu:

%Vor%

Dann könnte Ihr Initialisierungsmuster so aussehen (mit Platzierung neu ):

%Vor%

Allgemeine Typen

Im Allgemeinen funktioniert Ihr typischer wert-semantischer Typ perfekt, wenn Sie ihn initialisieren und dann dank der Umzugs-Semantik zuweisen:

%Vor%

Häufig werden Sie jedoch weiterhin im Standardkonstruktor arbeiten, da die standardkonstruierten Objekte bestimmter Typen (z. B. std::vector ) einen wohldefinierten Status haben. Wenn Sie diese Arbeit für einen beliebigen vordefinierten Typ vermeiden möchten, können Sie std::optional verwenden (zum jetzigen Zeitpunkt noch nicht wirklich Standard):

%Vor%

Ohne std::optional

Sie könnten einwenden, dass std::optional mit zu viel Aufwand verbunden ist, und ich überlasse es Ihnen und Ihren Messungen, um zu entscheiden, ob das der Fall ist. Auf jeden Fall können wir unser Verhalten bekommen, ohne uns um den Overhead kümmern zu müssen - aber vielleicht haben die Nasendämonen Gnade, wenn du deine Initialisierung nicht tatsächlich ausführst. Wir verwenden union , um zu bekommen, was wir wollen:

%Vor%

Dies könnte verbessert werden. Zum Beispiel können wir den Speicher zu einer Vorlage machen:

%Vor%

Wir können uns selbst eine Referenz geben:

%Vor%

Und mit ein wenig Liebe zum Detail, um Namenskollisionen zu vermeiden und die interessante Deklarationssyntax von C ++ zu handhaben, könnten wir sogar ein paar Makros definieren, um den Code zu bereinigen (Implementierung als Übung für den Leser):

%Vor%     
Stuart Olsen 18.03.2014 20:16
quelle
2

Sie können einen Zeiger auf Ihr Objekt verwenden und es durch einen neuen Operator instanziieren:

%Vor%     
Nejat 18.03.2014 12:56
quelle
2

Sie verwenden etwas, das elision genannt wird.

Dies besagt, dass der Compiler MAY den Code optimieren und in diesem Fall einen Kopierkonstruktor vermeiden sollte. Aber es muss nicht, und kann sowieso einen Kopierkonstruktor verwenden.

Der richtige Weg (ohne den Launen des Compilers ausgesetzt zu sein) wäre, einen Zeiger zu verwenden:

%Vor%     
Yochai Timmer 18.03.2014 12:57
quelle
1

In Ihrer Version des Ein-Parameter-Konstruktors wird das mInt2 -Member einfach ignoriert (es wird nie initialisiert), also nehme ich an, dass Sie keine Berechnungen mit diesem Member durchführen, wenn type1 ist false (obwohl ich nicht weiß, wie Sie es tun, ohne type1 zu speichern).

Also, warum nicht einfach das Dessign ändern? Lassen Sie den Konstruktor int param1 , int param2 und type1 als Parameter verwenden und wählen Sie intern aus, wie er sich selbst erstellt:

%Vor%

Dann in main :

%Vor%

Ich schätze, dass es ein wenig besser aussieht.

    
Paula_plus_plus 18.03.2014 15:55
quelle
1

Sie könnten eine Zugaufgabe schreiben. Abhängig davon, wie Ihre Datenmitglieder aussehen, können Sie damit davonkommen, einige oder alle von ihnen zu speichern, vgl. Konstruktor mit memcpy verschieben .

Ich nehme an, dass Sie entweder einen vollständigen Satz von Konstruktoren / Destruktoren benötigen, einschließlich Kopie und Zuweisung; das wird immer notwendig sein, wenn du es in Containern haben willst, es zuweisen etc. Oder sonst benötigt die Klasse nichts davon und du initialisierst nur die benötigten Teile davon abhängig von der Situation, und wenn du fertig bist, du manuell initialisieren.

    
Peter A. Schneider 18.03.2014 13:27
quelle
0

Ich denke, ich habe einen Weg gefunden, zu tun, ohne das Objekt auf dem Heap zuzuweisen , indem ich ein Lambda verwende. Grundsätzlich besteht die Idee darin, die Konstruktion von der Nutzung zu trennen. (Im Folgenden wird davon ausgegangen, dass Sie die Definition von Object so festgelegt haben, dass es kompiliert wird)

%Vor%

Hier war keine Kopie beteiligt, aber der gleiche Code wird immer angewendet, unabhängig davon, wie das Objekt konstruiert wurde und ohne eine externe Funktion deklarieren zu müssen (da Lambda eine lokale Funktion ist). Aus der Sicht des Speichers wird es immer nur ein Instanzobjekt geben, so dass der zugewiesene Stapelspeicher unabhängig vom Pfad immer die gleiche Größe hat.

Offensichtlich funktioniert das nicht, wenn die Instanz den Rahmen der gesamten Funktion verlassen muss. Wenn es der Fall ist, dann müssen Sie es wirklich auf dem Haufen mit einem intelligenten Zeiger vorzugsweise zuweisen.

    
Klaim 22.03.2014 00:14
quelle