object copying
Főnév
object copying (tsz. object copyings)
- (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.
- object copying - Szótár.net (en-hu)
- object copying - Sztaki (en-hu)
- object copying - Merriam–Webster
- object copying - Cambridge
- object copying - WordNet
- object copying - Яндекс (en-ru)
- object copying - Google (en-hu)
- object copying - Wikidata
- object copying - Wikipédia (angol)