Kopioinnin rakentaja

Kokeneet kirjoittajat eivät ole vielä tarkistaneet sivun nykyistä versiota, ja se voi poiketa merkittävästi 31. maaliskuuta 2015 tarkistetusta versiosta . tarkastukset vaativat 16 muokkausta .

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ö ).

Määritelmä

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älkeen

Seuraavat 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:

  1. Kun objekti on palautusarvo
  2. Kun objekti välitetään (funktiolle) arvolla argumenttina
  3. Kun objekti on rakennettu toisesta objektista (saman luokan)
  4. Kun kääntäjä luo väliaikaisen objektin (kuten edellä ensimmäisessä ja toisessa tapauksessa; eksplisiittisenä muunnoksena jne.)

Toiminnot

Objektille voidaan määrittää arvo kahdella tavalla:

  • Eksplisiittinen määritys lausekkeessa
  • Alustus

Eksplisiittinen määritys lausekkeessa

Objekti A ; Objekti B ; A = B ; // käännetty muotoon Object::operator=(const Object&), // jolloin kutsutaan A.operator=(B)

Alustus

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.

Esimerkkejä

Seuraavat esimerkit havainnollistavat, kuinka kopiointikonstruktorit toimivat ja miksi niitä tarvitaan.

Implisiittinen kopiokonstruktori

#include <iostream> luokan henkilö { julkinen : int ikä ; Henkilö ( keskiikä ) : ikä ( ikä ) { } }; int main () { henkilö timmy ( 10 ); henkilö sally ( 15 ); Henkilö timmy_clone = timmy ; std :: cout << timmy . ikä << " " << sally . ikä << " " << timmy_clone . ikä << std :: endl ; timmy . ikä = 23 ; std :: cout << timmy . ikä << " " << sally . ikä << " " << timmy_clone . ikä << std :: endl ; }

Tulos

10 15 10 23 15 10

Kuten 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ä ) { }

Eksplisiittisen kopion rakentaja

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 Segmentointivirhe

Tää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).

Kopioi rakentajat ja mallit

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.

Katso myös

Muistiinpanot

  1. INCITS ISO IEC 14882-2003 12.8.2. [1] Arkistoitu 8. kesäkuuta 2007 Wayback Machinessa
  2. INCITS ISO IEC 14882-2003 12.8.8. [2] Arkistoitu 8. kesäkuuta 2007 Wayback Machinessa