Wyłącz konstruktora kopiującego

173

Mam klasę :

class SymbolIndexer {
protected:
  SymbolIndexer ( ) { }

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

Jak mam go zmodyfikować, aby wyłączyć kod, taki jak:

SymbolIndexer symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

i zezwalaj tylko na kod taki jak:

SymbolIndexer & ref_symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );
Humble Debugger
źródło
1
Przy okazji, czy jest to singleton z przepisami dotyczącymi dziedziczenia (chroniony)?
R. Martinho Fernandes
Mam wątpliwości co do tego, że za każdym razem, gdy zostanie utworzona inna instancja, w Twoim kodzie. Myślę, że GetUniqueInstance () zawsze poda odniesienie do tego samego obiektu.
Pratham Shah

Odpowiedzi:

286

Możesz ustawić konstruktor kopiujący jako prywatny i nie zapewniać żadnej implementacji:

private:
    SymbolIndexer(const SymbolIndexer&);

Lub w C ++ 11, wyraźnie tego zabraniaj:

SymbolIndexer(const SymbolIndexer&) = delete;
R. Martinho Fernandes
źródło
43
Jeśli chodzi o deletesłowo kluczowe, chciałbym dodać, co następuje. Moim obecnym nawykiem podczas projektowania nowej klasy jest natychmiastowe działanie deletezarówno konstruktora kopiującego, jak i operatora przypisania. Odkryłem, że w zależności od kontekstu są one przeważnie niepotrzebne, a ich usunięcie zapobiega niektórym przypadkom nieoczekiwanego zachowania. Jeśli wystąpi sytuacja, w której może być potrzebny procesor kopiowania, określ, czy można to zrobić za pomocą semantyki przenoszenia. Jeśli jest to niepożądane, należy zapewnić implementację zarówno dla (!) Elementu kopiującego, jak i operatora przypisania. Czy to dobre podejście, zostawię czytelnikowi.
pauluss86
1
@ pauluss86 Podoba mi się twoje podejście, ale nie zobowiązałbym się do tego w pełni, ponieważ myślę, że czas spędzony na śledzeniu tego wzorca jest większy niż czas zaoszczędzony przez błędy, którym zapobiega. Po prostu zabraniam kopiowania, gdy nie jestem pewien.
Tomáš Zato - Przywróć Monikę
@ pauluss86 To jest w zasadzie to, co robi Rust: Domyślne przesuwanie (i domyślnie stała). Moim zdaniem bardzo pomocne.
Kapichu
33

Jeśli nie przeszkadza ci wielokrotne dziedziczenie (w końcu nie jest tak źle), możesz napisać prostą klasę z prywatnym konstruktorem kopiującym i operatorem przypisania i dodatkowo podklasować ją:

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

class SymbolIndexer: public Indexer, public NonAssignable {
};

W przypadku GCC daje to następujący komunikat o błędzie:

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

Nie jestem jednak pewien, czy zadziała to w każdym kompilatorze. Jest pytanie pokrewne , ale nie ma jeszcze odpowiedzi.

UPD:

W C ++ 11 możesz również napisać NonAssignableklasę w następujący sposób:

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

W deleteZapobiega słów kluczowych członków z domyślnym jest skonstruowany tak, że nie mogą być stosowane również w domyślnych skonstruowane członków klasy pochodnej jest. Próba przypisania powoduje następujący błąd w GCC:

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 ma już klasę tylko do tego samego celu, myślę, że jest nawet zaimplementowany w podobny sposób. Klasa jest wywoływana boost::noncopyablei ma być używana w następujący sposób:

#include <boost/core/noncopyable.hpp>

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

Poleciłbym trzymać się rozwiązania Boost, jeśli pozwala na to polityka projektu. Aby uzyskać więcej informacji, zobacz także inne boost::noncopyablepytanie powiązane .

firegurafiku
źródło
Nie powinno tak być NonAssignable(const NonAssignable &other);?
Troyseph
Myślę, że to pytanie zyskałoby znacznie więcej głosów, gdyby zostało zaktualizowane do deleteskładni słowa kluczowego C ++ 11 .
Tomáš Zato - Przywróć Monikę
@ TomášZato: Chodzi o to, aby konstruktor kopiujący i operator przypisania były obecne, ale prywatne. Jeśli deleteje wy , przestaje działać (właśnie sprawdziłem).
firegurafiku
@ TomášZato: Ach, przepraszam, moja metoda testowania była trochę zła. Usuwanie też działa. Zaktualizuję odpowiedź za minutę.
firegurafiku
3
@Troyseph: const Class&i Class const&są prawie takie same. W przypadku wskaźników możesz mieć nawet Class const * consttyp.
firegurafiku
4

Ustaw jako SymbolIndexer( const SymbolIndexer& )prywatne. Jeśli przypisujesz odwołanie, nie kopiujesz.

Aaron Klotz
źródło