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 ( );

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;

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 .


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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.