Disabilita il costruttore di copie


173

Ho una lezione :

class SymbolIndexer {
protected:
  SymbolIndexer ( ) { }

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

Come devo modificarlo per disabilitare il codice come:

SymbolIndexer symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

e consenti solo codice come:

SymbolIndexer & ref_symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

1
A proposito, è questo un singleton con disposizioni per l'eredità (dato protetto)?
R. Martinho Fernandes,

Ho un dubbio nel tuo codice ogni volta che verrà creata un'istanza diversa, penso che GetUniqueInstance () farà sempre riferimento allo stesso oggetto.
Pratham Shah,

Risposte:


286

È possibile rendere privato il costruttore della copia e non fornire alcuna implementazione:

private:
    SymbolIndexer(const SymbolIndexer&);

O in C ++ 11, vietalo esplicitamente:

SymbolIndexer(const SymbolIndexer&) = delete;

43
Per quanto riguarda la deleteparola chiave, vorrei aggiungere quanto segue. La mia abitudine attuale quando si progetta una nuova classe è deleteimmediatamente sia il costruttore di copie che l'operatore di assegnazione. Ho scoperto che, a seconda del contesto, sono per lo più inutili e la loro eliminazione impedisce alcuni casi di comportamento imprevisto. Se si verifica una situazione in cui potrebbe essere necessario un ctor copia, determinare se può essere fatto con la semantica di spostamento. Se ciò non è auspicabile, fornire un'implementazione per entrambi (!) Il copiatrice e l'operatore di assegnazione. Se questo è un buon approccio, lo lascerò al lettore.
pauluss86,

1
@ pauluss86 Mi piace il tuo approccio, ma non mi impegnerei completamente perché penso che il tempo trascorso seguendo questo schema sia maggiore del tempo risparmiato dagli errori che previene. Ho semplicemente vietato la copia ogni volta che non sono sicuro.
Tomáš Zato - Ripristina Monica il

@ pauluss86 Questo è fondamentalmente ciò che Rust fa: Sposta per impostazione predefinita (e const per impostazione predefinita). Molto utile secondo me.
Kapichu,

33

Se non ti dispiace l'ereditarietà multipla (dopotutto non è poi così male), puoi scrivere una classe semplice con il costruttore di copie private e l'operatore di assegnazione e inoltre sottoclassarlo:

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

class SymbolIndexer: public Indexer, public NonAssignable {
};

Per GCC questo dà il seguente messaggio di errore:

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

Non sono molto sicuro che funzioni in ogni compilatore. C'è una domanda correlata , ma ancora senza risposta.

UPD:

In C ++ 11 puoi anche scrivere la NonAssignableclasse come segue:

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

La deleteparola chiave impedisce ai membri di essere predefiniti, quindi non possono essere ulteriormente utilizzati nei membri predefiniti di una classe derivata. Tentare di assegnare genera il seguente errore in 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 ha già una classe solo per lo stesso scopo, immagino sia addirittura implementata in modo simile. La classe viene chiamata boost::noncopyableed è pensata per essere utilizzata come segue:

#include <boost/core/noncopyable.hpp>

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

Ti consiglierei di attenermi alla soluzione di Boost se la tua politica di progetto lo consente. Vedi anche un'altra boost::noncopyabledomanda correlata per ulteriori informazioni.


Non dovrebbe essere NonAssignable(const NonAssignable &other);?
Troyseph,

Penso che questa domanda otterrebbe molti più voti se fosse aggiornata alla deletesintassi delle parole chiave C ++ 11 .
Tomáš Zato - Ripristina Monica il

@ TomášZato: L'idea è di mantenere il costruttore di copie e l'operatore di assegnazione presenti, ma privati. Se deleteli hai , smette di funzionare (ho appena controllato).
Firegurafiku,

@ TomášZato: Ah, scusa, il mio metodo di prova era un po 'sbagliato. Anche l'eliminazione funziona. Aggiornerà la risposta tra un minuto.
Firegurafiku,

3
@Troyseph: const Class&e Class const&sono praticamente uguali. Per i puntatori potresti avere anche il Class const * consttipo.
firegurafiku,

4

Rendi SymbolIndexer( const SymbolIndexer& )privato. Se stai assegnando un riferimento, non stai copiando.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.