A másoláskonstruktor egy speciális konstruktor a C++ programozási nyelvben és néhány más programozási nyelvben, például a Java -ban, amelyet egy új objektum létrehozására használnak egy meglévő másolataként . Egy ilyen konstruktor legalább egy argumentumot használ: egy hivatkozást a másolandó objektumra.
Normális esetben a fordító minden osztályhoz automatikusan generál egy másolatkonstruktort ( implicit copy konstruktornak nevezik, azaz implicit módon megadott másoláskonstruktornak), de bizonyos esetekben a programozó létrehoz egy másolatkonstruktort, amelyet ezután explicitnek neveznek. másolás konstruktor (vagy "explicit módon megadott másolás konstruktor"). módon"). Ilyen esetekben a fordító nem generál implicit konstruktorokat.
A másoláskonstruktorra leginkább akkor van szükség, ha az objektum mutatót vagy nem megosztott hivatkozást tartalmaz, például fájlt , ebben az esetben általában szükség lesz egy destruktorra és egy hozzárendelési operátorra is (lásd a három szabályt ).
Az objektumok másolása a másoláskonstruktor és a hozzárendelés operátor használatával történik . A másoláskonstruktor első paramétereként (az opcionális const vagy volatile type módosítóval) a saját osztálytípusára való hivatkozást veszi. Ezen a paraméteren kívül további további paraméterek is lehetnek, feltéve, hogy az ilyen további paraméterek alapértelmezett értékekre vannak állítva [1] . A következő példa bemutatja az X osztály érvényes másolatkonstruktorait:
X ( const X & ); X ( X & ); X ( const volatile X & ); X ( illékony X & ); X ( const X & , int = 10 ); X ( const X & , double = 1,0 , int = 40 );A másoláskonstruktor első bejegyzése az elsődleges, más űrlapokat csak szükség esetén szabad használni. Csak ideiglenes objektumokat másolhat az első konstruktor használatával. Például:
Xa = X ( ); // Lefordítja, ha az X(const X&) konstruktor implementálva van, és hibát dob, // ha csak X(X&) van megadva. // Az a objektum létrehozásához a fordító létrehoz egy // X osztályú ideiglenes objektumot, majd a copy konstruktor segítségével létrehoz egy a objektumot. // Ideiglenes objektumok másolásához const típus szükséges.Az alábbi példában az a objektum megváltoztathatatlanként van létrehozva, így a b objektum létrehozásakor az első példány konstruktorra van szükség.
const X a ; X b = a ; // helyes, ha van X(const X&) és nem helyes, ha van X(X&) // mivel a második nem támogatja a const X& típustA X&másolás konstruktor típust akkor használjuk, ha módosítani kell a másolandó objektumot. Ez meglehetősen ritka helyzet, de a szabványos könyvtárban a hívással elérhető std::auto_ptr. A linknek meg kell valósítania:
Xa ; _ X b = a ; // javítsa ki, ha a másoláskonstruktorok bármelyike definiálva van // a hivatkozás átadása ótaA következő másoláskonstruktorok (vagy konstans konstruktorok) érvénytelenek:
X ( X ); X ( állandó X );mivel ezeknek a konstruktoroknak a meghívásához egy másik másolatra lesz szükség, ami egy végtelen rekurzív híváshoz (vagyis egy végtelen ciklushoz) vezet.
A másoláskonstruktor meghívásának négy esete van:
Egy objektumhoz kétféleképpen lehet értéket rendelni:
Egy objektum inicializálása a következő módok bármelyikén lehetséges:
a. Inicializálás a nyilatkozatnál
B objektum = A ; // lefordítva: Object::Object(const Object&)b. Inicializálás argumentumok függvényeknek való átadásakor
típusfüggvény ( Object a ) ;c. Amikor függvényértéket ad vissza
Objektum a = függvény ();A másoláskonstruktor csak inicializálás esetén használatos, és nem egy kifejezett hozzárendelés helyett (vagyis ahol a hozzárendelési operátort használják ).
Az implicit osztálymásolat konstruktor meghívja az alaposztályok másoláskonstruktorait, és bitenkénti másolatokat készít az osztálytagokról. Ha egy osztálytag egy osztály, akkor a másolat konstruktora meghívásra kerül. Ha skalár típusú (POD típusú C++-ban), akkor a beépített hozzárendelési operátort használjuk. És végül, ha egy tömbről van szó, akkor a tömb minden elemét a típusának megfelelő módon másoljuk. [2]
Egy explicit másoláskonstruktor használatával a programozó meghatározhatja, hogy mit tegyen az objektum másolása után.
A következő példák bemutatják, hogyan működnek a másoláskonstruktorok, és miért van szükség rájuk.
Eredmény
10 15 10 23 15 10Ahogy az várható volt, a timmy át lett másolva az új timmy_clone objektumba . A timmy korának (korának) megváltoztatásakor a timmy_clone kora nem változott: az objektumok teljesen függetlenek.
A fordító generált nekünk egy másolás konstruktort, ami valahogy így írható:
Személy ( Person Const & Copy ) : kor ( példány . kor ) {}A következő példa egy egyszerű dinamikus tömbosztályt mutat be:
#include <iostream> osztály Array { nyilvános : mérete ; _ int * adatok ; Tömb ( belső méret ) : méret ( méret ), adatok ( új int [ méret ]) {} ~ Tömb () { töröl [] adatot ; } }; int main () { Először a tömb ( 20 ); először . adat [ 0 ] = 25 ; { Tömb másolat = első ; std :: cout << first . adat [ 0 ] << " " << másolás . adat [ 0 ] << std :: endl ; } // (1) először . adat [ 0 ] = 10 ; // (2) }Eredmény
25 25 felosztási hibaItt a fordító automatikusan generálta a másolás konstruktort. Ez a konstruktor így néz ki:
Tömb ( Tömb állítása és másolása ) : méret ( másolat . méret ), adat ( másolat . adat ) {}Ezzel a konstruktorral az a probléma, hogy egyszerű másolatot készít az adatmutatóról . Csak a címet másolja, magát az adatot nem. És amikor a program eléri az (1) sort, a másolás destruktor meghívódik (a veremben lévő objektumok automatikusan megsemmisülnek, amikor elérik a határukat). Amint láthatja, az Array destruktor törli az adattömböt , így a másolat adatainak törlésekor az első adatot is törli . A (2) sor most hibás adatokat fogad és kiírja. Ez a híres szegmentációs hibához vezet.
Egy mélymásolást végrehajtó natív másoláskonstruktor esetén ez a probléma nem fog fellépni:
Tömb ( Tömb állítása és másolása ) : méret ( másolat . méret ), adat ( új int [ másolat . méret ]) { std :: másolás ( másolás . adat , másolás . adat + másolat . méret , adat ); // #include <algoritm> for std::copy }Itt egy új int tömb jön létre , és a tartalom belemásolódik. Most a másolás destruktora csak az adatait távolítja el, és nem érinti meg az első adatot . A (2) vonal már nem okoz szegmentációs hibát.
A mélymásolás végrehajtása helyett többféle optimalizálási stratégia is használható. Ez lehetővé teszi több objektum adathozzáférését biztonságos módon, így memóriát takarít meg. A másolás írásra stratégia csak akkor hoz létre másolatot az adatokról, amikor azokba írják. A hivatkozási szám tartalmazza az adatokra hivatkozó objektumok számának számlálóját, és csak akkor távolítja el, ha a számláló eléri a nullát (például boost::shared_ptr).
A sablonkonstruktor nem másoláskonstruktor .
sablon < típusnév T > Tömb :: Tömb ( const T & copy ) : méret ( másolat . méret ()), adatok ( új int [ másolat . méret ()]) { std :: másolás ( másolás . kezdete (), másolás . vége (), adat ); }Ez a konstruktor nem kerül felhasználásra, ha a T Tömb típusú.
Array arr ( 5 ); Array arr2 ( arr );A második sor meghívja a nem sablon másoláskonstruktort, vagy ha nem létezik, akkor az alapértelmezett másolatkonstruktort.