Warunek wstępny: Konstruktor w C++
A konstruktor kopiujący to funkcja członkowska, która inicjuje obiekt przy użyciu innego obiektu tej samej klasy. W uproszczeniu konstruktor tworzący obiekt poprzez inicjalizację go obiektem tej samej klasy, który został utworzony wcześniej, nazywany jest konstruktor kopiujący .
Konstruktor kopiujący służy do inicjowania elementów nowo utworzonego obiektu poprzez kopiowanie elementów już istniejącego obiektu.
Konstruktor kopiujący przyjmuje jako argument odwołanie do obiektu tej samej klasy.
Sample(Sample &t) { id=t.id; }>
Proces inicjowania elementów obiektu za pomocą konstruktora kopiującego nazywany jest inicjalizacją kopiowania.
Nazywa się to również inicjalizacją członkowską, ponieważ konstruktor kopiujący inicjuje jeden obiekt istniejącym obiektem, przy czym oba należą do tej samej klasy na zasadzie kopiowania elementów członkowskich.
Konstruktor kopiujący może być jawnie zdefiniowany przez programistę. Jeśli programista nie zdefiniuje konstruktora kopiującego, kompilator zrobi to za nas.
Przykład:
testowanie i rodzaje testów

Składnia konstruktora kopiującego
C++
#include> #include> using> namespace> std;> class> student {> > int> rno;> > char> name[50];> > double> fee;> public> :> > student(> int> ,> char> [],> double> );> > student(student& t)> // copy constructor> > {> > rno = t.rno;> > strcpy> (name, t.name);> > fee = t.fee;> > }> > void> display();> };> student::student(> int> no,> char> n[],> double> f)> {> > rno = no;> > strcpy> (name, n);> > fee = f;> }> void> student::display()> {> > cout << endl << rno <<> ' '> << name <<> ' '> << fee;> }> int> main()> {> > student s(1001,> 'Manjeet'> , 10000);> > s.display();> > student manjeet(s);> // copy constructor called> > manjeet.display();> > return> 0;> }> |
>
>Wyjście
1001 Manjeet 10000 1001 Manjeet 10000>
C++
#include> #include> using> namespace> std;> class> student {> > int> rno;> > char> name[50];> > double> fee;> public> :> > student(> int> ,> char> [],> double> );> > student(student& t)> // copy constructor (member wise> > // initialization)> > {> > rno = t.rno;> > strcpy> (name, t.name);> > }> > void> display();> > void> disp() { cout << endl << rno <<> ' '> << name; }> };> student::student(> int> no,> char> n[],> double> f)> {> > rno = no;> > strcpy> (name, n);> > fee = f;> }> void> student::display()> {> > cout << endl << rno <<> ' '> << name <<> ' '> << fee;> }> int> main()> {> > student s(1001,> 'Manjeet'> , 10000);> > s.display();> > student manjeet(s);> // copy constructor called> > manjeet.disp();> > return> 0;> }> |
>
>Wyjście
1001 Manjeet 10000 1001 Manjeet>
Charakterystyka konstruktora kopiującego
1. Konstruktor kopiujący służy do inicjowania elementów nowo utworzonego obiektu poprzez kopiowanie elementów już istniejącego obiektu.
2. Konstruktor kopiujący przyjmuje jako argument odwołanie do obiektu tej samej klasy. Jeśli przekażesz obiekt według wartości w konstruktorze kopiującym, spowoduje to rekurencyjne wywołanie samego konstruktora kopiującego. Dzieje się tak, ponieważ przekazanie wartości wiąże się z utworzeniem kopii, a wykonanie kopii wiąże się z wywołaniem konstruktora kopiującego, co prowadzi do nieskończonej pętli. Użycie odniesienia pozwala uniknąć tej rekurencji. Dlatego używamy odniesienia do obiektów, aby uniknąć nieskończonych wywołań.
Sample(Sample &t) { id=t.id; }>
3. Proces inicjowania elementów obiektu za pomocą konstruktora kopiującego nosi nazwę inicjalizacja kopii.
4 . Nazywa się to również inicjalizacją członkowską, ponieważ konstruktor kopiujący inicjuje jeden obiekt istniejącym obiektem, przy czym oba należą do tej samej klasy na zasadzie kopiowania element po elemencie.
5. Konstruktor kopiujący może być jawnie zdefiniowany przez programistę. Jeśli programista nie zdefiniuje konstruktora kopiującego, kompilator zrobi to za nas.
Przykład:
C++
// C++ program to demonstrate the working> // of a COPY CONSTRUCTOR> #include> using> namespace> std;> class> Point {> private> :> > int> x, y;> public> :> > Point(> int> x1,> int> y1)> > {> > x = x1;> > y = y1;> > }> > // Copy constructor> > Point(> const> Point& p1)> > {> > x = p1.x;> > y = p1.y;> > }> > int> getX() {> return> x; }> > int> getY() {> return> y; }> };> int> main()> {> > Point p1(10, 15);> // Normal constructor is called here> > Point p2 = p1;> // Copy constructor is called here> > // Let us access values assigned by constructors> > cout <<> 'p1.x = '> << p1.getX()> > <<> ', p1.y = '> << p1.getY();> > cout <<> '
p2.x = '> << p2.getX()> > <<> ', p2.y = '> << p2.getY();> > return> 0;> }> |
>
>Wyjście
p1.x = 10, p1.y = 15 p2.x = 10, p2.y = 15>
Rodzaje konstruktorów kopiujących
1. Domyślny konstruktor kopiujący
Niejawnie zdefiniowany konstruktor kopiujący skopiuje podstawy i elementy obiektu w tej samej kolejności, w jakiej konstruktor inicjował podstawy i elementy obiektu.
C++
// Implicit copy constructor Calling> #include> using> namespace> std;> class> Sample {> > int> id;> public> :> > void> init(> int> x) { id = x; }> > void> display() { cout << endl <<> 'ID='> << id; }> };> int> main()> {> > Sample obj1;> > obj1.init(10);> > obj1.display();> > // Implicit Copy Constructor Calling> > Sample obj2(obj1);> // or obj2=obj1;> > obj2.display();> > return> 0;> }> |
>
>Wyjście
ID=10 ID=10>
2. Konstruktor kopiujący zdefiniowany przez użytkownika
Konstruktor kopiujący zdefiniowany przez użytkownika jest zazwyczaj potrzebny, gdy obiekt posiada wskaźniki lub odniesienia, których nie można współdzielić, na przykład do pliku, w takim przypadku należy również zapisać destruktor i operator przypisania
C++
// Explicitly copy constructor Calling> #include> using> namespace> std;> class> Sample {> > int> id;> public> :> > void> init(> int> x) { id = x; }> > Sample() {}> // default constructor with empty body> > Sample(Sample& t)> // copy constructor> > {> > id = t.id;> > }> > void> display() { cout << endl <<> 'ID='> << id; }> };> int> main()> {> > Sample obj1;> > obj1.init(10);> > obj1.display();> > Sample obj2(> > obj1);> // or obj2=obj1; copy constructor called> > obj2.display();> > return> 0;> }> |
wielkość łyżeczki
>
>Wyjście
ID=10 ID=10>
C++
mój krykiet na żywo
// C++ Programt to demonstrate the student details> #include> #include> using> namespace> std;> class> student {> > int> rno;> > string name;> > double> fee;> public> :> > student(> int> , string,> double> );> > student(student& t)> // copy constructor> > {> > rno = t.rno;> > name = t.name;> > fee = t.fee;> > }> > void> display();> };> student::student(> int> no, string n,> double> f)> {> > rno = no;> > name = n;> > fee = f;> }> void> student::display()> {> > cout << endl << rno <<> ' '> << name <<> ' '> << fee;> }> int> main()> {> > student s(1001,> 'Ram'> , 10000);> > s.display();> > student ram(s);> // copy constructor called> > ram.display();> > return> 0;> }> |
>
>Wyjście
1001 Ram 10000 1001 Ram 10000>
Kiedy wywoływany jest konstruktor kopiujący?
W C++ konstruktor kopiujący można wywołać w następujących przypadkach:
- Gdy obiekt klasy jest zwracany przez wartość.
- Kiedy obiekt klasy jest przekazywany (do funkcji) przez wartość jako argument.
- Kiedy obiekt jest konstruowany w oparciu o inny obiekt tej samej klasy.
- Gdy kompilator generuje obiekt tymczasowy.
Nie ma jednak gwarancji, że we wszystkich tych przypadkach zostanie wywołany konstruktor kopiujący, ponieważ standard C++ pozwala kompilatorowi zoptymalizować kopiowanie w niektórych przypadkach, jednym z przykładów jest optymalizacja wartości zwrotu (czasami określane jako RVO).
Skopiuj Elisiona
W przypadku eliminacji kopii kompilator zapobiega tworzeniu dodatkowych kopii, co skutkuje oszczędnością miejsca i większą złożonością programu (zarówno czasu, jak i przestrzeni); Stąd też większa optymalizacja kodu.
Przykład:
C++
// C++ program to demonstrate> // the working of copy elision> #include> using> namespace> std;> class> GFG {> public> :> > void> print() { cout <<> ' GFG!'> ; }> };> int> main()> {> > GFG G;> > for> (> int> i = 0; i <= 2; i++) {> > G.print();> > cout <<> '
'> ;> > }> > return> 0;> }> |
>
>Wyjście
GFG! GFG! GFG!>
Teraz kompilator decyduje, co chce wydrukować, może albo wydrukować powyższy wynik, albo może wydrukować przypadek 1 lub przypadek 2 poniżej i to właśnie Optymalizacja wartości zwrotu Jest. W prostych słowach, RVO to technika, która daje kompilatorowi dodatkową moc zakończenia utworzonego obiektu tymczasowego, co skutkuje zmianą obserwowalnego zachowania/charakterystyki końcowego programu.
Przypadek 1:
GFG! GFG!>
Przypadek 2:
GFG!>
Kiedy potrzebny jest konstruktor kopiujący zdefiniowany przez użytkownika?
Jeśli nie zdefiniujemy własnego konstruktora kopiującego, kompilator C++ utworzy domyślny konstruktor kopiujący dla każdej klasy, który wykonuje kopiowanie składowe pomiędzy obiektami. Konstruktor kopiujący utworzony przez kompilator działa ogólnie dobrze. Musimy zdefiniować własny konstruktor kopiujący tylko wtedy, gdy obiekt ma wskaźniki lub jakąkolwiek alokację zasobu w czasie wykonywania uchwyt pliku , połączenie sieciowe itp.
Domyślny konstruktor wykonuje tylko płytką kopię.
Głębokie kopiowanie jest możliwe tylko przy użyciu konstruktora kopiującego zdefiniowanego przez użytkownika. W konstruktorze kopiującym zdefiniowanym przez użytkownika upewniamy się, że wskaźniki (lub referencje) kopiowanych obiektów wskazują nowe lokalizacje w pamięci.
Konstruktor kopiujący a operator przypisania
Główna różnica między konstruktorem kopiującym a operatorem przypisania polega na tym, że konstruktor kopiujący tworzy nową pamięć za każdym razem, gdy jest wywoływany, podczas gdy operator przypisania nie tworzy nowej pamięci.
Która z poniższych instrukcji wywołuje konstruktor kopiujący, a która operator przypisania?
Ciąg listy Java
MyClass t1, t2; MyClass t3 = t1; // ---->(1) t2 = t1; // -----> (2)>
Konstruktor kopiujący wywoływany jest, gdy na podstawie istniejącego obiektu tworzony jest nowy obiekt, jako kopia istniejącego obiektu. Operator przypisania jest wywoływany, gdy już zainicjowanemu obiektowi zostaje przypisana nowa wartość z innego istniejącego obiektu. W powyższym przykładzie (1) wywołuje konstruktor kopiujący i (2) wywołuje operator przypisania. Zobacz to, aby uzyskać więcej szczegółów.
Przykład – klasa, w której wymagany jest konstruktor kopiujący
Poniżej znajduje się kompletny program w języku C++ demonstrujący użycie konstruktora Copy. W poniższej klasie String musimy napisać konstruktor kopiujący.
Przykład:
C++
// C++ program to demonstrate the> // Working of Copy constructor> #include> #include> using> namespace> std;> class> String {> private> :> > char> * s;> > int> size;> public> :> > String(> const> char> * str = NULL);> // constructor> > ~String() {> delete> [] s; }> // destructor> > String(> const> String&);> // copy constructor> > void> print()> > {> > cout << s << endl;> > }> // Function to print string> > void> change(> const> char> *);> // Function to change> };> // In this the pointer returns the CHAR ARRAY> // in the same sequence of string object but> // with an additional null pointer ' '> String::String(> const> char> * str)> {> > size => strlen> (str);> > s => new> char> [size + 1];> > strcpy> (s, str);> }> void> String::change(> const> char> * str)> {> > delete> [] s;> > size => strlen> (str);> > s => new> char> [size + 1];> > strcpy> (s, str);> }> String::String(> const> String& old_str)> {> > size = old_str.size;> > s => new> char> [size + 1];> > strcpy> (s, old_str.s);> }> int> main()> {> > String str1(> 'GeeksQuiz'> );> > String str2 = str1;> > str1.print();> // what is printed ?> > str2.print();> > str2.change(> 'techcodeview.com'> );> > str1.print();> // what is printed now ?> > str2.print();> > return> 0;> }> |
>
>Wyjście
GeeksQuiz GeeksQuiz GeeksQuiz techcodeview.com>
Jaki byłby problem, gdybyśmy usunęli konstruktor kopiujący z powyższego kodu?
Jeśli usuniemy konstruktor kopiujący z powyższego programu, nie otrzymamy oczekiwanych wyników. Zmiany wprowadzone w str2 odzwierciedlają również str1, czego nigdy się nie oczekuje.
C++
#include> #include> using> namespace> std;> class> String {> private> :> > char> * s;> > int> size;> public> :> > String(> const> char> * str = NULL);> // constructor> > ~String() {> delete> [] s; }> // destructor> > void> print() { cout << s << endl; }> > void> change(> const> char> *);> // Function to change> };> String::String(> const> char> * str)> {> > size => strlen> (str);> > s => new> char> [size + 1];> > strcpy> (s, str);> }> // In this the pointer returns the CHAR ARRAY> // in the same sequence of string object but> // with an additional null pointer ' '> void> String::change(> const> char> * str) {> strcpy> (s, str); }> int> main()> {> > String str1(> 'GeeksQuiz'> );> > String str2 = str1;> > str1.print();> // what is printed ?> > str2.print();> > str2.change(> 'techcodeview.com'> );> > str1.print();> // what is printed now ?> > str2.print();> > return> 0;> }> |
>
>
Wyjście:
GeeksQuiz GeeksQuiz techcodeview.com techcodeview.com>
Czy możemy ustawić konstruktor kopiujący jako prywatny?
Tak, Konstruktor kopiujący można ustawić jako prywatny. Kiedy w klasie uczynimy konstruktor kopiujący prywatnym, obiektów tej klasy nie będzie można kopiować. Jest to szczególnie przydatne, gdy nasza klasa ma wskaźniki lub dynamicznie przydzielane zasoby. W takich sytuacjach możemy albo napisać własny konstruktor kopiujący, jak w powyższym przykładzie String, albo utworzyć prywatny konstruktor kopiujący, aby użytkownicy otrzymywali błędy kompilatora, a nie niespodzianki w czasie wykonywania.
Dlaczego argument konstruktora kopiującego musi być przekazywany jako referencja?
Konstruktor kopiujący wywoływany jest, gdy obiekt jest przekazywany przez wartość. Konstruktor kopiujący sam w sobie jest funkcją. Jeśli więc przekażemy argument przez wartość w konstruktorze kopiującym, zostanie wykonane wywołanie konstruktora kopiującego w celu wywołania konstruktora kopiującego, co stanie się niekończącym się łańcuchem wywołań. Dlatego kompilator nie pozwala na przekazywanie parametrów przez wartość.
Dlaczego argument konstruktora kopiującego powinien być stały?
Jeden powód przejścia konst odniesieniem jest to, że powinniśmy użyć konst w C++, jeśli to możliwe, aby obiekty nie zostały przypadkowo zmodyfikowane. Jest to jeden z dobrych powodów przekazywania odniesienia jako konst , ale kryje się za tym coś więcej niż tylko „ Dlaczego argument konstruktora kopiującego powinien mieć wartość stałą?