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 ).
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&-typenKopikonstruktø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åetFø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:
Et objekt kan tildeles en værdi på en af to måder:
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.
De følgende eksempler illustrerer, hvordan kopikonstruktører fungerer, og hvorfor de er nødvendige.
Resultat
10 15 10 23 15 10Som 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 ) {}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 SegmenteringsfejlHer 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).
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.