Wie verbietet man die Konstruktion eines Objekts?

8

Wie kann ich die Konstruktion eines Objekts verbieten? Ich markiere = delete; alle relevanten Sonderfunktionen wie folgt:

%Vor%

LIVE-BEISPIEL

Aber x , y und *z können noch existieren. Was ist zu tun? Ich bin an beiden Fällen interessiert; statische / Stapelzuweisung und Heap-Zuweisung.

    
Orient 15.10.2015, 10:28
quelle

5 Antworten

14

Eine Option wäre, der Klasse eine rein virtuelle Funktion zu geben und sie als endgültig zu markieren:

%Vor%

[Live-Beispiel]

    
Angew 15.10.2015, 10:39
quelle
6

Wenn Sie die Klasse nicht instanziieren möchten, können Sie einfach private Konstruktoren deklarieren:

%Vor%

Und% NotInstantiable nicht weiter definieren. Dies kann jetzt nicht instanziiert werden, da der Konstruktor zuerst private ist, aber auch keine Definition für den Konstruktor angegeben wurde.

Das zweite Hindernis, um die NotInstantiable zu instantiieren, würde zum Beispiel diese Möglichkeit verbieten, die ansonsten ein bekanntes Muster ist:

%Vor%     
skyking 15.10.2015 10:39
quelle
5
  1. Wenn Sie nur static Mitglieder haben möchten, dann schreiben Sie namespace A anstatt struct A . Der nachfolgende Code ist syntaktisch ähnlich.

  2. Um die Erstellung einer Instanz einer Klasse zu verhindern, sollten Sie sie abstrakt machen. (Fügen Sie eine reine virtuelle Funktion ein). Aber dadurch wird eine v-Tabelle in Ihre Klasse eingeführt, die Sie vielleicht nicht möchten.

Bathsheba 15.10.2015 10:37
quelle
3

Um die Client-Code-Instantiierung einer Klasse vollständig zu verhindern, können Sie im Allgemeinen die Klasse final und entweder

deklarieren
  • Machen Sie die Konstruktoren nicht public oder

  • Löschen Sie die Konstruktoren und stellen Sie sicher, dass die Klasse kein Aggregat ist oder

  • Fügen Sie eine reine virtuelle Elementfunktion hinzu (z. B. machen Sie den Destruktor rein virtuell), um die Klasse abstrakt zu machen.

Die Deklaration der Klasse final ist erforderlich, wenn die nicht public protected und die abstrakte Klasse ist, um die Instanziierung eines Basisklassenunterobjekts einer abgeleiteten Klasse zu verhindern.

Um die Instanziierung teilweise zu verhindern, können Sie

verwenden
  • mache den Destruktor nicht public .

Dies verhindert automatische und statische Variablen, verhindert jedoch nicht die dynamische Zuweisung mit new .

  • macht die Zuordnungsfunktion der Klasse ( operator new ) nicht public .

Dies verhindert die dynamische Zuweisung über einen normalen new -Ausdruck im Client-Code, bietet jedoch keine automatischen und statischen Variablen oder Unterobjekte anderer Objekte und verhindert nicht die dynamische Zuweisung über ::new - Ausdruck, der die globale Zuordnungsfunktion verwendet.

Es gibt auch andere relevante Techniken, wie eine Zuordnungsfunktion mit zusätzlichen Argumenten, die new -Ausdrücke übermäßig kompliziert und unpraktisch machen. Ich habe das einmal verwendet, um die Verwendung eines speziellen Makros zu erzwingen, um Objekte, z. für eine shared-from-this-Klasse . Aber das war in der Zeit vor C ++ 11 Unterstützung für die Weiterleitung von Argumenten; Heutzutage kann eine gewöhnliche Funktion die Aufgabe übernehmen, und eine solche Funktion kann als friend der Klasse erstellt werden.

Die Tatsache, dass der Code mit mindestens einer Version des Clang-Compilers kompiliert mit -std=gnu++1z , ist auf einen Fehler und / oder eine Spracherweiterung in diesem Compiler zurückzuführen.

Der Code sollte nicht kompiliert werden, da er den Standardkonstruktor aufruft, der gelöscht wurde. Und es kompiliert nicht mit z.B. MinGW g ++ 5.1.0, auch mit -std=gnu++1z .

Die Tatsache, dass der Code mit mindestens einer Version des Clang-Compilers kompiliert wird mit -std=gnu++1z , kann aufgrund eines Fehlers und / oder einer Spracherweiterung in diesem Compiler sein. Was das richtige Verhalten ist, ist unklar, weil

  • Obwohl der Code mit clang und mit Visual C ++ 2015 kompiliert wird, kompiliert er nicht mit z. MinGW g ++ 5.1.0, sogar mit -std=gnu++1z .

  • Intuitiv wäre die delete bedeutungslos, wenn der Code kompiliert werden müsste, aber viele sinnlose Konstrukte sind in C ++ erlaubt.

  • Es besteht die Frage, ob die Klasse ein Aggregat ist (in diesem Fall führt der new -Ausdruck eine Aggregat-Initialisierung durch), die davon abhängt, ob der gelöschte Standardkonstruktor als vom Benutzer bereitgestellt Und als Benutzer TartanLlama , der in Kommentaren erläutert wird, sind die Anforderungen für vom Benutzer bereitgestellte

C ++ 11 § 8.4.2 / 4
  

" Eine spezielle Memberfunktion ist vom Benutzer bereitgestellt , wenn sie vom Benutzer deklariert wurde und nicht explizit   bei seiner ersten Deklaration voreingestellt oder gelöscht.

i.e. Obwohl der delete des Standardkonstruktors im Beispiel dieser Frage diesen Konstruktor deklariert, wird er nicht vom Benutzer bereitgestellt (und dito für die anderen Mitglieder), und daher ist die Klasse ein Aggregat.

Der einzige Fehlerbericht, den ich über diese Formulierung finden kann, ist DR 1355 , die jedoch nur ein Problem mit der Verwendung der Wörter "besonderes Mitglied" betrifft, und schlägt vor, diese Wörter fallen zu lassen. Aber in Anbetracht der Wirkung dieser Frage und der Tatsache, dass eine Funktion nur in der ersten Deklaration gelöscht werden kann, ist der Wortlaut seltsam.

Zusammenfassend, formell ab C ++ 11 (Ich habe C ++ 14 nicht überprüft), sollte der Code kompilieren. Dies kann jedoch ein Fehler in der Norm sein, wobei die Formulierung die Absicht nicht widerspiegelt. Und da MinGW g ++ 5.1.0 den Code nicht kompiliert, ist es ab Oktober 2015 nicht sinnvoll, sich auf die Code-Kompilierung zu verlassen.

    
Cheers and hth. - Alf 15.10.2015 11:11
quelle
1

Im Wesentlichen wird dies kompiliert und ist erlaubt, weil der Typ A ein Aggregattyp ist und die Aggregat-Initialisierung keine Standardkonstruktoren verwendet.

Was ist ein Aggregattyp? ;

  

Klassentyp (normalerweise struct oder union), der

hat      
  • keine privaten oder geschützten Mitglieder
  •   
  • keine vom Benutzer bereitgestellten Konstruktoren (explizit vordefinierte oder gelöschte Konstruktoren sind erlaubt) (seit C ++ 11)
  •   
  • keine Basisklassen
  •   
  • keine virtuellen Mitgliedsfunktionen
  •   

Wenn Sie eine der oben genannten Angaben angeben, würde dies zu einer Nicht-Aggregation führen, sodass die aggregierte Initialisierung nicht angewendet werden kann. Geben Sie es einen privaten Benutzer definierten (und nicht implementiert) Konstruktor tun.

%Vor%

Als eine Randnotiz; Ich hoffe, das ist ein Fehler in den Sprachspezifikationen. Auf den ersten Blick dachte ich, dass das nicht kompilieren sollte, aber es tut es. Eine der Motivationen für =delete bestand darin, den C ++ 03-Trick zu vermeiden, die Konstruktoren zu deklarieren, um sie "zu verstecken" und somit unbrauchbar zu sein. Ich würde erwarten, dass ein =delete im Standardkonstruktor die Erstellung von Klassen effektiv verbietet (außerhalb anderer benutzerdefinierter Konstruktoren).

Zum leichteren Lesen und klarerer Absicht sollten Sie sogar eine leere Basisklasse in Betracht ziehen;

%Vor%

Vielleicht ist es am einfachsten, hier zum C ++ 03-Stil zurückzukehren und den Standardkonstruktor privat zu machen;

%Vor%     
Niall 15.10.2015 12:27
quelle