Конструкторды көшіру (C ++) - Copy constructor (C++)

Ішінде C ++ бағдарламалау тілі, а көшірме конструкторы ерекше конструктор жаңа құру үшін объект көшірме ретінде бар объектінің. Көшіру конструкторлары - бұл C ++ тіліндегі объектілерді көшірудің стандартты тәсілі клондау, және C ++ - арнайы нюанстары бар.

Мұндай конструктордың бірінші аргументі - салынып жатқан типтегі объектіге сілтеме (const немесе const емес), оған кез-келген типтегі параметрлер қосылуы мүмкін (барлығы әдепкі мәндерге ие).

Әдетте құрастырушы автоматты түрде әрқайсысы үшін көшірме конструкторын жасайды сынып (белгілі жасырын көшірме конструкторы), бірақ ерекше жағдайлар үшін бағдарламашы а деп аталатын көшірме конструкторын жасайды пайдаланушы анықтаған көшірме конструкторы. Мұндай жағдайларда компилятор біреуін жасамайды. Демек, пайдаланушы немесе жүйе анықтайтын әрқашан бір көшірме конструкторы болады.

Пайдаланушы анықтаған көшірме конструкторы, әдетте, объект иелік еткен кезде қажет көрсеткіштер немесе ортақ емес сілтемелер сияқты, а файл, бұл жағдайда а деструктор және ан тағайындау операторы жазылуы керек (қараңыз) Үш ереже ).

Анықтама

Нысандарды көшіру көшірме конструкторын қолдану арқылы жүзеге асырылады тағайындау операторы. Көшірме конструкторының бірінші параметрі бар a (мүмкін const немесе тұрақсыз ) анықтама өзінің класс түріне. Оның көп аргументтері болуы мүмкін, ал қалғандарында олармен байланысты әдепкі мәндер болуы керек.[1] Келесі класс үшін жарамды көшірме конструкторлары болады X:

X(const X& көшірме_фигурасы);X(X& көшірме_фигурасы);X(тұрақсыз X& көшірме_фигурасы);X(const тұрақсыз X& көшірме_фигурасы);X(X& көшірме_фигурасы, int = 0);X(const X& көшірме_фигурасы, екі есе = 1.0, int = 42);...

Біріншісі, басқаларының біреуін қолдануға жеткілікті себеп болмаса, қолданылуы керек. Біріншісі мен екіншісінің айырмашылықтарының бірі - уақытша материалдарды біріншісіне көшіруге болады. Мысалға:

X а = X();     // берілген X жарамды (const X & copy_from_me), бірақ жарамсыз X (X & copy_from_me)               // өйткені екіншісі тұрақты емес X & қажет               // құру үшін, компилятор алдымен әдепкі конструкторды шақыру арқылы уақытша жасайды               // of X, содан кейін осы уақытша көшірме ретінде инициализациялау үшін көшірме конструкторын қолданады.                // Бағдарламаны орындау кезінде құрылған уақытша нысандар әрқашан const типінде болады. Сонымен, const кілт сөзі қажет.               // Кейбір компиляторлар үшін екі нұсқа да жұмыс істейді, бірақ мұндай әрекетке сенуге болмайды                // өйткені стандартты емес.

Олардың тағы бір айырмашылығы айқын:

const X а;X б = а;       // берілген X жарамды (const X & copy_from_me), бірақ жарамсыз X (X & copy_from_me)               // өйткені екіншісі тұрақты емес X & қажет

The X & көшірме конструкторының формасы көшірілген нысанды өзгерту қажет болған кезде қолданылады. Бұл өте сирек кездеседі, бірақ оны стандартты кітапханада қолдануға болады std :: auto_ptr. Анықтама ұсынылуы керек:

X а;X б = а;       // егер кез-келген көшірме конструкторы анықталса, жарамды               // сілтеме жіберіліп жатқандықтан.

Келесі жарамсыз көшірме конструкторлары (Себеп - көшірме_фигурасы сілтеме ретінде берілмеген):

X(X көшірме_фигурасы);X(const X көшірме_фигурасы);

өйткені сол конструкторларға қоңырау шексіз рекурсивті шақыруға әкелетін көшірмені де қажет етеді.

Келесі жағдайлар көшірме конструкторына қоңырау шалуы мүмкін:

  1. Нысан мәні қайтарылған кезде
  2. Нысан аргумент ретінде мәні бойынша берілгенде (функцияға)
  3. Нысан лақтырылған кезде
  4. Нысан ұсталғанда
  5. Нысан жақшаға енгізілген инициализатор тізіміне орналастырылған кезде

Бұл жағдайлар жиынтық деп аталады көшірме-инициализация және келесіге тең:[2]T x = a;

Бұл жағдайда көшірме конструкторының шақырылатынына кепілдік берілмейді, өйткені C ++ стандарты кейбір жағдайларда компиляторға көшірмені оңтайландыруға мүмкіндік береді, мысалы қайтару мәнін оңтайландыру (кейде RVO деп аталады).

Пайдалану

Нысанға екі техниканың бірін қолданып мән беруге болады:

  • Өрнектегі нақты тапсырма
  • Инициализация

Өрнектегі нақты тапсырма

Нысан а;Нысан б;а = б;       // Object :: operator = (const Object &) деп аударылады, осылайша a.operator = (b) деп аталады              // (қарапайым көшірмені шақырыңыз, конструкторды көшірмеңіз!)

Инициализация

Нысанды келесі жолдардың кез-келгенімен инициализациялауға болады.

а. Декларация арқылы

Нысан б = а; // Object :: Object (const Object &) деп аударылады (көшірме конструкторын шақыру)

б. Функция аргументтері арқылы

түрі функциясы(Нысан а);

c. Функцияның қайтару мәні арқылы

Нысан а = функциясы();

Көшіру конструкторы тек инициализация үшін қолданылады, ал оның орнына тағайындау операторы қолданылатын тапсырмаларға қолданылмайды.

Кластың жасырын көшірме конструкторы негізгі көшірме конструкторларын шақырады және олардың мүшелерін олардың типтеріне сәйкес тәсілдермен көшіреді. Егер бұл класс типі болса, көшірме конструкторы деп аталады. Егер бұл скалярлық тип болса, кіріктірілген тағайындау операторы қолданылады. Сонымен, егер бұл массив болса, әр элемент оның түріне сәйкес түрде көшіріледі.[3]

Пайдаланушы анықтайтын көшірме конструкторын қолдану арқылы бағдарламашы объект көшірілген кезде орындалатын әрекетті анықтай алады.

Мысалдар

Бұл мысалдар көшірме конструкторларының қалай жұмыс істейтінін және неге кейде олар қажет болатындығын көрсетеді.

Жасырын көшірме конструкторы

Келесі мысалды қарастырайық:

# қосу <iostream>сынып Адам { қоғамдық:  айқын Адам(int жас) : жас(жас) {}  int жас;};int негізгі() {  Адам күңгірт(10);  Адам салли(15);  Адам timmy_clone = күңгірт;  std::cout << күңгірт.жас << " " << салли.жас << " " << timmy_clone.жас            << std::соңы;  күңгірт.жас = 23;  std::cout << күңгірт.жас << " " << салли.жас << " " << timmy_clone.жас            << std::соңы;}

Шығу

10 15 1023 15 10

Күткендей, күңгірт жаңа нысанға көшірілді, timmy_clone. Әзірге Тимми жас өзгертілді, timmy_clone's жас өзгеріссіз қалды. Себебі олар мүлдем басқа нысандар.

Компилятор бізге көшірме конструкторын жасады және оны келесідей жазуға болады:

Адам(const Адам& басқа)     : жас(басқа.жас)  // Дәуірдің көшірме конструкторын шақырады.{}

Сонымен, бізге пайдаланушы анықтайтын көшірме конструкторы қашан қажет? Келесі бөлімде осы сұрақ қарастырылады.

Пайдаланушы анықтаған көшірме конструкторы

Өте қарапайымды қарастырайық динамикалық массив келесідей сынып:

# қосу <iostream>сынып Массив { қоғамдық:  айқын Массив(int өлшемі) : өлшемі(өлшемі), деректер(жаңа int[өлшемі]) {}  ~Массив() {    егер (деректер != nullptr) {      жою[] деректер;    }  }  int өлшемі;  int* деректер;};int негізгі() {  Массив бірінші(20);  бірінші.деректер[0] = 25;  {    Массив көшірме = бірінші;    std::cout << бірінші.деректер[0] << " " << көшірме.деректер[0] << std::соңы;  }  // (1)  бірінші.деректер[0] = 10;  // (2)}

Шығу

25 25 Сегментация ақаулығы

Көшірме конструкторын көрсетпегендіктен, компилятор бізге біреуін жасады. Құрылған конструктор келесідей көрінуі мүмкін:

Массив(const Массив& басқа)  : өлшемі(басқа.өлшемі), деректер(басқа.деректер) {}

Бұл конструктордың проблемасы оның а таяз көшірме туралы деректер көрсеткіш. Ол тек бастапқы деректер мүшесінің мекен-жайын көшіреді; бұл дегеніміз, екеуі де бірдей жадқа көрсеткішті бөліседі, бұл біз қалағандай емес. Бағдарлама жолға жеткенде (1), көшірме деструктор шақырылады (өйткені стектегі объектілер олардың аяғы аяқталған кезде автоматты түрде жойылады). Массивтікі жойғыш деректер массив түпнұсқа, сондықтан оны жойған кезде көшірме деректер, өйткені олар бірдей көрсеткішті бөліседі, сондықтан ол да жойылды бірінші деректер. Түзу (2) енді жарамсыз деректерге қол жеткізіп, оларға жазады! Бұл атақты шығарады сегментация ақаулығы.

Егер біз а-ны орындайтын өзіндік көшірме конструкторын жазсақ терең көшірме онда бұл мәселе жойылады.

// std :: copy үшін# қосу <algorithm>Массив(const Массив& басқа)    : өлшемі(басқа.өлшемі), деректер(жаңа int[басқа.өлшемі]) {  std::көшірме(басқа.деректер, басқа.деректер + басқа.өлшемі, деректер); }

Міне, біз жаңасын жасап жатырмыз int массив және оған мазмұнды көшіру. Енді, басқалар деструктор тек оның деректерін ғана жояды, ал емес бірінші деректер. Түзу (2) енді сегменттеу ақаулығын тудырмайды.

Терең көшірмені бірден жасаудың орнына кейбір оңтайландыру стратегияларын қолдануға болады. Бұл бірнеше объектілер арасында бірдей деректерді қауіпсіз бөлісуге мүмкіндік береді, осылайша кеңістікті үнемдейді. The жазбаға көшіру стратегия деректердің көшірмесін олар жазылған кезде ғана жасайды. Анықтамалық санау деректердің қанша объектіге сілтеме жасайтындығының есебін жүргізеді және тек осы сан нөлге жеткенде ғана жояды (мысалы.). boost :: shared_ptr).

Конструкторлар мен шаблондарды көшіру

Күтуге қарама-қарсы шаблон көшірмесінің конструкторы қолданушы анықтаған көшірме конструкторы емес. Осылайша, тек:

шаблон <жазу аты A> Массив::Массив(A const& басқа)    : өлшемі(басқа.өлшемі()), деректер(жаңа int[басқа.өлшемі()]) {  std::көшірме(басқа.баста(), басқа.Соңы(), деректер);}

(Түріне назар аударыңыз A бола алады Массив.) Массивтен массив құру үшін пайдаланушы анықтаған, шаблонға жатпайтын көшірме конструкторы ұсынылуы керек.

Биттік көшірме конструкторы

C ++ тілінде «биттік көшірме конструкторы» деген түсінік жоқ. Алайда, әдепкі бойынша құрылған көшірме конструкторы мүшелерге көшірме конструкторларын шақыру арқылы көшіріледі, ал шикі сілтеме мүшесі үшін бұл шикі көрсеткішті көшіреді (яғни терең көшірме емес).

Логикалық көшірме конструкторы

Логикалық көшірме конструкторында мәндерді көшірумен қатар көрсеткіш үшін жаңа динамикалық мүшенің айнымалысы жасалынатындығын көруге болады. [4]

Логикалық көшірме конструкторы құрылымның және оның динамикалық құрылымдарының шын көшірмесін жасайды. Логикалық көшірме конструкторлары суретке негізінен көшірілетін объектінің ішінде көрсеткіштер немесе күрделі объектілер болған кезде түседі.

Айқын көшірме конструкторы

Көшірменің нақты конструкторы - көмегімен ашық деп жарияланады айқын кілт сөз. Мысалға:

айқын X(const X& көшірме_фигурасы);

Ол функционалды шақырулар кезінде немесе көшіру-инициализация синтаксисімен объектілерді көшірудің алдын алу үшін қолданылады.

Сондай-ақ қараңыз

Әдебиеттер тізімі

  1. ^ ISO IEC 14882-2003 шақырады 12.8.2. [1] Мұрағатталды 8 маусым 2007 ж Wayback Machine
  2. ^ ISO /IEC (2003). ISO / IEC 14882: 2003 (E): бағдарламалау тілдері - C ++ §8.5 инициализаторлар [dcl.init] параграф. 12
  3. ^ ISO IEC 14882-2003 12.8.8 шақырады. [2] Мұрағатталды 8 маусым 2007 ж Wayback Machine
  4. ^ Информатика С ++ көмегімен құрылымдалған тәсіл, Бехруз А.Фурузан және Ричард Ф.Гилберг, 10-9 сурет, 507 бет

Сыртқы сілтемелер