MySQL Java JavaScript PHP Python HTML-CSS C-sharp C++ Go

Constructorul de copiere

În mod implicit, la compilarea claselor, compilatorul generează automat un constructor special – constructorul de copiere, care permite crearea unui obiect pe baza altui obiect (practic, îl copiază). Constructorul de copiere implicit copiază valorile câmpurilor obiectului într-un nou obiect. Să analizăm un exemplu simplu:

#include <iostream>
 
class Person 
{
private:
    std::string name;
    unsigned age;
public:
    Person(std::string p_name, unsigned p_age)
    { 
        name = p_name; 
        age = p_age;
    }
    void print() 
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
};
int main()
{
    Person tom{"Tom", 38};
    Person tomas{tom};    // creăm obiectul tomas pe baza obiectului tom
    tomas.print();        // Name: Tom   Age: 38
}

În acest caz, linia:

Person tomas{tom};

reprezintă apelul constructorului de copiere. Deși nu definim nicăieri în cod un constructor care să primească un alt obiect Person, un astfel de constructor este generat automat de compilator. În rezultat, obiectul tomas va avea aceleași valori ca și obiectul tom.

Constructorul de copiere este foarte util atunci când dorim să creăm un obiect pe baza altuia, însă are și dezavantajele sale. De exemplu, dacă un câmp reprezintă un pointer, atunci se copiază adresa. În acest caz, câmpurile ambelor obiecte vor indica aceeași adresă în memorie. Prin urmare, dacă dorim să modificăm valoarea unui obiect, aceasta se va modifica și pentru celălalt. În asemenea situații, putem defini propriul nostru constructor de copiere.

Crearea unui constructor de copiere

Constructorul de copiere trebuie să primească ca parametru un obiect al aceleiași clase. Este recomandat ca parametrul să fie transmis prin referință, deoarece la transmiterea prin valoare, compilatorul va crea o copie a obiectului, iar pentru a crea copia se va apela constructorul de copiere, ceea ce va duce la recursivitate infinită. Așadar, să definim propriul constructor de copiere:

#include <iostream>
 
class Person 
{
    std::string name;
    unsigned age;
public:
    Person(std::string p_name, unsigned p_age)
    { 
        name = p_name; 
        age = p_age;
    }
    Person(const Person &p)
    {
        name = p.name;
        age = p.age + 1;    // doar ca exemplu
    }
    void print() 
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
};
int main()
{
    Person tom{"Tom", 38};
    Person tomas{tom};    // creăm obiectul tomas pe baza obiectului tom
    tomas.print();        // Name: Tom   Age: 39
}

Aici, constructorul de copiere primește o referință constantă către un obiect Person și copiază valorile câmpurilor acestuia în câmpurile obiectului curent. Ca exemplu, pentru a arăta o diferență, am adăugat 1 la proprietatea age. Astfel, în locul constructorului de copiere implicit, este utilizat constructorul personalizat.

Ștergerea constructorului de copiere

Constructorul de copiere nu este întotdeauna necesar. Și poate fi șters cu ajutorul operatorului delete:

#include <iostream>
 
class Person 
{
    std::string name;
    unsigned age;
public:
    Person(std::string p_name, unsigned p_age)
    { 
        name = p_name; 
        age = p_age;
    }
    Person(const Person &p) = delete;   // ștergem constructorul
    void print() 
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
};
int main()
{
    Person tom{"Tom", 38};
    //Person tomas{tom};    // constructorul de copiere nu este disponibil
}