Ugrás a tartalomhoz

object copying

A Wikiszótárból, a nyitott szótárból


Főnév

object copying (tsz. object copyings)

  1. (informatika) Objektumok másolása C++-ban egy fontos koncepció, amelynek során egy objektum másolatát hozzuk létre. A másolás során különböző problémák merülhetnek fel, például sekély másolás (shallow copy) vagy mély másolás (deep copy). C++ támogatja az automatikus másolást, de néha saját másoló mechanizmusokat kell írni.



1. Az objektum másolásának három módja

C++-ban egy objektum másolása három módon történhet: 1. Másoló konstruktor – Új objektum létrehozása egy meglévő másolataként. 2. Értékadás (operator=) – Egy meglévő objektum másolása egy másikba. 3. Mozgató konstruktor (move) – Erőforrások áthelyezése másolás helyett (C++11+).



2. Alapértelmezett másolás (shallow copy)

Ha egy osztály nem definiál saját másolási mechanizmust, akkor a C++ alapértelmezett másoló konstruktort és értékadó operátort biztosít.

Példa: Alapértelmezett másolás

#include <iostream>
using namespace std;

class Auto {
public:
    string marka;
    
    Auto(string m) { marka = m; }
};

int main() {
    Auto a1("Toyota");
    Auto a2 = a1;  // Alapértelmezett másolás

    cout << "A1 márkája: " << a1.marka << endl;
    cout << "A2 márkája: " << a2.marka << endl;
    return 0;
}

Kimenet:

A1 márkája: Toyota
A2 márkája: Toyota

Ez az alapértelmezett másoló konstruktor működése, amely az adattagokat byte-by-byte másolja.



3. Sekély másolás vs. Mély másolás

Ha az osztály dinamikus memóriát (new) használ, akkor az alapértelmezett másolás veszélyes lehet.

Sekély másolás problémája

#include <iostream>
using namespace std;

class Auto {
public:
    string* marka;
    
    Auto(string m) {
        marka = new string(m);  // Dinamikus memóriafoglalás
    }

    ~Auto() {
        delete marka;  // Memória felszabadítása
    }
};

int main() {
    Auto a1("BMW");
    Auto a2 = a1;  // Alapértelmezett másolás (hiba!)

    cout << *a1.marka << endl;
    cout << *a2.marka << endl;  // Azonos memóriaterületre mutat

    return 0;
}

Probléma:
- Az a1 és a2 ugyanarra a memóriaterületre mutat. - Ha az egyik objektum destruktora lefut, a másik objektum mutatója érvénytelenné válik (double delete hiba!).

Mély másolás megoldása

A sekély másolás helyett mély másolást kell használni, amely új memóriaterületet foglal minden másolat számára.

#include <iostream>
using namespace std;

class Auto {
public:
    string* marka;
    
    Auto(string m) {
        marka = new string(m);  // Dinamikus memóriafoglalás
    }

    // Másoló konstruktor (mély másolás)
    Auto(const Auto& masik) {
        marka = new string(*masik.marka);
    }

    ~Auto() {
        delete marka;
    }
};

int main() {
    Auto a1("Mercedes");
    Auto a2 = a1;  // Most mély másolás történik

    cout << *a1.marka << endl;
    cout << *a2.marka << endl;  // Külön példányban tárolt érték

    return 0;
}

Megoldás: - A másoló konstruktor új memóriát foglal és önálló másolatot készít.



4. Másoló konstruktor (Copy Constructor)

A másoló konstruktor felelős az objektum másolásáért.

Szintaxis

ClassName(const ClassName& other);

Példa másoló konstruktorra

class Ember {
public:
    string nev;

    Ember(string n) { nev = n; }

    // Másoló konstruktor
    Ember(const Ember& masik) {
        nev = masik.nev;
    }
};

5. Értékadó operátor (operator=) túlterhelése

A másoló konstruktor csak új objektum létrehozásánál működik. Ha egy már létező objektumnak adunk értéket egy másik objektumból, akkor az operator= operátort kell felüldefiniálni.

Értékadás alapértelmezett működése

Ember e1("Anna");
Ember e2 = e1;  // Másoló konstruktor
Ember e3;
e3 = e1;        // `operator=` hívódik meg

Értékadó operátor túlterhelése

class Ember {
public:
    string* nev;

    Ember(string n) {
        nev = new string(n);
    }

    // Másoló konstruktor
    Ember(const Ember& masik) {
        nev = new string(*masik.nev);
    }

    // Értékadó operátor túlterhelése
    Ember& operator=(const Ember& masik) {
        if (this != &masik) {  // Önvisszaadás ellenőrzése
            delete nev;  // Régi memória felszabadítása
            nev = new string(*masik.nev);
        }
        return *this;
    }

    ~Ember() {
        delete nev;
    }
};

Miért fontos az if (this != &masik) feltétel? - Ha a = a; történne, akkor feleslegesen törölnénk az objektumot és újra foglalnánk.



6. Mozgató konstruktor (Move Constructor)

C++11-től a másolás helyett hatékonyabb megoldás a mozgatás.

Példa mozgató konstruktorra:

class Ember {
public:
    string* nev;

    Ember(string n) {
        nev = new string(n);
    }

    // Mozgató konstruktor
    Ember(Ember&& masik) noexcept {
        nev = masik.nev;  // Átvesszük a mutatót
        masik.nev = nullptr;  // Az eredeti objektum "üressé" válik
    }

    ~Ember() {
        delete nev;
    }
};

A mozgató konstruktor átadja az erőforrásokat ahelyett, hogy lemásolná őket.



7. Összegzés

Módszer Használat Probléma
Alapértelmezett másolás Automatikus másolás = vagy másoló konstruktor nélkül Sekély másolás – azonos memóriacímek
Mély másolás Saját másoló konstruktor és operator= Hatékony, de extra memóriafoglalás
Mozgató konstruktor Erőforrások áthelyezése (move) Hatékony, de csak C++11+ támogatja



Konklúzió

Objektummásolás C++-ban alapértelmezetten sekély másolásként működik, ami dinamikus memória esetén veszélyes lehet. A helyes működés érdekében érdemes saját másoló konstruktort, értékadó operátort vagy mozgató konstruktort implementálni.