2010-02-06 17 views
15

Jestem trochę zdezorientowany co do mechaniki konstruktora kopii. Popraw mnie, jeśli się mylę:Czy mogę wywołać jawnie konstruktora kopiowania?

Jeśli metoda odwołuje się do obiektu jako parametru, a klasa definiuje construtor kopiowania, wówczas klasa używa konstruktora do utworzenia kopii samego siebie i to zostaje przekazane do funkcja zamiast odniesienia do oryginalnego obiektu?

Ponadto można nazwać

Object * obj = new Object(&anotherObject); 

aby utworzyć kopię anotherObject?

+1

Twój terminologia nie powinny sugerować, że 'anotherObject' tworzy kopię siebie .. obiekt, który jest tworzony po nowy będzie tworzenie kopii' anotherObject'. –

+5

@Hassan: Nie zmieniaj znaczenia pytania bez zadawania OP. To ważne wyróżnienie. '& anotherObject' to wskaźnik, a nie referencja. –

+0

"nie można powiedzieć", że 'nowy obiekt (& innyObiekt)' utworzy niejawnie kopię innego obiektu (co sugeruje to sformułowanie). Aby to stwierdzenie było prawdziwe, należy dostosować składnię. –

Odpowiedz

26

Nie, jeśli funkcja ma odniesienie:

void f1(Object & o); // call by reference 

wtedy nie kopii. Jeśli funkcja przyjmuje wartość:

void f2(Object o); // call by value 

to kopia jest tworzona przez kompilator za pomocą konstruktora kopiowania.

I tak, kiedy mówisz:

Object * obj = new Object(anotherObject); // not &anotherObject 

konstruktor kopia jest wykorzystywana jawnie (zakładając anotherObject jest typu Object). Nie ma nic magicznego używania new Tutaj jednak - w tym przypadku:

Object obj2(anotherObject); 

używany jest również konstruktor kopii.

+1

"Następnie kompilator tworzy kopię za pomocą konstruktora kopii." - może użyć konstruktora ruchu, a także jest scenariuszem kopiowania, więc być może nawet nie ma konstruktora. –

7

Jeśli metoda odwołuje się do obiektu jako parametru, konstruktor kopiowania nie zostanie wywołany. Gdyby tak było, to wywołanie konstruktora kopiowania spowodowałoby nieskończoną pętlę (ponieważ jako argument przyjmuje referencję).

Linia ta nie jest prawidłowym sposobem wywołania konstruktora kopiowania. Oczekuje odniesienia jako argumentu, a nie wskaźnika.

+0

W rzeczywistości argument konstruktora kopii nie jest stały. Standard językowy umożliwia opcjonalne kopiowanie w niektórych przypadkach i wyraźnie zaznacza, że ​​implementacja musi zapewnić, że nie ma nieskończonej rekursji w przypadku konstruktora kopiowania, jak jeden przykład (patrz przypis 93 w C++ 03) – AnT

+0

@AndreyT: Interesujące. W każdym razie to nie zmienia rzeczy w tym przypadku, ponieważ mówimy o * nie * kopiowaniu w przypadku odniesienia, co jest całkiem pewną rzeczą. –

+0

Cóż, część standardu, o której mówiłem, dotyczy w rzeczywistości inicjowania referencji i jawnie pozwala na kopiowanie w niektórych sytuacjach (w przypadku inicjalizacji const-reference). Oczywiście, kompilatory zwykle nie angażują się w dzikie nieuzasadnione kopiowanie, ale znane są przykłady kompilatorów tworzących dziwne, nielogiczne (ale wciąż legalne) kopie. – AnT

1

Nie w obu przypadkach. W pierwszym przypadku odniesienie do samego obiektu jest przekazywane, a kopia nie jest tworzona. W drugim przypadku przekazujesz wskaźnik do konstruktora object, więc nie jest tworzona żadna kopia. Tak obiekt powinien mieć konstruktora (nie konstruktora kopia), który jest czymś w rodzaju object(anotherClass*)

1

Copy konstruktora jest wywoływana tylko przy przejściu przez wartośćnie przez odniesienie. Przez odniesienie nie jest potrzebne kopiowanie (jest to część tego, do czego odnoszą się referencje!), Więc nie wywołano konstruktora kopiowania.

3

Fakt, że wykonujesz wywołanie metody, nie ma tutaj żadnego znaczenia. Inicjowanie parametrów referencyjnych podczas wywołania funkcji nie różni się od inicjalizacji referencyjnej i podlega tym samym regułom.

Zasady inicjowania referencji są nieco skomplikowane, ale najważniejsze jest to, że jeśli inicjalizator jest lwartością (argument w wywołaniu metody w twoim przypadku), a typ referencji jest taki sam jak typ inicjator (tj. typ parametru jest taki sam, jak typ argumentu), wówczas referencja będzie powiązana bezpośrednio. To znaczy. kopia nie jest tworzona.

Object a; // class type 
Object &r = a; // no copying 
const Object &cr = a; // no copying 

Jeśli te wymagania nie są spełnione (na przykład jeśli inicjalizator jest wartością runt), to wszystko zależy. W niektórych przypadkach kopiowanie może i będzie miało miejsce. Na przykład

const Object &tr = Object(); 

mogą być interpretowane przez kompilator jako

const Object &tr = Object(Object(Object(Object()))); 

z realizacji zależny od skończonej liczby copyings. Oczywiście, ze względów wydajnościowych kompilatory normalnie starają się nie tworzyć niepotrzebnych kopii, nawet jeśli mogą kopiować.

Klasycznym przykładem, że często wzbudza dyskusję na temat ważności zachowania kopiowanie kompilator jest inicjalizacja odniesienia w wyrażeniach jak na poniższym jednego

Object a; 
const Object &r = <some condition> ? a : Object(); 

Osoba zaznajomiona z semantyki C++ odniesienia zrozumie, że wyrażenia takie jak powyższe są prawdopodobnie uzasadnieniem standardowego zezwolenia na wykonywanie zbędnego kopiowania podczas inicjowania referencji.

+0

czy jesteś pewny tego nieskończonego problemu? C++ 03 8.5.3 mówi, że istnieją tylko dwa przypadki: (a) "Odniesienie jest związane z obiektem reprezentowanym przez wartość r (patrz 3.10) lub do pod-obiektu wewnątrz tego obiektu" i (b) " Tworzony jest tymczasowy typ "cv1 T2" [sic] i wywoływany jest konstruktor, który kopiuje cały obiekt rvalue do pliku tymczasowego.Odniesienie jest powiązane z tymczasowym lub z obiektem podrzędnym wewnątrz tymczasowego ". Zauważ, że przypadek (b) mówi * związany z *, nie * zainicjowany z *, więc nie widzę, w jaki sposób (b) zezwala na więcej niż jeden dodatkowy czasowy. –

+0

Oczywiście, C++ 14 (który pojawił się po napisaniu twojej odpowiedzi) zezwala tylko na case (a), tj. Nie można utworzyć więcej tymczasowych obiektów poza 'Object()'. Nie sprawdziłem brzmienia C++ 11, chociaż ISTR miał wadę dotyczącą inicjowania referencji od wartości r. –

0

tak wykorzystujące umieszczenie nowego tak:

Object dstObject; 
new(&dstObject) Object(&anotherObject); 
Powiązane problemy