Що таке правило трьох?


2145
  • Що означає копіювання об'єкта ?
  • Що таке конструктор копій та оператор призначення копії ?
  • Коли мені потрібно оголосити їх самостійно?
  • Як я можу запобігти їх копіювання?

52
Будь ласка , прочитайте всю цю тему і в c++-faqтеги вікі , перш ніж голосувати , щоб закрити .
sbi

13
@Binary: Принаймні знайдіть час, щоб прочитати обговорення коментарів, перш ніж ви проголосуєте. Текст був набагато простішим, але Фреду було запропоновано розширити його. Крім того, хоча це граматично чотири запитання , це справді лише одне питання з декількома аспектами. (Якщо ви не згодні з цим, тоді доведіть свою POV, відповідаючи на кожне з цих питань самостійно і дайте нам голосувати за результати.)
sbi

1
Фред, ось цікаве доповнення до вашої відповіді щодо C ++ 1x: stackoverflow.com/questions/4782757 / ... . Як ми маємо справу з цим?
sbi


4
Майте на увазі, що станом на C ++ 11, я думаю, що це було покращено до правила п'яти, або щось подібне.
paxdiablo

Відповіді:


1793

Вступ

C ++ обробляє змінні визначених користувачем типів із значенням семантики . Це означає, що об'єкти неявно копіюються в різних контекстах, і ми повинні розуміти, що насправді означає "копіювання об'єкта".

Розглянемо простий приклад:

class person
{
    std::string name;
    int age;

public:

    person(const std::string& name, int age) : name(name), age(age)
    {
    }
};

int main()
{
    person a("Bjarne Stroustrup", 60);
    person b(a);   // What happens here?
    b = a;         // And here?
}

(Якщо name(name), age(age)частина вас спантеличує , це називається списком ініціалізаторів членів .)

Спеціальні функції членів

Що означає копіювати personоб’єкт? mainФункція показує два різні сценарії копіювання. Ініціалізація person b(a);виконується конструктором копіювання . Його завдання - побудувати свіжий об’єкт, виходячи зі стану існуючого об’єкта. Присвоєння b = aвиконує оператор присвоєння копії . Його робота, як правило, трохи складніша, тому що цільовий об’єкт вже знаходиться в якомусь дійсному стані, з яким потрібно вирішуватись.

Оскільки ми самі не оголосили ані конструктора копіювання, ані оператора присвоєння (ані деструктора), це для нас неявно визначено. Цитата від стандарту:

Конструктор копій та оператор призначення копії, [...] та деструктор - це спеціальні функції членів. [ Примітка : Реалізація неявно оголошує ці функції членів для деяких типів класів, коли програма не оголошує їх явно. Реалізація неявно визначатиме їх, якщо вони будуть використані. [...] кінцева примітка ] [n3126.pdf розділ 12 §1]

За замовчуванням копіювання об’єкта означає копіювання його членів:

Неявно визначений конструктор копій для несоюзного класу X виконує копіювання його суб'єктів, що належать до нього. [n3126.pdf розділ 12.8 §16]

Определено неявно визначений оператор присвоєння копії для несоюзного класу X виконує присвоєння ним копії в суб'єктах. [n3126.pdf розділ 12.8 §30]

Неявні визначення

Неявно визначені функції спеціального члена personвиглядають так:

// 1. copy constructor
person(const person& that) : name(that.name), age(that.age)
{
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    name = that.name;
    age = that.age;
    return *this;
}

// 3. destructor
~person()
{
}

Копіювання в національному порядку - це саме те, що ми хочемо в цьому випадку: nameі ageкопіюються, тому ми отримуємо самостійний незалежний personоб'єкт. Неявно визначений деструктор завжди порожній. Це також добре в цьому випадку, оскільки ми не набули жодних ресурсів у конструкторі. Деструктори членів неявно викликаються після закінчення personдеструктора:

Після виконання корпусу деструктора та знищення будь-яких автоматичних об'єктів, виділених всередині тіла, деструктор класу X викликає деструктори для безпосередніх [...] членів X [n3126.pdf 12.4 §6]

Управління ресурсами

Тож коли ми повинні чітко заявляти про ці спеціальні функції членів? Коли наш клас керує ресурсом , тобто коли об’єкт класу відповідає за цей ресурс. Зазвичай це означає, що ресурс накопичується в конструкторі (або передається в конструктор) і випускається в деструктор.

Повернемося в часі до попереднього стандарту C ++. Такого не було std::string, і програмісти були закохані у вказівники. personКлас міг виглядати наступним чином :

class person
{
    char* name;
    int age;

public:

    // the constructor acquires a resource:
    // in this case, dynamic memory obtained via new[]
    person(const char* the_name, int the_age)
    {
        name = new char[strlen(the_name) + 1];
        strcpy(name, the_name);
        age = the_age;
    }

    // the destructor must release this resource via delete[]
    ~person()
    {
        delete[] name;
    }
};

Навіть сьогодні люди все ще пишуть заняття в цьому стилі і потрапляють у неприємності: « Я штовхнув людину у вектор, і тепер я отримую шалені помилки пам’яті! » Пам’ятайте, що за замовчуванням копіювання об’єкта означає копіювання його членів, а копіювання nameчлена просто копіює покажчик, а не масив символів, на який він вказує! Це має кілька неприємних наслідків:

  1. Зміни через aможна спостерігати через b.
  2. Після bзнищення a.name- це звисаючий покажчик.
  3. Якщо aйого знищити, видалення звисаючого покажчика призводить до невизначеної поведінки .
  4. Оскільки в призначенні не враховується те, на що nameвказувалося перед призначенням, рано чи пізно ви отримаєте витоки пам'яті в усьому місці.

Явні визначення

Оскільки копіювання в контингенті не дає бажаного ефекту, ми повинні чітко визначити конструктор копій та оператор призначення копії, щоб зробити глибокі копії масиву символів:

// 1. copy constructor
person(const person& that)
{
    name = new char[strlen(that.name) + 1];
    strcpy(name, that.name);
    age = that.age;
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    if (this != &that)
    {
        delete[] name;
        // This is a dangerous point in the flow of execution!
        // We have temporarily invalidated the class invariants,
        // and the next statement might throw an exception,
        // leaving the object in an invalid state :(
        name = new char[strlen(that.name) + 1];
        strcpy(name, that.name);
        age = that.age;
    }
    return *this;
}

Зверніть увагу на різницю між ініціалізацією та призначенням: перед тим, як призначити, nameщоб запобігти витоку пам'яті, ми повинні зруйнувати старий стан . Також ми маємо захищатись від самопризначення форми x = x. Без цієї перевірки delete[] nameбуде видалено масив, що містить рядок джерела , тому що, коли ви пишете x = x, обидва this->nameі that.nameмістять однаковий покажчик.

Виняток безпеки

На жаль, це рішення не вдасться, якщо new char[...]викине виняток через виснаження пам'яті. Одне можливе рішення - ввести локальну змінну та змінити порядок операторів:

// 2. copy assignment operator
person& operator=(const person& that)
{
    char* local_name = new char[strlen(that.name) + 1];
    // If the above statement throws,
    // the object is still in the same state as before.
    // None of the following statements will throw an exception :)
    strcpy(local_name, that.name);
    delete[] name;
    name = local_name;
    age = that.age;
    return *this;
}

Це також забезпечує самопризначення без явної перевірки. Ще більш надійним рішенням цієї проблеми є ідіома копіювання та заміни , але я не буду тут вникати в деталі безпеки виключень. Я лише згадав про винятки, щоб зробити наступне: Класи написання, які керують ресурсами, важкі.

Некопіювані ресурси

Деякі ресурси не можна або не слід копіювати, наприклад, ручки файлів або мутекси. У такому випадку просто оголосіть конструктор копій та оператор призначення копії як privateбез визначення:

private:

    person(const person& that);
    person& operator=(const person& that);

Крім того, ви можете успадкувати їх boost::noncopyableабо оголосити їх видаленими (на C ++ 11 і вище):

person(const person& that) = delete;
person& operator=(const person& that) = delete;

Правило трьох

Іноді потрібно реалізувати клас, який керує ресурсом. (Ніколи не керуйте кількома ресурсами в одному класі, це призведе лише до болю.) У цьому випадку запам’ятайте правило трьох :

Якщо вам потрібно чітко оголосити або деструктора, конструктора копіювання або оператора присвоєння копії самостійно, вам, ймовірно, потрібно чітко заявити про всі три з них.

(На жаль, це "правило" не застосовується стандартом C ++ чи будь-яким мені компілятором.)

Правило п'яти

Починаючи з C ++ 11, об’єкт має 2 додаткові функції спеціального члена: конструктор переміщення та призначення переміщення. Правило п'яти держав здійснювати і ці функції.

Приклад із підписами:

class person
{
    std::string name;
    int age;

public:
    person(const std::string& name, int age);        // Ctor
    person(const person &) = default;                // Copy Ctor
    person(person &&) noexcept = default;            // Move Ctor
    person& operator=(const person &) = default;     // Copy Assignment
    person& operator=(person &&) noexcept = default; // Move Assignment
    ~person() noexcept = default;                    // Dtor
};

Правило нуля

Правило 3/5 також називається правилом 0/3/5. Нульова частина правила вказує, що ви можете не писати жодної функції спеціального члена під час створення свого класу.

Поради

Більшу частину часу вам не потрібно самостійно керувати ресурсом, тому що існуючий клас на зразок std::stringвже робить це за вас. Просто порівняйте простий код за допомогою std::stringчлена з перекрученою та схильною до помилок альтернативою з використанням a, char*і ви повинні переконатися. Поки ви тримаєтесь подалі від необроблених членів вказівника, правило трьох навряд чи стосуватиметься вашого власного коду.


4
Фреде, я б почував себе краще під час свого голосування, якби (А) ви не прописали неправильно виконане завдання в копіюваному коді та додали примітку, вказуючи, що це неправильно, і шукати в іншому місці в тонкому відбитку; або використовувати c & s у коді, або просто пропустити впровадження всіх цих членів (B), ви б скоротили першу половину, що мало стосується RoT; (C) Ви б обговорили запровадження семантики руху та що це означає для RoT.
sbi

7
Але тоді посаду треба робити C / W, я думаю. Мені подобається, що ви зберігаєте терміни в основному точні (тобто, ви говорите " оператор присвоєння копії ", і щоб ви не потрапляли в загальну пастку, щоб призначення не означало копію).
Йоханнес Шауб - ліб

4
@Prasoon: Я не думаю, що вирізання половини відповіді буде розглядатися як "чесне редагування" відповіді, що не відповідає CW.
sbi

69
Було б чудово, якби ви оновили свою посаду для C ++ 11 (тобто перемістити конструктор / завдання)
Олександр Малахов

5
@solalito Все, що потрібно випустити після використання: блокування одночасності, ручки файлів, підключення до бази даних, мережеві розетки, купа пам’яті…
fredoverflow

509

Правило трьох є правилом для C ++, в основному говорять ,

Якщо ваш клас потребує будь-якого з

  • конструктор копіювання ,
  • оператор присвоювання ,
  • або деструктора ,

визначено експліцитно, то, ймовірно, знадобляться всі троє .

Причини цього полягають у тому, що всі троє зазвичай використовуються для управління ресурсом, і якщо ваш клас управляє ресурсом, йому зазвичай потрібно керувати копіюванням, а також звільненням.

Якщо немає хорошої семантики для копіювання ресурсу, яким керує ваш клас, то розгляньте можливість заборонити копіювання, оголосивши (не визначаючи ) конструктор копій та оператор присвоєння як private.

(Зауважте, що наступна нова версія стандарту C ++ (а це C ++ 11) додає семантику переміщення до C ++, що, ймовірно, змінить Правило 3. Але я знаю надто мало про це, щоб написати розділ C ++ 11 про правило трьох.)


3
Іншим рішенням для запобігання копіювання є успадкування (приватно) від класу, який неможливо скопіювати (як boost::noncopyable). Це також може бути набагато зрозуміліше. Я думаю, що C ++ 0x і можливість "видалити" функції можуть тут допомогти, але забув синтаксис: /
Matthieu M.

2
@Matthieu: Так, це теж працює. Але якщо noncopyableвона не є частиною std lib, я не вважаю це великим вдосконаленням. (О, і якщо ви забули синтаксис видалення, ви забули mor ethan, що я коли-небудь знав. :))
sbi

3
@Daan: Дивіться цю відповідь . Тим НЕ менше, я рекомендую дотримуватися Martinho «s Правило Зеро . На мій погляд, це одне з найважливіших правил роботи для C ++, винайденого за останнє десятиліття.
sbi

3
Правило Мартінхо «Правило нуля» тепер краще (без очевидних захоплень рекламного ПЗ) розміщене на archive.org
Натан Кідд

161

Закон великої трійки визначений вище.

Простий приклад простої англійської мови про проблему, яку вона вирішує:

Неруйнівний деструктор

Ви виділили пам'ять у своєму конструкторі, і тому вам потрібно написати деструктор, щоб видалити його. Інакше ви спричинить витік пам'яті.

Ви можете подумати, що це робота зроблена.

Проблема буде в тому випадку, якщо копія зроблена з вашого об’єкта, то копія буде вказувати на ту саму пам'ять, що і вихідний об'єкт.

Після того, як один з них видаляє пам'ять у своєму деструкторі, інший матиме вказівник на недійсну пам'ять (це називається звисаючим вказівником), коли він намагатиметься використовувати її, речі стають волохатими.

Тому ви пишете конструктор копій, щоб він виділяв новим об’єктам власні фрагменти пам'яті для знищення.

Оператор призначення та конструктор копій

Ви виділили пам'ять у своєму конструкторі вказівнику вашого класу. Коли ви копіюєте об'єкт цього класу, оператор присвоєння за замовчуванням та конструктор копій копіюють значення цього вказівника на новий об'єкт.

Це означає, що новий об'єкт і старий об'єкт будуть вказувати на один і той же фрагмент пам'яті, тому при зміні його в одному об'єкт він буде змінений і на інший об'єкт. Якщо один об'єкт видаляє цю пам'ять, інший буде продовжувати спробувати використовувати його - eek.

Для вирішення цього питання ви пишете власну версію конструктора копіювання та оператора присвоєння. Ваші версії виділяють окрему пам'ять новим об'єктам і копіюють значення, на які вказує перший вказівник, а не його адресу.


4
Отже, якщо ми використовуємо конструктор копій, тоді копія робиться, але взагалі в іншому місці пам'яті, і якщо ми не використовуємо конструктор копій, тоді копія робиться, але вона вказує на те саме місце пам'яті. це те, що ти намагаєшся сказати? Таким чином, копія без конструктора копій означає, що новий покажчик буде там, але вказує на те саме місце пам’яті, однак якщо у нас конструктор копій чітко визначений користувачем, у нас буде окремий вказівник, що вказує на інше місце пам’яті, але має дані.
Незламний

4
Вибачте, я відповів на це століття тому, але моєї відповіді, здається, все ще немає :-( В основному, так - ви отримуєте це :-)
Стефан

1
Яким чином цей принцип відповідає оператору призначення копії? Ця відповідь була б кориснішою, якби згадувались третій у Правилі трьох.
DBedrenko

1
@DBedrenko, "ви пишете конструктор копій, щоб він виділяв новим об'єктам власні фрагменти пам'яті ..." Це той самий принцип, який поширюється на оператора призначення копії. Ви не думаєте, що я це зрозумів?
Стефан

2
@DBedrenko, я додав ще трохи інформації. Це робить це зрозумілішим?
Стефан

44

В основному, якщо у вас є деструктор (не деструктор за замовчуванням), це означає, що визначений вами клас має деякий розподіл пам'яті. Припустимо, що клас використовується зовні за допомогою якогось коду клієнта чи ви.

    MyClass x(a, b);
    MyClass y(c, d);
    x = y; // This is a shallow copy if assignment operator is not provided

Якщо у MyClass є лише деякі примітивні типізовані члени, оператор присвоєння за замовчуванням працював би, але якщо у нього є деякі члени вказівника та об'єкти, які не мають операторів призначення, результат був би непередбачуваним. Тому ми можемо сказати, що якщо деструктор класу є чим видалити, нам може знадобитися оператор глибокої копії, що означає, що ми повинні забезпечити конструктор копій та оператор присвоєння.


36

Що означає копіювання об'єкта? Існує кілька способів копіювання об'єктів - поговоримо про два види, які ви, швидше за все, маєте на увазі - глибока копія та неглибока копія.

Оскільки ми є об'єктно-орієнтованою мовою (або принаймні припускаємо це), скажімо, у вас виділений фрагмент пам'яті. Оскільки це є мовою ОО, ми можемо легко посилатися на фрагменти пам’яті, які ми виділяємо, оскільки вони, як правило, примітивні змінні (ints, символи, байти) або класи, які ми визначили, створені з наших власних типів та примітивів. Тож скажімо, у нас такий клас автомобіля:

class Car //A very simple class just to demonstrate what these definitions mean.
//It's pseudocode C++/Javaish, I assume strings do not need to be allocated.
{
private String sPrintColor;
private String sModel;
private String sMake;

public changePaint(String newColor)
{
   this.sPrintColor = newColor;
}

public Car(String model, String make, String color) //Constructor
{
   this.sPrintColor = color;
   this.sModel = model;
   this.sMake = make;
}

public ~Car() //Destructor
{
//Because we did not create any custom types, we aren't adding more code.
//Anytime your object goes out of scope / program collects garbage / etc. this guy gets called + all other related destructors.
//Since we did not use anything but strings, we have nothing additional to handle.
//The assumption is being made that the 3 strings will be handled by string's destructor and that it is being called automatically--if this were not the case you would need to do it here.
}

public Car(const Car &other) // Copy Constructor
{
   this.sPrintColor = other.sPrintColor;
   this.sModel = other.sModel;
   this.sMake = other.sMake;
}
public Car &operator =(const Car &other) // Assignment Operator
{
   if(this != &other)
   {
      this.sPrintColor = other.sPrintColor;
      this.sModel = other.sModel;
      this.sMake = other.sMake;
   }
   return *this;
}

}

Глибока копія - якщо ми оголосимо об'єкт, а потім створимо абсолютно окрему копію об'єкта ... ми закінчимось 2 об’єктами в 2 повністю наборах пам'яті.

Car car1 = new Car("mustang", "ford", "red");
Car car2 = car1; //Call the copy constructor
car2.changePaint("green");
//car2 is now green but car1 is still red.

Тепер давайте зробимо щось дивне. Скажімо, car2 або запрограмований неправильно, або навмисно призначений для обміну фактичною пам'яттю, з якої складається car1. (Зазвичай це помилка. У класах це звичайно ковдра, про яку йдеться.) Робіть вигляд, що щоразу, коли ви запитаєте про car2, ви дійсно вирішуєте вказівник на простір пам’яті car1 ... це більш-менш яка дрібна копія є.

//Shallow copy example
//Assume we're in C++ because it's standard behavior is to shallow copy objects if you do not have a constructor written for an operation.
//Now let's assume I do not have any code for the assignment or copy operations like I do above...with those now gone, C++ will use the default.

 Car car1 = new Car("ford", "mustang", "red"); 
 Car car2 = car1; 
 car2.changePaint("green");//car1 is also now green 
 delete car2;/*I get rid of my car which is also really your car...I told C++ to resolve 
 the address of where car2 exists and delete the memory...which is also
 the memory associated with your car.*/
 car1.changePaint("red");/*program will likely crash because this area is
 no longer allocated to the program.*/

Тож незалежно від того, якою мовою ви пишете, будьте дуже уважні до того, що ви маєте на увазі, коли йдеться про копіювання об'єктів, оскільки більшу частину часу ви хочете глибокої копії.

Що таке конструктор копій та оператор призначення копії? Я вже використовував їх вище. Конструктор копіювання викликається при введенні коду, такого як Car car2 = car1; Essential, якщо ви оголошуєте змінну і призначаєте її в одному рядку, саме тоді викликається конструктор копіювання. Оператор присвоєння - це те, що відбувається, коли ви використовуєте знак рівності-- car2 = car1;. Зауважтеcar2 не оголошується в одній заяві. Два фрагменти коду, які ви пишете для цих операцій, ймовірно, дуже схожі. Насправді типова модель дизайну має ще одну функцію, яку ви закликаєте встановити все, як тільки ви задоволені, початкова копія / призначення є законною - якщо ви подивитесь на написаний вами кодекс, функції майже однакові.

Коли мені потрібно оголосити їх самостійно? Якщо ви не пишете код, який потрібно поділитись або якийсь спосіб виготовлення, вам потрібно оголосити їх лише тоді, коли вони вам потрібні. Вам потрібно знати, що робить ваша програмна мова, якщо ви вирішили використовувати її "випадково", а не зробили її - тобто ви отримаєте компілятор за замовчуванням. Наприклад, я рідко використовую конструктори копій, але скасування операторів присвоєння дуже часто. Чи знаєте ви, що ви можете перекрити, що означають також додавання, віднімання тощо?

Як я можу запобігти їх копіювання? Переосмислити всі способи, яким вам дозволяється виділити пам'ять для вашого об'єкта приватною функцією, є розумним початком. Якщо ви дійсно не хочете, щоб люди їх копіювали, ви можете оприлюднити їх і попередити програміста, кинувши виняток, а також не скопіювавши об'єкт.


5
Запитання було позначене C ++. Ця псевдо-кодова виставка мало що дозволяє прояснити що-небудь про чітко визначене "правило трьох" в кращому випадку і просто поширює плутанину в гіршому випадку.
sehe

26

Коли мені потрібно оголосити їх самостійно?

Правило трьох зазначає, що якщо ви оголосите будь-яке з

  1. конструктор копій
  2. оператор присвоєння копії
  3. руйнівник

тоді слід оголосити всіх трьох. Це випливало із спостереження, що необхідність перейняти значення операції копіювання майже завжди випливала з класу, який здійснює якесь управління ресурсами, і що майже завжди передбачає, що

  • що б управління ресурсами не робилося в одній операції з копіюванням, можливо, це було потрібно зробити в іншій операції копіювання та

  • деструктор класу також братиме участь в управлінні ресурсом (зазвичай його випускають). Класичним ресурсом, яким слід керувати, була пам’ять, і саме тому всі класи Стандартної бібліотеки, що управляють пам'яттю (наприклад, контейнери STL, які виконують динамічне управління пам'яттю), всі оголошують "велику трійку": і операції копіювання, і деструктор.

Наслідком Правила трьох є те, що наявність оголошеного користувачем деструктора вказує на те, що проста копія учасника навряд чи буде придатною для операцій з копіювання в класі. Це, в свою чергу, говорить про те, що якщо клас оголошує деструктор, операції копіювання, ймовірно, не повинні генеруватися автоматично, оскільки вони не зробили б правильно. На той час, коли C ++ 98 був прийнятий, значимість цього аргументу не була повністю оцінена, тому в C ++ 98 існування оголошеного користувачем деструктора не впливало на готовність компіляторів генерувати операції копіювання. Це продовжує відбуватися в C ++ 11, але лише тому, що обмеження умов, за яких створюються операції копіювання, порушить занадто багато застарілого коду.

Як я можу запобігти їх копіювання?

Заявіть конструктор копій та оператор присвоєння копії як приватний специфікатор доступу.

class MemoryBlock
{
public:

//code here

private:
MemoryBlock(const MemoryBlock& other)
{
   cout<<"copy constructor"<<endl;
}

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other)
{
 return *this;
}
};

int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}

В C ++ 11 далі ви також можете оголосити конструктор копії та оператор призначення видаленим

class MemoryBlock
{
public:
MemoryBlock(const MemoryBlock& other) = delete

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other) =delete
};


int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}

16

Багато з існуючих відповідей вже стосуються конструктора копіювання, оператора присвоєння та деструктора. Однак у пост C ++ 11 введення семантичного руху може розширити це за межі 3.

Нещодавно Майкл Клайсз виступив із доповіддю, яка стосується цієї теми: http://channel9.msdn.com/events/CPP/C-PP-Con-2014/The-Canonical-Class


10

Правило трьох в C ++ є основоположним принципом розробки та розробки трьох вимог, що якщо є чітке визначення в одній з наступних функцій-членів, програміст повинен визначати інші два члени функції. А саме такі три функції-члена незамінні: деструктор, конструктор копій, оператор присвоєння копії.

Конструктор копіювання в C ++ - це спеціальний конструктор. Він використовується для побудови нового об'єкта, який є новим об'єктом, еквівалентним копії існуючого об'єкта.

Оператор присвоєння копій - це спеціальний оператор присвоєння, який зазвичай використовується для вказівки існуючого об'єкта іншим об'єктам одного типу.

Є короткі приклади:

// default constructor
My_Class a;

// copy constructor
My_Class b(a);

// copy constructor
My_Class c = a;

// copy assignment operator
b = a;

7
Привіт, ваша відповідь не додає нічого нового. Інші висвітлюють тему набагато більше глибин, а точніше - ваша відповідь у деяких місцях приблизна і насправді неправильна (а саме тут немає «обов’язково»; це «дуже ймовірно, що слід»). Насправді не варто вашої публікації відповіді на запитання, на які вже ґрунтовно відповіли. Якщо у вас немає нових речей для додання.
Мат

1
Також є чотири швидкі приклади, які так чи інакше пов'язані з двома з трьох , про які говорить Правило трьох. Занадто багато плутанини.
anatolyg
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.