Kopiokonstruktori on C++-ohjelmointikielessä ja joissakin muissa ohjelmointikielissä, kuten Javassa, erityinen konstruktori , jota käytetään uuden objektin luomiseen kopiona olemassa olevasta. Tällainen konstruktori ottaa ainakin yhden argumentin: viittauksen kopioitavaan objektiin.
Normaalisti kääntäjä luo automaattisesti kopiokonstruktorin jokaiselle luokalle (tunnetaan implisiittisinä kopiokonstruktoreina, eli implisiittisesti määritetyinä kopiokonstruktoreina), mutta joissain tapauksissa ohjelmoija luo kopiokonstruktorin, jota kutsutaan sitten eksplisiittiseksi kopiokonstruktori (tai "eksplisiittisesti määritetty kopiointikonstruktori"). tavalla"). Tällaisissa tapauksissa kääntäjä ei luo implisiittisiä konstruktoreita.
Kopiokonstruktoria tarvitaan enimmäkseen, kun objektilla on osoitin tai ei-jaettu viittaus , kuten tiedosto , jolloin yleensä tarvitset myös destruktorin ja osoitusoperaattorin (katso Kolmen sääntö ).
Objektien kopiointi tapahtuu kopiointikonstruktorin ja osoitusoperaattorin avulla . Kopiokonstruktori ottaa ensimmäisenä parametrinaan (valinnaisen const tai volatile type modifierin kanssa) viittauksen omaan luokkatyyppiinsä. Tämän parametrin lisäksi siinä voi olla muita lisäparametreja, jos tällaiset lisäparametrit on asetettu oletusarvoihin [1] . Seuraava esimerkki osoittaa kelvolliset kopiokonstruktorit luokassa X:
X ( jatkuva X & ); X ( X & ); X ( const volatile X & ); X ( haihtuva X & ); X ( const X & , int = 10 ); X ( const X & , double = 1.0 , int = 40 );Kopion rakentajan ensimmäinen merkintä on ensisijainen, muita lomakkeita tulee käyttää vain tarvittaessa. Voit kopioida vain väliaikaisia objekteja käyttämällä ensimmäistä konstruktoria. Esimerkiksi:
Xa = X ( ); // Kääntää, jos X(const X&) -konstruktori on toteutettu, ja antaa virheilmoituksen // jos vain X(X&) on määritetty. // Objektin a luomiseksi kääntäjä luo väliaikaisen objektin luokasta // X ja käyttää sitten kopiokonstruktoria objektin a luomiseen. // Väliaikaisten objektien kopiointi vaatii const-tyypin.Alla olevassa esimerkissä objekti a luodaan muuttumattomaksi, joten objektia b luotaessa tarvitaan ensimmäinen kopiokonstruktori.
const X a ; Xb = a ; _ // korjaa jos on X(const X&) ja ei oikein jos on X(X&) // koska toinen ei tue const X& -tyyppiäX&Kopiointikonstruktorityyppiä käytetään, kun kopioitavaa objektia on muutettava . Tämä on melko harvinainen tilanne, mutta se tarjotaan vakiokirjastossa soittamalla std::auto_ptr. Linkin tulee toteuttaa:
Xa ; _ Xb = a ; _ // korjaa, jos jokin kopioiden rakentajista on määritetty // viittauksen välityksen jälkeenSeuraavat kopiokonstruktorit (tai vakiokonstruktorit) ovat virheellisiä:
X ( X ); X ( jatkuva X );koska näiden konstruktorien kutsuminen vaatii toisen kopion, joka johtaa äärettömään rekursiiviseen kutsuun (eli äärettömään silmukkaan).
Kopiokonstruktorin kutsumisessa on neljä tapausta:
Objektille voidaan määrittää arvo kahdella tavalla:
Objekti voidaan alustaa millä tahansa seuraavista tavoista:
a. Alustus ilmoituksessa
Objekti B = A ; // käännetty nimellä Object::Object(const Object&)b. Alustus siirrettäessä argumentteja funktioille
tyyppifunktio ( Object a ) ;c. Kun palauttaa funktion arvon
Objekti a = funktio ();Kopiokonstruktoria käytetään vain alustuksen yhteydessä, eikä sitä käytetä nimenomaisen osoituksen sijasta (eli silloin, kun käytetään osoitusoperaattoria ).
Implisiittinen luokkakopiokonstruktori kutsuu perusluokkien kopiokonstruktorit ja tekee bittikohtaisia kopioita luokan jäsenistä. Jos luokan jäsen on luokka, sen kopiokonstruktori kutsutaan. Jos se on skalaarityyppiä (POD-tyyppi C++:ssa), käytetään sisäänrakennettua määritysoperaattoria. Ja lopuksi, jos se on taulukko, taulukon jokainen elementti kopioidaan tyypille sopivalla tavalla. [2]
Käyttämällä eksplisiittistä kopiointikonstruktoria ohjelmoija voi määrittää, mitä tehdä objektin kopioinnin jälkeen.
Seuraavat esimerkit havainnollistavat, kuinka kopiointikonstruktorit toimivat ja miksi niitä tarvitaan.
Tulos
10 15 10 23 15 10Kuten odotettiin, timmy kopioitiin uuteen timmy_clone -objektiin . Kun timmyn ikää (ikää) muutettiin, timmy_clone : n ikä ei muuttunut: objektit ovat täysin itsenäisiä.
Kääntäjä loi meille kopiokonstruktorin, joka voisi olla vaikkapa näin:
Henkilö ( Henkilövahvistus ja kopio ) _ ikä ( kopio . ikä ) { }Seuraava esimerkki näyttää yhden yksinkertaisen dynaamisen taulukon luokan:
#include <iostream> luokan Array { julkinen : kokoinen ; _ int * data ; Array ( int size ) : koko ( koko ), tiedot ( uusi int [ koko ]) {} ~ Joukko () { poista [] tiedot ; } }; int main () { Array ensin ( 20 ); ensin . data [ 0 ] = 25 ; { Array kopio = ensimmäinen ; std :: cout << ensin . data [ 0 ] << " " << kopioida . data [ 0 ] << std :: endl ; } // (1) ensin . data [ 0 ] = 10 ; // (2) }Tulos
25 25 SegmentointivirheTäällä kääntäjä loi kopiokonstruktorin automaattisesti. Tämä konstruktori näyttää tältä:
Array ( Array const & copy ) : koko ( kopio . koko ), tiedot ( kopio . data ) {}Tämän konstruktorin ongelma on, että se tekee yksinkertaisen kopion dataosoittimesta . Se kopioi vain osoitteen, ei itse tietoja. Ja kun ohjelma saavuttaa rivin (1) , kutsutaan kopioiden tuhoajaa (pinon objektit tuhoutuvat automaattisesti, kun ne saavuttavat rajansa). Kuten näet, Array - destruktori poistaa tietotaulukon , joten kun se poistaa kopion tiedot , se poistaa myös ensimmäisen tiedot . Rivi (2) vastaanottaa nyt virheellisiä tietoja ja kirjoittaa sen. Tämä johtaa kuuluisaan segmentointivirheeseen .
Tätä ongelmaa ei esiinny, kun kyseessä on natiivikopion rakentaja, joka suorittaa syväkopion :
Array ( Array const & copy ) : koko ( kopio . koko ), tiedot ( uusi int [ kopio . koko ]) { std :: kopioi ( kopio . data , kopioi . data + kopio . koko , data ); // #include <algoritm> for std::copy }Täällä luodaan uusi int - taulukko ja sisältö kopioidaan siihen. Nyt kopioinnin tuhoaja poistaa vain sen tiedot, ei koske ensimmäisen tietoihin . Linja (2) ei enää aiheuta segmentointivirhettä.
Syväkopioinnin sijaan voidaan käyttää useita optimointistrategioita. Tämä mahdollistaa useiden kohteiden tietojen käytön turvallisella tavalla, mikä säästää muistia. Kopiointi-kirjoitusstrategia luo kopion tiedoista vain, kun se on kirjoitettu. Viitemäärä sisältää tietoihin viittaavien objektien lukumäärän laskurin ja poistaa sen vasta, kun laskuri saavuttaa nollan (esimerkiksi boost::shared_ptr).
Mallin rakentaja ei ole kopiokonstruktori .
malli < typename T > Array :: Array ( const T & copy ) : koko ( kopio . koko ()), tiedot ( uusi int [ kopio . koko ()]) { std :: kopioi ( kopioi . alkaa (), kopioi . loppu (), data ); }Tätä konstruktoria ei käytetä, jos T on tyyppiä Array.
Array arr ( 5 ); Array arr2 ( arr );Toinen rivi kutsuu joko ei-mallipohjaista kopiokonstruktoria tai, jos sitä ei ole, oletuskopion rakentajaa.