Qual è la regola dei tre?


2151
  • Cosa significa copiare un oggetto ?
  • Cosa sono il costruttore di copie e l' operatore di assegnazione delle copie ?
  • Quando devo dichiararli io stesso?
  • Come posso impedire che i miei oggetti vengano copiati?

52
Si prega di leggere l'intero thread e il c++-faqtag wiki prima di votare per chiudere .
sabato

13
@Binary: almeno prenditi il ​​tempo di leggere la discussione sul commento prima di esprimere un voto. Il testo era molto più semplice, ma a Fred fu chiesto di ampliarlo. Inoltre, sebbene siano grammaticamente quattro domande , in realtà è solo una domanda con diversi aspetti. (Se non sei d'accordo, prova il tuo POV rispondendo a ciascuna di queste domande da solo e facendoci votare i risultati.)
sbi

1
Fred, ecco un'interessante aggiunta alla tua risposta riguardante C ++ 1x: stackoverflow.com/questions/4782757/… . Come gestiamo questo?
sabato


4
Tieni presente che, a partire da C ++ 11, penso che questo sia stato aggiornato alla regola del cinque, o qualcosa del genere.
paxdiablo,

Risposte:


1795

introduzione

Il C ++ tratta variabili di tipi definiti dall'utente con semantica di valore . Ciò significa che gli oggetti vengono implicitamente copiati in vari contesti e dovremmo capire cosa significa effettivamente "copiare un oggetto".

Consideriamo un semplice esempio:

class person
{
    std::string name;
    int age;

public:

    person(const std::string& name, int age) : name(name), age(age)
    {
    }
};

int main()
{
    person a("Bjarne Stroustrup", 60);
    person b(a);   // What happens here?
    b = a;         // And here?
}

(Se sei perplesso dalla name(name), age(age)parte, questo si chiama a elenco di inizializzatori del membro .)

Funzioni speciali per i membri

Cosa significa copiare un personoggetto? La mainfunzione mostra due scenari di copia distinti. L'inizializzazione person b(a);viene eseguita dal costruttore della copia . Il suo compito è costruire un nuovo oggetto basato sullo stato di un oggetto esistente. L'assegnazione b = aviene eseguita dall'operatore di assegnazione della copia . Il suo lavoro è generalmente un po 'più complicato, perché l'oggetto target è già in uno stato valido che deve essere affrontato.

Poiché non abbiamo dichiarato né il costruttore di copie né l'operatore di assegnazione (né il distruttore), questi sono implicitamente definiti per noi. Citazione dalla norma:

Il [...] costruttore di copie e l'operatore di assegnazione delle copie, [...] e il distruttore sono funzioni speciali dei membri. [ Nota : l'implementazione dichiarerà implicitamente queste funzioni membro per alcuni tipi di classe quando il programma non le dichiara esplicitamente. L'implementazione li definirà implicitamente se vengono utilizzati. [...] nota finale ] [n3126.pdf sezione 12 §1]

Per impostazione predefinita, copiare un oggetto significa copiare i suoi membri:

Il costruttore di copie definito implicitamente per una classe X non sindacale esegue una copia membro dei suoi oggetti secondari. [n3126.pdf sezione 12.8 §16]

L'operatore di assegnazione copia implicitamente definito per una classe X non sindacale esegue l'assegnazione di copia membro dei suoi oggetti secondari. [n3126.pdf sezione 12.8 §30]

Definizioni implicite

Le funzioni membro speciali implicitamente definite per personassomigliano a questo:

// 1. copy constructor
person(const person& that) : name(that.name), age(that.age)
{
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    name = that.name;
    age = that.age;
    return *this;
}

// 3. destructor
~person()
{
}

La copia a livello di membro è esattamente ciò che vogliamo in questo caso: namee agevengono copiati, quindi otteniamo un personoggetto indipendente e indipendente . Il distruttore definito implicitamente è sempre vuoto. Anche in questo caso va bene, dal momento che non abbiamo acquisito risorse nel costruttore. I distruttori dei membri vengono chiamati implicitamente dopo che il persondistruttore è finito:

Dopo aver eseguito il corpo del distruttore e aver distrutto qualsiasi oggetto automatico allocato all'interno del corpo, un distruttore per la classe X chiama i distruttori per i membri diretti [...] di X [n3126.pdf 12.4 §6]

Gestire le risorse

Quindi, quando dovremmo dichiarare esplicitamente quelle funzioni di membro speciale? Quando la nostra classe gestisce una risorsa , cioè quando un oggetto della classe è responsabile di quella risorsa. Questo di solito significa che la risorsa viene acquisita nel costruttore (o passata nel costruttore) e rilasciata nel distruttore.

Torniamo indietro nel tempo al C ++ pre-standard. Non c'era niente di simile std::stringe i programmatori erano innamorati dei puntatori. La personclasse avrebbe potuto apparire così:

class person
{
    char* name;
    int age;

public:

    // the constructor acquires a resource:
    // in this case, dynamic memory obtained via new[]
    person(const char* the_name, int the_age)
    {
        name = new char[strlen(the_name) + 1];
        strcpy(name, the_name);
        age = the_age;
    }

    // the destructor must release this resource via delete[]
    ~person()
    {
        delete[] name;
    }
};

Ancora oggi, le persone scrivono ancora lezioni in questo stile e si mettono nei guai: " Ho spinto una persona in un vettore e ora ho errori di memoria pazzi! " Ricorda che per impostazione predefinita, copiare un oggetto significa copiare i suoi membri, ma copiare namesemplicemente il membro copia un puntatore, non l'array di caratteri a cui punta! Ciò ha diversi effetti spiacevoli:

  1. Le modifiche tramite apossono essere osservate tramiteb .
  2. Una volta bdistrutto,a.name è un puntatore penzolante.
  3. Se aviene distrutto, l'eliminazione del puntatore penzolante produce un comportamento indefinito .
  4. Poiché il compito non tiene conto di ciò che nameindicava prima del compito, prima o poi otterrai perdite di memoria in tutto il luogo.

Definizioni esplicite

Poiché la copia membro non ha l'effetto desiderato, è necessario definire esplicitamente il costruttore della copia e l'operatore di assegnazione della copia per eseguire copie profonde della matrice di caratteri:

// 1. copy constructor
person(const person& that)
{
    name = new char[strlen(that.name) + 1];
    strcpy(name, that.name);
    age = that.age;
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    if (this != &that)
    {
        delete[] name;
        // This is a dangerous point in the flow of execution!
        // We have temporarily invalidated the class invariants,
        // and the next statement might throw an exception,
        // leaving the object in an invalid state :(
        name = new char[strlen(that.name) + 1];
        strcpy(name, that.name);
        age = that.age;
    }
    return *this;
}

Nota la differenza tra inizializzazione e assegnazione: dobbiamo eliminare il vecchio stato prima di assegnare a nameper evitare perdite di memoria. Inoltre, dobbiamo proteggere dall'autoassegnazione del modulo x = x. Senza quel controllo, delete[] nameeliminerebbe l'array contenente la stringa di origine , perché quando scrivi x = x, entrambi this->namee that.namecontengono lo stesso puntatore.

Sicurezza eccezionale

Sfortunatamente, questa soluzione fallirà se new char[...]genera un'eccezione a causa dell'esaurimento della memoria. Una possibile soluzione è quella di introdurre una variabile locale e riordinare le istruzioni:

// 2. copy assignment operator
person& operator=(const person& that)
{
    char* local_name = new char[strlen(that.name) + 1];
    // If the above statement throws,
    // the object is still in the same state as before.
    // None of the following statements will throw an exception :)
    strcpy(local_name, that.name);
    delete[] name;
    name = local_name;
    age = that.age;
    return *this;
}

Questo si occupa anche di autoassegnazione senza un controllo esplicito. Una soluzione ancora più efficace a questo problema è il linguaggio copia-e-scambia , ma non entrerò nei dettagli della sicurezza delle eccezioni qui. Ho citato solo le eccezioni per evidenziare quanto segue: Scrivere classi che gestiscono le risorse è difficile.

Risorse non copiabili

Alcune risorse non possono o non devono essere copiate, come handle di file o mutex. In tal caso, dichiarare semplicemente il costruttore della copia e l'operatore di assegnazione della copia come privatesenza fornire una definizione:

private:

    person(const person& that);
    person& operator=(const person& that);

In alternativa, puoi ereditarli boost::noncopyableo dichiararli come eliminati (in C ++ 11 e versioni successive):

person(const person& that) = delete;
person& operator=(const person& that) = delete;

La regola del tre

A volte è necessario implementare una classe che gestisce una risorsa. (Non gestire mai più risorse in una singola classe, questo provocherà solo dolore.) In tal caso, ricorda la regola di tre :

Se devi dichiarare esplicitamente tu stesso il distruttore, il costruttore della copia o l'operatore di assegnazione della copia, probabilmente dovrai dichiarare esplicitamente tutti e tre.

(Sfortunatamente, questa "regola" non è applicata dallo standard C ++ o da qualsiasi compilatore di cui sia a conoscenza.)

La regola del cinque

Da C ++ 11 in poi, un oggetto ha 2 funzioni membro speciali extra: il costruttore di spostamento e l'assegnazione di spostamento. La regola di cinque stati per implementare anche queste funzioni.

Un esempio con le firme:

class person
{
    std::string name;
    int age;

public:
    person(const std::string& name, int age);        // Ctor
    person(const person &) = default;                // Copy Ctor
    person(person &&) noexcept = default;            // Move Ctor
    person& operator=(const person &) = default;     // Copy Assignment
    person& operator=(person &&) noexcept = default; // Move Assignment
    ~person() noexcept = default;                    // Dtor
};

La regola dello zero

La regola di 3/5 viene anche definita regola del 0/3/5. La parte zero della regola indica che durante la creazione della classe è consentito non scrivere alcuna funzione del membro speciale.

Consigli

Il più delle volte, non è necessario gestire una risorsa da soli, perché una classe esistente come std::stringgià lo fa per te. Basta confrontare il semplice codice usando un std::stringmembro con l'alternativa contorta e soggetta a errori usando un char*e dovresti essere convinto. Finché stai lontano dai membri puntatori non elaborati, è improbabile che la regola del tre riguardi il tuo codice.


4
Fred, mi sentirei meglio con il mio voto positivo se (A) non specificassi un compito male implementato in un codice copiabile e aggiungessi una nota dicendo che è sbagliata e guardassi altrove nella stampa fine; o usa c & s nel codice o salta semplicemente l'implementazione di tutti questi membri (B) accorciando la prima metà, che ha poco a che fare con il RoT; (C) discuteresti dell'introduzione della semantica di movimento e di cosa significhi per il RoT.
sabato

7
Ma poi il post dovrebbe essere realizzato in C / W, credo. Mi piace che tu mantenga i termini per lo più precisi (vale a dire che dici " operatore copia incarico", e che non attiri nella trappola comune che l'assegnazione non potrebbe implicare una copia).
Johannes Schaub - litb

4
@Prasoon: non credo che ritagliare metà della risposta sarebbe visto come un "editing equo" di una risposta non CW.
sabato

69
Sarebbe bello se aggiorni il tuo post per C ++ 11 (ovvero sposta costruttore / incarico)
Alexander Malakhov

5
@solalito Tutto ciò che devi rilasciare dopo l'uso: blocchi di concorrenza, handle di file, connessioni al database, socket di rete, memoria heap ...
fredoverflow

510

La Regola dei tre è una regola empirica per il C ++, in pratica dice

Se la tua classe ne ha bisogno

  • un costruttore di copie ,
  • un operatore di incarico ,
  • o un distruttore ,

definito esplicitamente, quindi probabilmente avrà bisogno di tutti e tre .

La ragione di ciò è che tutti e tre sono generalmente usati per gestire una risorsa e se la tua classe gestisce una risorsa, di solito deve gestire sia la copia che la liberazione.

Se non esiste una semantica valida per la copia della risorsa gestita dalla tua classe, considera di vietare la copia dichiarando (non definendo ) il costruttore della copia e l'operatore di assegnazione come private.

(Si noti che la nuova versione imminente dello standard C ++ (che è C ++ 11) aggiunge la semantica di spostamento al C ++, che probabilmente cambierà la Regola di Tre. Tuttavia, ne so troppo poco per scrivere una sezione C ++ 11 sulla Regola dei Tre.)


3
Un'altra soluzione per impedire la copia è quella di ereditare (privatamente) da una classe che non può essere copiata (come boost::noncopyable). Può anche essere molto più chiaro. Penso che C ++ 0x e la possibilità di "eliminare" le funzioni potrebbero aiutare qui, ma ho dimenticato la sintassi: /
Matthieu M.

2
@Matthieu: Sì, anche quello funziona. Ma a meno noncopyableche non faccia parte della libreria standard, non lo considero molto un miglioramento. (Oh, e se hai dimenticato la sintassi di cancellazione, hai dimenticato mor ethan che io abbia mai conosciuto. :))
sbi

3
@Daan: vedi questa risposta . Tuttavia, mi consiglia di attenersi a Martinho s' Regola di Zero . Per me, questa è una delle regole empiriche più importanti per il C ++ coniato nell'ultimo decennio.
sbi,

3
La Regola dello Zero di Martinho ora è meglio (senza apparente acquisizione di adware) su Archive.org
Nathan Kidd il

161

La legge dei tre grandi è come sopra specificato.

Un semplice esempio, in parole povere, del tipo di problema che risolve:

Distruttore non predefinito

Hai allocato memoria nel tuo costruttore e quindi devi scrivere un distruttore per eliminarlo. Altrimenti causerai una perdita di memoria.

Potresti pensare che questo sia un lavoro fatto.

Il problema sarà che, se viene creata una copia del tuo oggetto, la copia punterà alla stessa memoria dell'oggetto originale.

Una volta, uno di questi elimina la memoria nel suo distruttore, l'altro avrà un puntatore alla memoria non valida (questo si chiama puntatore penzolante) quando cerca di usarlo le cose diventeranno pelose.

Pertanto, si scrive un costruttore di copie in modo che alloca nuovi oggetti i propri pezzi di memoria da distruggere.

Operatore di assegnazione e costruttore di copie

Hai allocato memoria nel costruttore a un puntatore membro della tua classe. Quando si copia un oggetto di questa classe, l'operatore di assegnazione predefinito e il costruttore della copia copieranno il valore di questo puntatore membro sul nuovo oggetto.

Ciò significa che il nuovo oggetto e l'oggetto vecchio indicheranno lo stesso pezzo di memoria, quindi quando lo si modifica in un oggetto verrà modificato anche per l'altro oggetto. Se un oggetto cancella questa memoria, l'altro continuerà a provare a usarla - eek.

Per risolvere questo problema, scrivi la tua versione del costruttore della copia e dell'operatore di assegnazione. Le versioni allocano memoria separata per i nuovi oggetti e copiano i valori a cui punta il primo puntatore anziché il suo indirizzo.


4
Quindi, se utilizziamo un costruttore di copie, la copia viene eseguita ma in una posizione di memoria completamente diversa e se non utilizziamo il costruttore di copie, viene eseguita la copia, ma punta alla stessa posizione di memoria. è quello che stai cercando di dire? Quindi una copia senza costruttore della copia significa che ci sarà un nuovo puntatore ma che punta alla stessa posizione di memoria, tuttavia se abbiamo il costruttore della copia esplicitamente definito dall'utente, avremo un puntatore separato che punta a una diversa posizione di memoria ma con i dati.
Infrangibile il

4
Mi dispiace, ho risposto a questo secoli fa, ma la mia risposta non sembra essere ancora qui :-( Fondamentalmente sì - lo capisci :-)
Stefan

1
Come si estende il principio all'operatore di assegnazione delle copie? Questa risposta sarebbe più utile se venisse menzionata la terza nella Regola dei tre.
DBedrenko,

1
@DBedrenko, "scrivi un costruttore di copie in modo che allochi nuovi oggetti i propri pezzi di memoria ..." questo è lo stesso principio che si estende all'operatore di assegnazione delle copie. Non pensi che l'abbia chiarito?
Stefan,

2
@DBedrenko, ho aggiunto qualche informazione in più. Ciò lo rende più chiaro?
Stefan,

44

Fondamentalmente se hai un distruttore (non il distruttore predefinito) significa che la classe che hai definito ha qualche allocazione di memoria. Supponiamo che la classe sia utilizzata all'esterno da un codice client o dall'utente.

    MyClass x(a, b);
    MyClass y(c, d);
    x = y; // This is a shallow copy if assignment operator is not provided

Se MyClass ha solo alcuni membri tipizzati primitivi, un operatore di assegnazione predefinito funzionerebbe, ma se avesse alcuni membri del puntatore e oggetti che non hanno operatori di assegnazione, il risultato sarebbe imprevedibile. Quindi possiamo dire che se c'è qualcosa da cancellare nel distruttore di una classe, potremmo aver bisogno di un operatore di copia profonda, il che significa che dovremmo fornire un costruttore di copia e un operatore di assegnazione.


36

Cosa significa copiare un oggetto? Ci sono alcuni modi in cui puoi copiare gli oggetti - parliamo dei 2 tipi a cui ti riferisci molto probabilmente - copia profonda e copia superficiale.

Dato che siamo in un linguaggio orientato agli oggetti (o almeno lo stiamo assumendo), supponiamo che abbiate un pezzo di memoria allocato. Dato che è un linguaggio OO, possiamo facilmente riferirci a blocchi di memoria che allociamo perché di solito sono variabili primitive (ints, caratteri, byte) o classi che abbiamo definito che sono fatte dei nostri tipi e primitive. Quindi diciamo che abbiamo una classe di auto come segue:

class Car //A very simple class just to demonstrate what these definitions mean.
//It's pseudocode C++/Javaish, I assume strings do not need to be allocated.
{
private String sPrintColor;
private String sModel;
private String sMake;

public changePaint(String newColor)
{
   this.sPrintColor = newColor;
}

public Car(String model, String make, String color) //Constructor
{
   this.sPrintColor = color;
   this.sModel = model;
   this.sMake = make;
}

public ~Car() //Destructor
{
//Because we did not create any custom types, we aren't adding more code.
//Anytime your object goes out of scope / program collects garbage / etc. this guy gets called + all other related destructors.
//Since we did not use anything but strings, we have nothing additional to handle.
//The assumption is being made that the 3 strings will be handled by string's destructor and that it is being called automatically--if this were not the case you would need to do it here.
}

public Car(const Car &other) // Copy Constructor
{
   this.sPrintColor = other.sPrintColor;
   this.sModel = other.sModel;
   this.sMake = other.sMake;
}
public Car &operator =(const Car &other) // Assignment Operator
{
   if(this != &other)
   {
      this.sPrintColor = other.sPrintColor;
      this.sModel = other.sModel;
      this.sMake = other.sMake;
   }
   return *this;
}

}

Una copia profonda è se dichiariamo un oggetto e quindi creiamo una copia completamente separata dell'oggetto ... finiamo con 2 oggetti in 2 set di memoria completamente.

Car car1 = new Car("mustang", "ford", "red");
Car car2 = car1; //Call the copy constructor
car2.changePaint("green");
//car2 is now green but car1 is still red.

Ora facciamo qualcosa di strano. Supponiamo che car2 sia programmato in modo errato o intenzionalmente inteso a condividere la memoria effettiva di cui è composto car1. (Di solito è un errore farlo e in classe è di solito la coperta di cui è discusso.) Fai finta che ogni volta che chiedi di car2, stai davvero risolvendo un puntatore allo spazio di memoria di car1 ... è più o meno una copia superficiale è.

//Shallow copy example
//Assume we're in C++ because it's standard behavior is to shallow copy objects if you do not have a constructor written for an operation.
//Now let's assume I do not have any code for the assignment or copy operations like I do above...with those now gone, C++ will use the default.

 Car car1 = new Car("ford", "mustang", "red"); 
 Car car2 = car1; 
 car2.changePaint("green");//car1 is also now green 
 delete car2;/*I get rid of my car which is also really your car...I told C++ to resolve 
 the address of where car2 exists and delete the memory...which is also
 the memory associated with your car.*/
 car1.changePaint("red");/*program will likely crash because this area is
 no longer allocated to the program.*/

Quindi, indipendentemente dalla lingua in cui stai scrivendo, fai molta attenzione a cosa intendi quando si tratta di copiare oggetti perché la maggior parte delle volte vuoi una copia approfondita.

Cosa sono il costruttore di copie e l'operatore di assegnazione delle copie? Li ho già usati sopra. Il costruttore della copia viene chiamato quando si digita un codice come Car car2 = car1; Essenzialmente se si dichiara una variabile e la si assegna su una riga, in quel momento viene chiamato il costruttore della copia. L'operatore di assegnazione è ciò che accade quando si utilizza un segno di uguale - car2 = car1;. L'avviso car2non è dichiarato nella stessa dichiarazione. I due blocchi di codice che scrivi per queste operazioni sono probabilmente molto simili. In effetti il ​​tipico modello di progettazione ha un'altra funzione che chiami per impostare tutto una volta che sei soddisfatto la copia / assegnazione iniziale è legittima - se guardi il codice a mano lunga che ho scritto, le funzioni sono quasi identiche.

Quando devo dichiararli io stesso? Se non stai scrivendo codice che deve essere condiviso o per la produzione in qualche modo, devi davvero dichiararli solo quando ne hai bisogno. È necessario essere consapevoli di ciò che fa il linguaggio del programma se si sceglie di utilizzarlo "per caso" e non ne è stato creato uno, ovvero si ottiene il compilatore predefinito. Uso raramente i costruttori di copie per esempio, ma le sostituzioni degli operatori di assegnazione sono molto comuni. Sapevi che puoi scavalcare anche il significato di addizione, sottrazione, ecc.?

Come posso impedire che i miei oggetti vengano copiati? Sostituire tutti i modi in cui ti è permesso allocare memoria per il tuo oggetto con una funzione privata è un inizio ragionevole. Se davvero non vuoi che le persone li copino, potresti renderlo pubblico e avvisare il programmatore lanciando un'eccezione e anche non copiare l'oggetto.


5
La domanda è stata taggata C ++. Questa esposizione a pseudo-codice fa ben poco per chiarire qualcosa sulla ben definita "Regola dei tre" nella migliore delle ipotesi e diffonde solo confusione nel peggiore dei casi.
Sehe

26

Quando devo dichiararli io stesso?

La Regola del Tre afferma che se dichiari una di a

  1. costruttore di copie
  2. copia operatore di assegnazione
  3. distruttore

allora dovresti dichiarare tutti e tre. Nacque dall'osservazione che la necessità di assumere il significato di un'operazione di copia derivava quasi sempre dalla classe che eseguiva una sorta di gestione delle risorse, e ciò implicava quasi sempre che

  • qualunque sia stata la gestione delle risorse in un'operazione di copia probabilmente doveva essere fatta nell'altra operazione di copia e

  • il distruttore di classe parteciperebbe anche alla gestione della risorsa (di solito rilasciandola). La risorsa classica da gestire era la memoria, ed è per questo che tutte le classi della libreria standard che gestiscono la memoria (ad esempio, i contenitori STL che eseguono la gestione dinamica della memoria) dichiarano tutti "i tre grandi": entrambe le operazioni di copia e un distruttore.

Una conseguenza della Regola dei tre è che la presenza di un distruttore dichiarato dall'utente indica che è improbabile che una copia saggia dei membri semplici sia appropriata per le operazioni di copia nella classe. Ciò, a sua volta, suggerisce che se una classe dichiara un distruttore, probabilmente le operazioni di copia non dovrebbero essere generate automaticamente, perché non farebbero la cosa giusta. Al momento dell'adozione di C ++ 98, il significato di questa linea di ragionamento non era pienamente apprezzato, quindi in C ++ 98 l'esistenza di un utente dichiarato distruttore non ha avuto alcun impatto sulla volontà dei compilatori di generare operazioni di copia. Questo continua ad essere il caso di C ++ 11, ma solo perché limitando le condizioni in cui vengono generate le operazioni di copia si romperebbe troppo codice legacy.

Come posso impedire che i miei oggetti vengano copiati?

Dichiara costruttore di copia e operatore di assegnazione copia come identificatore di accesso privato.

class MemoryBlock
{
public:

//code here

private:
MemoryBlock(const MemoryBlock& other)
{
   cout<<"copy constructor"<<endl;
}

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other)
{
 return *this;
}
};

int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}

A partire da C ++ 11 puoi anche dichiarare il costruttore di copie e l'operatore di assegnazione cancellati

class MemoryBlock
{
public:
MemoryBlock(const MemoryBlock& other) = delete

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other) =delete
};


int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}


10

La regola del tre in C ++ è un principio fondamentale della progettazione e dello sviluppo di tre requisiti secondo cui se esiste una chiara definizione in una delle seguenti funzioni membro, il programmatore dovrebbe definire insieme le altre due funzioni membri. In particolare, sono indispensabili le seguenti tre funzioni membro: distruttore, costruttore della copia, operatore di assegnazione della copia.

Il costruttore di copie in C ++ è un costruttore speciale. Viene utilizzato per creare un nuovo oggetto, che è il nuovo oggetto equivalente a una copia di un oggetto esistente.

L'operatore di assegnazione copia è un operatore di assegnazione speciale che viene solitamente utilizzato per specificare un oggetto esistente ad altri dello stesso tipo di oggetto.

Ci sono esempi rapidi:

// default constructor
My_Class a;

// copy constructor
My_Class b(a);

// copy constructor
My_Class c = a;

// copy assignment operator
b = a;

7
Ciao, la tua risposta non aggiunge nulla di nuovo. Gli altri trattano l'argomento in modo molto più approfondito e più accurato: la tua risposta è approssimativa e in effetti sbagliata in alcuni punti (vale a dire che qui non c'è un "must"; è "molto probabilmente dovrebbe"). Non varrebbe davvero la pena pubblicare questo tipo di risposta a domande a cui è già stata data una risposta esaustiva. A meno che tu non abbia nuove cose da aggiungere.
Mat

1
Inoltre, ci sono quattro esempi rapidi, che sono in qualche modo correlati a due dei tre di cui parla la Regola dei tre. Troppa confusione.
Anatolyg
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.