Kopi konstruktør

Den aktuelle version af siden er endnu ikke blevet gennemgået af erfarne bidragydere og kan afvige væsentligt fra den version , der blev gennemgået den 31. marts 2015; checks kræver 16 redigeringer .

En kopikonstruktør er en  speciel konstruktør i C++ programmeringssproget og i nogle andre programmeringssprog, såsom Java , der bruges til at oprette et nyt objekt som en kopi af et eksisterende. En sådan konstruktør tager mindst ét ​​argument: en reference til det objekt, der skal kopieres.

Normalt vil compileren automatisk generere en kopikonstruktør for hver klasse (kendt som implicitte kopikonstruktører, dvs. kopikonstruktører, der er implicit specificeret), men i nogle tilfælde vil programmøren oprette en kopikonstruktør, som så kaldes en eksplicit kopikonstruktør (eller "kopikonstruktør specificeret eksplicit"). måde"). I sådanne tilfælde genererer compileren ikke implicitte konstruktører.

Kopikonstruktøren er for det meste nødvendig, når objektet har en pointer eller ikke-delt reference , såsom en fil , i hvilket tilfælde du normalt også har brug for en destruktor og en tildelingsoperator (se treregelen ).

Definition

Kopiering af objekter udføres ved at bruge kopikonstruktøren og tildelingsoperatoren . Kopikonstruktøren tager som sin første parameter (med den valgfrie const eller volatile type modifier) ​​en reference til sin egen klassetype. Ud over denne parameter kan den have flere yderligere parametre, forudsat at sådanne yderligere parametre er indstillet til standardværdier [1] . Følgende eksempel viser gyldige kopikonstruktører for klasse X:

X ( konst X & ); X ( X & ); X ( konst flygtigt X & ); X ( flygtige X & ); X ( konst X & , int = 10 ); X ( konst X & , dobbelt = 1,0 , int = 40 );

Den første indtastning af kopikonstruktøren er primær; andre former bør kun bruges, når det er nødvendigt. Du kan kun kopiere midlertidige objekter ved at bruge den første konstruktør. For eksempel:

Xa = X ( ); // Kompilerer, hvis X(const X&)-konstruktøren er implementeret, og vil kaste en fejl // hvis kun X(X&) er defineret. // For at oprette et objekt a, vil compileren oprette et midlertidigt objekt af klassen // X og derefter bruge kopikonstruktøren til at oprette et objekt a. // Kopiering af midlertidige objekter kræver en const-type.

I eksemplet nedenfor er objekt a oprettet som uforanderligt, så når du opretter objekt b , kræves den første kopikonstruktør.

const X a ; Xb = a ; _ // ret hvis der er X(const X&) og ikke korrekt hvis der er X(X&) // da den anden ikke understøtter const X&-typen

Kopikonstruktørtypen X&bruges, når det er nødvendigt at ændre det objekt, der kopieres. Dette er en ret sjælden situation, men den leveres i standardbiblioteket ved at kalde std::auto_ptr. Linket skal implementere:

Xa ; _ Xb = a ; _ // ret hvis nogen af ​​kopikonstruktørerne er defineret // siden referencen blev bestået

Følgende kopikonstruktører (eller konstantkonstruktører) er ugyldige:

X ( X ); X ( konst X );

da kald af disse konstruktører vil kræve en anden kopi, hvilket vil føre til et uendeligt rekursivt kald (det vil sige en uendelig løkke).

Der er fire tilfælde af at kalde en kopikonstruktør:

  1. Når et objekt er en returværdi
  2. Når et objekt sendes (til en funktion) efter værdi som et argument
  3. Når et objekt er konstrueret ud fra et andet objekt (af samme klasse)
  4. Når compileren genererer et midlertidigt objekt (som i det første og andet tilfælde ovenfor; som en eksplicit konvertering osv.)

Operationer

Et objekt kan tildeles en værdi på en af ​​to måder:

  • Eksplicit tildeling i et udtryk
  • Initialisering

Eksplicit tildeling i et udtryk

Objekt A ; Objekt B ; A = B ; // oversat som Object::operator=(const Object&), // kalder således A.operator=(B)

Initialisering

Et objekt kan initialiseres på en af ​​følgende måder:

en. Initialisering ved erklæring

Objekt B = A ; // oversat som Object::Object(const Object&)

b. Initialisering ved overførsel af argumenter til funktioner

type funktion ( Objekt a );

c. Når du returnerer en funktionsværdi

Objekt a = funktion ();

Kopikonstruktøren bruges kun i tilfælde af initialisering og bruges ikke i stedet for en eksplicit tildeling (det vil sige, hvor tildelingsoperatoren bruges ).

Den implicitte klassekopikonstruktør kalder basisklassernes kopikonstruktører og laver bitvise kopier af klassemedlemmerne. Hvis et klassemedlem er en klasse, kaldes dets kopikonstruktør. Hvis det er en skalartype (POD-type i C++), så bruges den indbyggede tildelingsoperator. Og endelig, hvis det er et array, kopieres hvert element i arrayet på den passende måde for deres type. [2]

Ved at bruge en eksplicit kopikonstruktør kan programmøren bestemme, hvad der skal gøres, efter at objektet er blevet kopieret.

Eksempler

De følgende eksempler illustrerer, hvordan kopikonstruktører fungerer, og hvorfor de er nødvendige.

Den implicitte kopikonstruktør

#include <iostream> klasseperson _ { offentligt : int alder ; Person ( int alder ) : alder ( alder ) {} }; int main () { person timmy ( 10 ); person sally ( 15 ); Person timmy_clone = timmy ; std :: cout << timmy . alder << " " << sally . alder << " " << timmy_clone . alder << std :: endl ; timmy . alder = 23 ; std :: cout << timmy . alder << " " << sally . alder << " " << timmy_clone . alder << std :: endl ; }

Resultat

10 15 10 23 15 10

Som forventet blev timmy kopieret ind i det nye timmy_clone- objekt . Ved ændring af timmys alder (alder) ændrede timmy_clone sig ikke: objekterne er fuldstændig uafhængige.

Compileren genererede en kopikonstruktør til os, som kunne skrives sådan her:

Person ( personkonst & kopi ) _ : alder ( kopi . alder ) {}

Eksplicit kopikonstruktør

Følgende eksempel viser en simpel dynamisk array-klasse:

#include <iostream> klasse Array { offentligt : intsize ; _ int * data ; Array ( int størrelse ) : størrelse ( størrelse ), data ( ny int [ størrelse ]) {} ~ Array () { slette [] data ; } }; int main () { Array først ( 20 ); først . data [ 0 ] = 25 ; { Array copy = first ; std :: cout << først . data [ 0 ] << " " << kopi . data [ 0 ] << std :: endl ; } // (1) først . data [ 0 ] = 10 ; // (2) }

Resultat

25 25 Segmenteringsfejl

Her genererede compileren kopikonstruktøren automatisk. Denne konstruktør ser sådan ud:

Array ( Array const & copy ) : størrelse ( kopi . størrelse ), data ( kopi . data ) {}

Problemet med denne konstruktør er, at den laver en simpel kopi af datamarkøren . Den kopierer kun adressen, ikke selve dataene. Og når programmet når linjen (1) kaldes kopidestruktoren ( objekter på stakken ødelægges automatisk, når de når deres grænser). Som du kan se, sletter Array - destruktoren dataarrayet , så når det sletter kopiens data , sletter det også først data . Linje (2) modtager nu forkerte data og skriver dem. Dette fører til den berømte segmenteringsfejl .

I tilfælde af en indbygget kopikonstruktør, der udfører en dyb kopi , vil dette problem ikke opstå:

Array ( Array const & copy ) : størrelse ( kopi . størrelse ), data ( ny int [ kopi . størrelse ]) { std :: kopi ( kopi . data , kopi . data + kopi . størrelse , data ); // #include <algorithm> for std::copy }

Her oprettes et nyt int -array, og indholdet kopieres ind i det. Nu vil copy 's destructor kun fjerne sine data og ikke røre førsts data . Linje (2) forårsager ikke længere en segmenteringsfejl.

I stedet for at udføre en dyb kopi, kan flere optimeringsstrategier bruges. Dette vil muliggøre dataadgang for flere objekter på en sikker måde, hvilket sparer hukommelse. Kopier-på-skriv- strategien opretter kun en kopi af dataene, når der skrives til dem. Referencetællingen indeholder en tæller for antallet af objekter, der refererer til dataene og fjerner den først, når tælleren når nul (f.eks. boost::shared_ptr).

Kopier konstruktører og skabeloner

Skabelonkonstruktør er ikke en kopikonstruktør .

skabelon < typename T > Array :: Array ( const T & copy ) : størrelse ( kopi . størrelse ()), data ( ny int [ kopi . størrelse ()]) { std :: copy ( copy . start (), copy . end (), data ); }

Denne konstruktør vil ikke blive brugt, hvis T er af typen Array.

Array arr ( 5 ); Array arr2 ( arr );

Den anden linje kalder enten kopikonstruktøren uden skabelon eller, hvis den ikke findes, standardkopikonstruktøren.

Se også

Noter

  1. INCITER ISO IEC 14882-2003 12.8.2. [1] Arkiveret 8. juni 2007 på Wayback Machine
  2. INCITER ISO IEC 14882-2003 12.8.8. [2] Arkiveret 8. juni 2007 på Wayback Machine