Kopierkonstruktor deaktivieren

172

Ich habe ein klasse :

class SymbolIndexer {
protected:
  SymbolIndexer ( ) { }

public:
  static inline SymbolIndexer & GetUniqueInstance ( ) 
  { 
    static SymbolIndexer uniqueinstance_ ;
    return uniqueinstance_ ; 
  }
};

Wie soll ich es ändern, um Code wie folgt zu deaktivieren:

SymbolIndexer symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

und erlauben Sie nur Code wie:

SymbolIndexer & ref_symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );
Bescheidener Debugger
quelle
1
Übrigens, ist dies ein Singleton mit Bestimmungen für die Vererbung (geschützt)?
R. Martinho Fernandes
Ich habe Zweifel, dass Ihr Code jedes Mal erstellt wird, wenn eine andere Instanz erstellt wird. Ich denke, GetUniqueInstance () verweist immer auf dasselbe Objekt.
Pratham Shah

Antworten:

285

Sie können den Kopierkonstruktor privat machen und keine Implementierung bereitstellen:

private:
    SymbolIndexer(const SymbolIndexer&);

Oder verbieten Sie es in C ++ 11 ausdrücklich:

SymbolIndexer(const SymbolIndexer&) = delete;
R. Martinho Fernandes
quelle
43
In Bezug auf das deleteSchlüsselwort möchte ich Folgendes hinzufügen. Meine derzeitige Gewohnheit beim Entwerfen einer neuen Klasse besteht darin, deletesofort sowohl den Kopierkonstruktor als auch den Zuweisungsoperator zu verwenden. Ich habe festgestellt, dass sie je nach Kontext meistens unnötig sind und das Löschen einige Fälle von unerwartetem Verhalten verhindert. Wenn eine Situation auftritt, in der möglicherweise ein Kopiercode benötigt wird, bestimmen Sie, ob dies mit der Verschiebungssemantik möglich ist. Wenn dies unerwünscht ist, stellen Sie eine Implementierung sowohl für (!) Den Kopierer als auch für den Zuweisungsoperator bereit. Ob dies ein guter Ansatz ist, überlasse ich dem Leser.
Pauluss86
1
@ pauluss86 Ich mag Ihren Ansatz, aber ich würde mich nicht voll darauf festlegen, da ich denke, dass die Zeit, die Sie damit verbringen, diesem Muster zu folgen, größer ist als die Zeit, die durch die Fehler gespart wird, die dadurch verhindert werden. Ich verbiete einfach das Kopieren, wenn ich nicht sicher bin.
Tomáš Zato - Wiedereinsetzung Monica
@ pauluss86 Dies ist im Grunde das, was Rust tut: Standardmäßig verschieben (und standardmäßig const). Sehr hilfreich meiner Meinung nach.
Kapichu
33

Wenn Ihnen die Mehrfachvererbung nichts ausmacht (es ist schließlich nicht so schlimm), können Sie eine einfache Klasse mit einem Konstruktor für private Kopien und einem Zuweisungsoperator schreiben und diese zusätzlich unterordnen:

class NonAssignable {
private:
    NonAssignable(NonAssignable const&);
    NonAssignable& operator=(NonAssignable const&);
public:
    NonAssignable() {}
};

class SymbolIndexer: public Indexer, public NonAssignable {
};

Für GCC gibt dies die folgende Fehlermeldung aus:

test.h: In copy constructor ‘SymbolIndexer::SymbolIndexer(const SymbolIndexer&)’:
test.h: error: ‘NonAssignable::NonAssignable(const NonAssignable&)’ is private

Ich bin mir jedoch nicht sicher, ob dies in jedem Compiler funktioniert. Da ist ein verwandte Frage , aber noch keine Antwort.

UPD:

In C ++ 11 können Sie die NonAssignableKlasse auch wie folgt schreiben :

class NonAssignable {
public:
    NonAssignable(NonAssignable const&) = delete;
    NonAssignable& operator=(NonAssignable const&) = delete;
    NonAssignable() {}
};

Das deleteSchlüsselwort verhindert, dass Mitglieder standardmäßig erstellt werden, sodass sie in den standardmäßig erstellten Elementen einer abgeleiteten Klasse nicht weiter verwendet werden können. Der Versuch, eine Zuweisung vorzunehmen, führt in GCC zu folgendem Fehler:

test.cpp: error: use of deleted function
          ‘SymbolIndexer& SymbolIndexer::operator=(const SymbolIndexer&)’
test.cpp: note: ‘SymbolIndexer& SymbolIndexer::operator=(const SymbolIndexer&)’
          is implicitly deleted because the default definition would
          be ill-formed:

UPD:

Boost hat bereits eine Klasse nur für den gleichen Zweck, ich denke, sie ist sogar auf ähnliche Weise implementiert. Die Klasse wird aufgerufen boost::noncopyableund soll wie folgt verwendet werden:

#include <boost/core/noncopyable.hpp>

class SymbolIndexer: public Indexer, private boost::noncopyable {
};

Ich würde empfehlen, an der Boost-Lösung festzuhalten, wenn Ihre Projektrichtlinie dies zulässt. Weitere Informationen finden Sie auch in einer anderen boost::noncopyableFrage .

firegurafiku
quelle
Sollte das nicht sein NonAssignable(const NonAssignable &other);?
Troyseph
Ich denke, diese Frage würde viel mehr positive Stimmen bekommen, wenn sie auf die C ++ 11- deleteSchlüsselwortsyntax aktualisiert würde .
Tomáš Zato - Wiedereinsetzung Monica
@ TomášZato: Die Idee ist, den Kopierkonstruktor und den Zuweisungsoperator präsent, aber privat zu halten. Wenn Sie deletesie, funktioniert es nicht mehr (ich habe gerade überprüft).
Firegurafiku
@ TomášZato: Ah, sorry, meine Testmethode war etwas falsch. Das Löschen funktioniert auch. Aktualisiert die Antwort in einer Minute.
Firegurafiku
3
@Troyseph: const Class&und Class const&sind ziemlich gleich. Für Zeiger können Sie sogar Class const * consteingeben.
Firegurafiku
4

Machen Sie SymbolIndexer( const SymbolIndexer& )privat. Wenn Sie einer Referenz zuweisen, kopieren Sie nicht.

Aaron Klotz
quelle