2012-01-29 11 views
11

Da quello che ho capito io sono in grado di copiare "disable" e assegnando ai miei oggetti definendo costruttore di copia privata e operatore di assegnazione:Impedire l'assegnazione e passando per valore

class MyClass 
{ 
private: 
    MyClass(const MyClass& srcMyClass); 
    MyClass& operator=(const MyClass& srcMyClass); 
} 

Ma qual è l'utilizzo di questo?
È considerata una cattiva pratica?

Sarei grato se potessi descrivere la situazione, in cui sarebbe ragionevole/utile "disabilitare" l'assegnazione e copiare il costruttore in questo modo.

+1

un singoletto sarebbe un esempio. –

risposta

11

È utile quando non ha senso che l'oggetto venga copiato. Non è assolutamente considerato una cattiva pratica.

Ad esempio, se si dispone di una classe che rappresenta una connessione di rete, non è significativo copiare tale oggetto. Un'altra volta che potresti desiderare che una classe sia non calcolabile è se avessi una classe che rappresenta un giocatore in una partita multiplayer. Entrambe queste classi rappresentano cose che non possono essere copiate nel mondo reale o che non hanno senso copiare (una persona, una connessione).

Inoltre, se si sta tentando di implementare un Singleton, è la procedura standard per rendere gli oggetti non copiabili.

+0

Grazie, ha senso. Che dire dell'operatore di assegnazione? – LihO

+3

@LihO generalmente se ne disabiliti uno disabilita l'altro. Se non si disabilita l'operatore di assegnazione, è possibile farlo: 'MyClass a, b; a = b; 'e se non si disabilita il costruttore di copie si può fare' MyClass b; MyClass a (b); 'Quindi se non disabiliti entrambi, puoi aggirare l'altro disabilitato. –

+0

Sì, ora ho capito. – LihO

0

Quando si sta tentando di implementare un modello singleton, è perfettamente accettabile utilizzare un costruttore privato poiché è pensato per essere istanziato solo all'interno di se stesso e da nessun'altra parte. Una volta richiamato, il costruttore non può essere revocato. Pertanto, il costruttore viene richiamato solo dopo aver verificato se la condizione di singleton è soddisfatta.

2

È una pratica piuttosto comune. Ci sono molti esempi in cui copiare non è appropriato.

Supponiamo che l'oggetto rappresenti un socket lato server aperto (ovvero una connessione di rete in ingresso); quale sarebbe la semantica di fare una copia di quell'oggetto?

5

In generale, qualsiasi classe che gestisce una risorsa deve essere non copiabile o avere semantica della copia specializzata. Il contrario è vero: qualsiasi classe che non è copiabile o che ha bisogno di semantica della copia specializzata sta gestendo una risorsa. "Gestire una risorsa" nella lingua C++ significa in pratica un certo spazio in memoria, o una connessione a una rete o un database, un handle a un file, una transazione di annullamento e così via.

La gestione delle risorse cattura un bel po 'di esempi. Queste sono le responsabilità che richiedono un'operazione di prefisso, un'operazione di suffisso e, eventualmente, un'azione nel mezzo. La gestione della memoria, ad esempio, implica l'acquisizione di un handle per un indirizzo di memoria che gestiremo, magari con la memoria, e infine rilasciare l'handle (perché se ami qualcosa, lascia che sia libero).

template<typename T> 
struct memory { 
    memory(T const& val = T()) : p(new T(val)) { } 
    ~memory() { delete p } 
    T& operator*() const { return *p; } 
private: 
    T* p; 
}; 

// ... 
{ 
    memory<int> m0; 
    *m0 = 3; 
    std::cout << *m0 << '\n'; 
} 

Questo memory classe è quasi corretto: acquisisce automaticamente lo spazio di memoria sottostante e rilascia automaticamente, anche se un'eccezione si propaga qualche tempo dopo ha acquisito la sua risorsa. Ma considerare questo scenario:

{ 
    memory<double> m1(3.14); 
    memory<double> m2(m1); // m2.p == m1.p (do you hear the bomb ticking?) 
} 

perché non abbiamo a disposizione la semantica di copia specializzati per memory, il compilatore fornisce un proprio costruttore di copia e copiare l'assegnazione. Questi fanno la cosa errata: significa m2.p = m1.p, tale che i due puntatori puntano allo stesso indirizzo.È sbagliato perché quando lo m2 esce dal campo di applicazione libera la sua risorsa come un buon oggetto responsabile, e quando lo m1 esce dallo scope libera anche la sua risorsa, la stessa risorsa m2 è già stata liberata, completando una doppia eliminazione - un famigerato scenario di comportamento non definito. Inoltre, in C++ è estremamente facile creare copie di un oggetto senza nemmeno accorgersene: una funzione prende il suo parametro in base al valore, restituisce il suo parametro in base al valore o prende il suo parametro per riferimento, ma chiama un'altra funzione che prende (o restituisce) stessa parametro per valore ... È più semplice presumere che le cose corrispondano a per essere copiati.

Tutto questo per dire che quando una raison d'être di classe sta gestendo una risorsa, immediatamente si deve sapere che è necessario gestire la copia. Si dovrebbe decidere

  • sostenete la copia, mentre si decide cosa copiare significa: condivisione sicura delle risorse, l'esecuzione di una copia completa della risorsa sottostante quindi non c'è condivisione di sorta, o combinando i due approcci come in copy-on-write o copia pigra. Qualunque percorso tu scelga, dovrai fornire un costruttore di copie specializzato e un operatore di assegnazione copie.
  • o non si supporta alcun tipo di copia della risorsa, nel qual caso si disabilita il costruttore di copie e l'operatore di assegnazione copia.

Mi piacerebbe andare così lontano e dire che la gestione delle risorse è l'unico caso in cui si disabilita la copia o fornire semantica della copia specializzata. Questa è solo un'altra prospettiva su The Rule of Three.

0

quando è consentito creare istanza di oggetto solo dopo aver controllato come nel caso di singleton u sono necessari costruttori privati. quando viene chiamato il costruttore viene chiamata l'istanza dell'oggetto e quindi non ha senso verificare se esiste già un'altra istanza. quindi quello che facciamo è chiamare una funzione membro di classe dalla funzione principale e all'interno di quella membro verificare se un'altra istanza è già presente nella memoria. se non viene chiamato il costruttore. altro abortito. controlla le classi singleton o altre classi protette in cui i dati dell'oggetto devono essere mantenuti protetti e non dovrebbero essere autorizzati a copiare.

controllare anche questo: Singleton Class in C++

Problemi correlati