Конструктор копіювання — особливий конструктор в мові програмування , який використовується для створення нових об'єктів як копії існуючого об'єкта. Першим аргументом такого коструктора є посилання (константне або ні) на об'єкт того ж типу, що й тип об'єкта який ми конструюємо, за цим параметром можуть іти інші будь-яких типів, але обов'язково із значеннями за замовчанням.
Зазвичай компілятор самостійно створює коструктор копіювання для кожного класу (відомий як уставний (англ. default) конструктор копіювання), але при потребі програміст створює конструктор копіювання, відомий як користувачевий або визначений користувачем конструктор копіювання. В таких випадках компілятор не створює його.
Користувацький конструктор копіювання здебільшого потрібен, коли об'єкт має вказівники або неспільні посилання, такі як файл, в цьому випадку деструктор і також мають бути написані (дивись ).
Визначення
Копіювання об'єктів досягається через використання конструктора копіювання і . Перший параметр конструктора копіювання це посилання (можливо const або volatile) на його власний тип класу. Він може мати більше аргументів, але інші мають мати значення за замовчанням. В наступному прикладі наводяться правильні конструктори для класу X:
X(const X& copyFromMe); X(X& copyFromMe); X(const volatile X& copyFromMe); X(volatile X& copyFromMe); X(const X& copyFromMe, int = 10); X(const X& copyFromMe, double = 1.0, int = 40);
X a = X(); // в даному випадку, завдяки оптимізації, одразу створюється об'єкт а // без використання конструктора копіювання, // хоча може бути створений тимчасовий константний об'єкт із // використанням звичайного конструктора X, // який потім, за допомогою конструктора копіювання, // ініціалізує a як копію тимчасового об'єкта.
Розглянемо різницю між першим і другим конструкторами копіювання
X const a; X b = a; // вірно для X(const X& copyFromMe), але не вірно X(X& copyFromMe) // бо другий конструктор вимагає неконстантне посилання X&
Варіант X&
використовується при потребі змінити об'єкт, що копіюється. Таке потрібно дуже рідко, але це можна побачити в стандартній бібліотеці .
X a; X b = a; // вірно для всіх конструкторів копіювання
Наступні конструктори копіювання не вірні через те, що copyFromMe передається не як посилання:
X(X copyFromMe); X(const X copyFromMe);
тобто виклик такого конструктора викличе копіювання параметрів, що в свою чергу призведе до нескінченної рекурсії.
Конструктор копіювання може викликатися в наступних випадках:
- Коли об'єкт повертається за значенням
- Коли об'єкт передається в функцію за значеннам як аргумент
- Коли об'єкт ініціалізується за допомогою іншого об'єкта (того ж типу)
- Коли об'єкт створюється компілятором як тимчасовий
Всі ці випадки тотожні до:T x = a;
У деяких випадках компілятор може з метою оптимізації не викликати конструктор копіюваня.
Операції
Об'єктові може бути присвоєне значення двома шляхами:
- Явне присвоєння у виразі
- Ініціалізація
Явне присвоєння у виразі
Object A; Object B; A = B; // перекладається як Object::operator=(const Object&), тобто викликається A.operator=(B) // (викликається просте копіювання, а не конструктор копіювання!)
Ініціалізація
Об'єкт може бути ініціалізований у будь-який спосіб з наступних.
a. Через оголошення
Object B = A; // перекладається як Object::Object(const Object&) (виклик конструктора копіювання)
b. Через аргументи функції
type function (Object a);
c. Через повернене значення
Object a = function();
Конструктор копіювання використовується виключно для ініціалізації, і не застосовується для присвоювання де використовується оператор присвоювання натомість.
Неявний конструктор копіювання класу викликає базовий конструктор копіювання і копіює члени класу підходящим для їхнього типу чином. Якщо це тип класу, тоді відповідний конструктор копіювання викликається, якщо це скалярний тип, використовується вбудований оператор присвоювання. Насамкінець, якщо це масив, кожен елемент копіюється відповідно до його типу.
Шляхом визначення користувачевого конструктора копіювання програміст може задати необхідну поведінку при копіюванні.
Приклади
Наступні приклади показують як працюють конструктори копіювання і навіщо вони потрібні.
Неявний конструктор копіювання
Розглянемо наступний приклад.
#include <iostream> class Person { public: int age; explicit Person(int age) : age(age) {} }; int main() { Person timmy(10); Person sally(15); Person timmy_clone = timmy; std::cout << timmy.age << " " << sally.age << " " << timmy_clone.age << std::endl; timmy.age = 23; std::cout << timmy.age << " " << sally.age << " " << timmy_clone.age << std::endl; return 0; }
Виведення
10 15 10 23 15 10
Як і очікувалось, timmy був скопійований в новий об'єкт, timmy_clone. І хоча вік timmy був змінений, вік timmy_clone залишився тим самим. Причина цього в тому, що це зовсім різні об'єкти.
Компілятор створив конструктор копіювання для нас, і він може бути записаний так:
Person(const Person& copy) : age(copy.age) {}
Тобто, коли ж ми дійсно потребуємо користувачевого конструктора копіювання? Наступний розділ дасть відповідь на це питання.
Користувацький конструктор копіювання
Тепер, уявімо дуже простий клас динамічних масивів:
#include <iostream> class Array { public: int size; int* data; explicit Array(int size) : size(size), data(new int[size]) {} ~Array() { delete[] data; } }; int main() { Array first(20); first.data[0] = 25; { Array copy = first; std::cout << first.data[0] << " " << copy.data[0] << std::endl; } // (1) first.data[0] = 10; // (2) return 0; }
Виведення
25 25 Segmentation fault
Через те, що ми не визначили конструктор копіювання, компілятор створив його для нас. Створений конструктор може виглядати схожим на наступний:
Array(const Array& copy) : size(copy.size), data(copy.data) {}
У даному разі конструктор виконав (англ. shallow copy) вказівника data. Він скопіював лише адресу первісного члена даних; тобто вони обидва використовують вказівник на одну ділянку пам'яті, нам би хотілось іншого. Коли програма досягає рядка (1), викликається деструктор копії (через автоматичне знищення об'єкта у стеку при виході з його області видимості). Деструктор масиву видаляє масив data, що використовується обома об'єктами. Після цього на рядку (2) ми намагаємось отримати доступ до неіснуючого об'єкта і записати туди! Це викликає відому .
Якщо ми напишемо власний конструктор копіювання, який виконує глибоке копіювання, тоді ми позбудемось цієї проблеми.
Array(const Array& copy) : size(copy.size), data(new int[copy.size]) { std::copy(copy.data, copy.data + copy.size, data); // #include <algorithm> для std::copy }
Тут, ми створюємо новий масив і копіюємо вміст у нього. Тепер деструктор copy видалить лише власні дані і не зачепить дані об'єкта first. Рядок (2) відтепер не викликатиме помилку сегментації.
Замість того, щоб робити глибоке копіюваня одразу, можна використати один із способів оптимізації. Вони дозволяють безпечне спільне використання даних кількома об'єктами, таким чином зберігаючи пам'ять. Стратегія копіювання при записі робить копію даних тільки при спробі запису. Лічильник посилань слідкує за тим скільки об'єктів посилаються на дані, і знищує їх тільки тоді, коли кількість дорівнює нулю (наприклад boost::shared_ptr).
Конструктори копіювання і шаблони
Всупереч очікуванням, шаблонний конструктор копіювання не є користувачевим конструктором копіювання. Тож недостатньо мати:
template <typename A> Array::Array(const A& copy) : size(copy.size()), data(new int[copy.size()]) { std::copy(copy.begin(),copy.end(),data); }
(Зауважте, що тип A
може бути Array
.) Користувачевий, нешаблонний конструктор копіювання має бути визначений для конструювання Array на основі Array.
Явний конструктор копіювання
Явний коструктор копіювання визначається за допомогою ключового слова explicit. Наприклад:
explicit X(const X& copyFromMe);
Він використовується для запобігання копіюванню об'єктів під час виклику функцій або під час ініціалізації копіюванням.
X a; X b = a; // помилка X b(a); // вірно
Примітки
- INCITS ISO IEC 14882-2003 12.8.2. [1] [ 8 червня 2007 у Wayback Machine.]
- ISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages — C++ § 8.5 Initializers [dcl.init] para. 12
- INCITS ISO IEC 14882-2003 12.8.8. [2] [ 8 червня 2007 у Wayback Machine.]
Вікіпедія, Українська, Україна, книга, книги, бібліотека, стаття, читати, завантажити, безкоштовно, безкоштовно завантажити, mp3, відео, mp4, 3gp, jpg, jpeg, gif, png, малюнок, музика, пісня, фільм, книга, гра, ігри, мобільний, телефон, android, ios, apple, мобільний телефон, samsung, iphone, xiomi, xiaomi, redmi, honor, oppo, nokia, sonya, mi, ПК, web, Інтернет
Konstruktor kopiyuvannya osoblivij konstruktor v movi programuvannya C yakij vikoristovuyetsya dlya stvorennya novih ob yektiv yak kopiyi isnuyuchogo ob yekta Pershim argumentom takogo kostruktora ye posilannya konstantne abo ni na ob yekt togo zh tipu sho j tip ob yekta yakij mi konstruyuyemo za cim parametrom mozhut iti inshi bud yakih tipiv ale obov yazkovo iz znachennyami za zamovchannyam Zazvichaj kompilyator samostijno stvoryuye kostruktor kopiyuvannya dlya kozhnogo klasu vidomij yak ustavnij angl default konstruktor kopiyuvannya ale pri potrebi programist stvoryuye konstruktor kopiyuvannya vidomij yak koristuvachevij abo viznachenij koristuvachem konstruktor kopiyuvannya V takih vipadkah kompilyator ne stvoryuye jogo Koristuvackij konstruktor kopiyuvannya zdebilshogo potriben koli ob yekt maye vkazivniki abo nespilni posilannya taki yak fajl v comu vipadku destruktor i takozh mayut buti napisani divis Pravilo troh ViznachennyaKopiyuvannya ob yektiv dosyagayetsya cherez vikoristannya konstruktora kopiyuvannya i operatora prisvoyuvannya Pershij parametr konstruktora kopiyuvannya ce posilannya mozhlivo const abo volatile na jogo vlasnij tip klasu Vin mozhe mati bilshe argumentiv ale inshi mayut mati znachennya za zamovchannyam V nastupnomu prikladi navodyatsya pravilni konstruktori dlya klasu X X const X amp copyFromMe X X amp copyFromMe X const volatile X amp copyFromMe X volatile X amp copyFromMe X const X amp copyFromMe int 10 X const X amp copyFromMe double 1 0 int 40 X a X v danomu vipadku zavdyaki optimizaciyi odrazu stvoryuyetsya ob yekt a bez vikoristannya konstruktora kopiyuvannya hocha mozhe buti stvorenij timchasovij konstantnij ob yekt iz vikoristannyam zvichajnogo konstruktora X yakij potim za dopomogoyu konstruktora kopiyuvannya inicializuye a yak kopiyu timchasovogo ob yekta Rozglyanemo riznicyu mizh pershim i drugim konstruktorami kopiyuvannya X const a X b a virno dlya X const X amp copyFromMe ale ne virno X X amp copyFromMe bo drugij konstruktor vimagaye nekonstantne posilannya X amp Variant X amp vikoristovuyetsya pri potrebi zminiti ob yekt sho kopiyuyetsya Take potribno duzhe ridko ale ce mozhna pobachiti v standartnij biblioteci X a X b a virno dlya vsih konstruktoriv kopiyuvannya Nastupni konstruktori kopiyuvannya ne virni cherez te sho copyFromMe peredayetsya ne yak posilannya X X copyFromMe X const X copyFromMe tobto viklik takogo konstruktora vikliche kopiyuvannya parametriv sho v svoyu chergu prizvede do neskinchennoyi rekursiyi Konstruktor kopiyuvannya mozhe viklikatisya v nastupnih vipadkah Koli ob yekt povertayetsya za znachennyam Koli ob yekt peredayetsya v funkciyu za znachennam yak argument Koli ob yekt inicializuyetsya za dopomogoyu inshogo ob yekta togo zh tipu Koli ob yekt stvoryuyetsya kompilyatorom yak timchasovij Vsi ci vipadki totozhni do T x a U deyakih vipadkah kompilyator mozhe z metoyu optimizaciyi ne viklikati konstruktor kopiyuvanya OperaciyiOb yektovi mozhe buti prisvoyene znachennya dvoma shlyahami Yavne prisvoyennya u virazi InicializaciyaYavne prisvoyennya u virazi Object A Object B A B perekladayetsya yak Object operator const Object amp tobto viklikayetsya A operator B viklikayetsya proste kopiyuvannya a ne konstruktor kopiyuvannya Inicializaciya Ob yekt mozhe buti inicializovanij u bud yakij sposib z nastupnih a Cherez ogoloshennya Object B A perekladayetsya yak Object Object const Object amp viklik konstruktora kopiyuvannya b Cherez argumenti funkciyi type function Object a c Cherez povernene znachennya Object a function Konstruktor kopiyuvannya vikoristovuyetsya viklyuchno dlya inicializaciyi i ne zastosovuyetsya dlya prisvoyuvannya de vikoristovuyetsya operator prisvoyuvannya natomist Neyavnij konstruktor kopiyuvannya klasu viklikaye bazovij konstruktor kopiyuvannya i kopiyuye chleni klasu pidhodyashim dlya yihnogo tipu chinom Yaksho ce tip klasu todi vidpovidnij konstruktor kopiyuvannya viklikayetsya yaksho ce skalyarnij tip vikoristovuyetsya vbudovanij operator prisvoyuvannya Nasamkinec yaksho ce masiv kozhen element kopiyuyetsya vidpovidno do jogo tipu Shlyahom viznachennya koristuvachevogo konstruktora kopiyuvannya programist mozhe zadati neobhidnu povedinku pri kopiyuvanni PrikladiNastupni prikladi pokazuyut yak pracyuyut konstruktori kopiyuvannya i navisho voni potribni Neyavnij konstruktor kopiyuvannya Rozglyanemo nastupnij priklad include lt iostream gt class Person public int age explicit Person int age age age int main Person timmy 10 Person sally 15 Person timmy clone timmy std cout lt lt timmy age lt lt lt lt sally age lt lt lt lt timmy clone age lt lt std endl timmy age 23 std cout lt lt timmy age lt lt lt lt sally age lt lt lt lt timmy clone age lt lt std endl return 0 Vivedennya 10 15 10 23 15 10 Yak i ochikuvalos timmy buv skopijovanij v novij ob yekt timmy clone I hocha vik timmy buv zminenij vik timmy clone zalishivsya tim samim Prichina cogo v tomu sho ce zovsim rizni ob yekti Kompilyator stvoriv konstruktor kopiyuvannya dlya nas i vin mozhe buti zapisanij tak Person const Person amp copy age copy age Tobto koli zh mi dijsno potrebuyemo koristuvachevogo konstruktora kopiyuvannya Nastupnij rozdil dast vidpovid na ce pitannya Koristuvackij konstruktor kopiyuvannya Teper uyavimo duzhe prostij klas dinamichnih masiviv include lt iostream gt class Array public int size int data explicit Array int size size size data new int size Array delete data int main Array first 20 first data 0 25 Array copy first std cout lt lt first data 0 lt lt lt lt copy data 0 lt lt std endl 1 first data 0 10 2 return 0 Vivedennya 25 25 Segmentation fault Cherez te sho mi ne viznachili konstruktor kopiyuvannya kompilyator stvoriv jogo dlya nas Stvorenij konstruktor mozhe viglyadati shozhim na nastupnij Array const Array amp copy size copy size data copy data U danomu razi konstruktor vikonav angl shallow copy vkazivnika data Vin skopiyuvav lishe adresu pervisnogo chlena danih tobto voni obidva vikoristovuyut vkazivnik na odnu dilyanku pam yati nam bi hotilos inshogo Koli programa dosyagaye ryadka 1 viklikayetsya destruktor kopiyi cherez avtomatichne znishennya ob yekta u steku pri vihodi z jogo oblasti vidimosti Destruktor masivu vidalyaye masiv data sho vikoristovuyetsya oboma ob yektami Pislya cogo na ryadku 2 mi namagayemos otrimati dostup do neisnuyuchogo ob yekta i zapisati tudi Ce viklikaye vidomu Yaksho mi napishemo vlasnij konstruktor kopiyuvannya yakij vikonuye gliboke kopiyuvannya todi mi pozbudemos ciyeyi problemi Array const Array amp copy size copy size data new int copy size std copy copy data copy data copy size data include lt algorithm gt dlya std copy Tut mi stvoryuyemo novij masiv i kopiyuyemo vmist u nogo Teper destruktor copy vidalit lishe vlasni dani i ne zachepit dani ob yekta first Ryadok 2 vidteper ne viklikatime pomilku segmentaciyi Zamist togo shob robiti gliboke kopiyuvanya odrazu mozhna vikoristati odin iz sposobiv optimizaciyi Voni dozvolyayut bezpechne spilne vikoristannya danih kilkoma ob yektami takim chinom zberigayuchi pam yat Strategiya kopiyuvannya pri zapisi robit kopiyu danih tilki pri sprobi zapisu Lichilnik posilan slidkuye za tim skilki ob yektiv posilayutsya na dani i znishuye yih tilki todi koli kilkist dorivnyuye nulyu napriklad boost shared ptr Konstruktori kopiyuvannya i shabloni Vsuperech ochikuvannyam shablonnij konstruktor kopiyuvannya ne ye koristuvachevim konstruktorom kopiyuvannya Tozh nedostatno mati template lt typename A gt Array Array const A amp copy size copy size data new int copy size std copy copy begin copy end data Zauvazhte sho tip A mozhe buti Array Koristuvachevij neshablonnij konstruktor kopiyuvannya maye buti viznachenij dlya konstruyuvannya Array na osnovi Array Yavnij konstruktor kopiyuvannyaYavnij kostruktor kopiyuvannya viznachayetsya za dopomogoyu klyuchovogo slova explicit Napriklad explicit X const X amp copyFromMe Vin vikoristovuyetsya dlya zapobigannya kopiyuvannyu ob yektiv pid chas vikliku funkcij abo pid chas inicializaciyi kopiyuvannyam X a X b a pomilka X b a virnoPrimitkiINCITS ISO IEC 14882 2003 12 8 2 1 8 chervnya 2007 u Wayback Machine ISO IEC 2003 ISO IEC 14882 2003 E Programming Languages C 8 5 Initializers dcl init para 12 INCITS ISO IEC 14882 2003 12 8 8 2 8 chervnya 2007 u Wayback Machine