С++. Класи. Частина 2. Конструктор копіювання. Приклади використання. Передача об’єкту класу в функцію. Повернення класу з функції
У даній темі розглянуто роботу конструктора копіювання на прикладі unmanaged (native) класів. При розгляді даної теми рекомендується прочитати тему:
Зміст
- 1. Що таке конструктор копіювання (copy constructor)?
- 2. У яких випадках викликається конструктор копіювання?
- 3. У яких випадках доцільно використовувати конструктор копіювання?
- 4. Приклад оголошення конструктора копіювання для класу, в якому немає динамічного виділення пам’яті
- 5. Приклад передачі об’єкту класу у функцію як параметр-значення
- 6. Приклад повернення об’єкту класу з функції за значенням з допомогою конструктора копіювання
- 7. Як здійснюється копіювання об’єктів, якщо конструктор копіювання не оголошений у класі?
- Зв’язані теми
Пошук на інших ресурсах:
1. Що таке конструктор копіювання (copy constructor)?
Конструктор копіювання – це конструктор, який дозволяє отримати ідентичний до заданого об’єкт. Тобто, з допомогою конструктора копіювання можна отримати копію вже існуючого об’єкту. Конструктор копіювання ще називається ініціалізатором копії (copy initializer). Конструктор копіювання повинен отримувати вхідним параметром константне посилання (&) на об’єкт такого самого класу.
⇑
2. У яких випадках викликається конструктор копіювання?
Конструктор копіювання викликається у випадках, коли потрібно отримати повну копію об’єкту. В C++ необхідність отримання повної копії об’єкту можлива у трьох випадках.
Випадок 1. При оголошенні нового об’єкту та його ініціалізації даними іншого об’єкту з допомогою оператора =. Наступний фрагмент коду демонструє дану ситуацію для деякого класу ClassName
// оголошення екземпляру (об'єкту) obj1 класу ClassName ClassName obj1; // оголошення об'єкту obj2 з одночасною ініціалізацією даними об'єкту obj1 ClassName obj2 = obj1; // викликається конструктор копіювання
У цьому випадку потрібно скопіювати дані з об’єкта obj1 в об’єкт obj2. Тобто, потрібно створити копію об’єкту obj1 так, щоб цей об’єкт obj1 міг в подальшому коректно використовуватись у програмі. Тому, необхідна копія. Цим займається конструктор копіювання.
Випадок 2. Коли потрібно передати об’єкт у функцію як параметр-значення. У цьому випадку створюється повна копія об’єкту.
Випадок 3. Коли потрібно повернути об’єкт з функції за значенням. У цьому випадку також створюється повна копія об’єкту, що повертається.
⇑
3. У яких випадках доцільно використовувати конструктор копіювання?
Конструктор копіювання необхідно використовувати у тих класах, в яких здійснюється динамічне виділення пам’яті для даних. Іншими словами, якщо у класі є покажчик, для даних якого пам’ять виділяється динамічно з допомогою оператора new (або іншими функціями), то такий клас обов’язково повинен містити конструктор копіювання. Інакше, у програмі будуть виникати проблеми, що пов’язані з недоліками побітового копіювання. Крім того, такий клас ще повинен містити операторну функцію operator=(), яка перевантажує оператор присвоєння (це вже інша тема).
Якщо у класі немає динамічного виділення пам’яті для даних, то конструктор копіювання можна не використовувати. У цьому випадку побітового копіювання (за замовчуванням) цілком достатньо для коректної роботи класу. Виняток, якщо при ініціалізації об’єкта іншим об’єктом потрібно встановити деякі спеціальні умови копіювання.
⇑
4. Приклад оголошення конструктора копіювання для класу, в якому немає динамічного виділення пам’яті
Даний приклад має демонстраційну мету. Для класу, в якому немає динамічного виділення пам’яті, використання конструктора копіювання необов’язкове.
Приклад. Нехай задано клас CMyPoint, що описує точку на координатній площині. У класі оголошується декілька конструкторів, у тому числі і конструктор копіювання.
// клас CMyPoint class CMyPoint { int x, y; public: CMyPoint(void); // конструктор класу за замовчуванням CMyPoint(int nx, int ny); // конструктор класу з двома параметрами CMyPoint(const CMyPoint& ref_Point); // конструктор копіювання // методи доступу - реалізовані в класі int GetX(void) { return x; } int GetY(void) { return y; } }; // реалізація конструкторів (методів) класу // конструктор класу CMyPoint CMyPoint::CMyPoint(void) { x = y = 0; } // конструктор класу CMyPoint з двома параметрами CMyPoint::CMyPoint(int nx, int ny) { x = nx; y = ny; } // конструктор копіювання класу CMyPoint // передається посилання на CMyPoint CMyPoint::CMyPoint(const CMyPoint & ref_Point) { // копіювання даних з одного об'єкту в інший x = ref_Point.x; y = ref_Point.y; }
Демонстрація використання конструктора копіювання у деякому програмному коді (методі)
// демонстрація використання конструктора копіювання CMyPoint p1(5, 8); // створення об'єкту p1 CMyPoint p2; // створення об'єкту p2 - викликається конструктор за замовчуванням // перевірка int d; d = p1.GetX(); // d = 5 d = p2.GetX(); // d = 0 p2 = p1; // побітове копіювання, конструктор копіювання не викликається d = p2.GetX(); // d = 5, дані скопіювались, але не з допомогою конструктора копіювання // код, що викликає конструктор копіювання CMyPoint p3 = p1; // ініціалізація об'єкта => викликається конструктор копіювання d = p3.GetX(); // d = 5
⇑
5. Приклад передачі об’єкту класу у функцію як параметр-значення
Приклад передачі об’єкту класу у функцію як параметр-значення. У прикладі передається об’єкт класу CMyPoint (див. п. 4) у функцію GetLength(), яка обчислює відстань від точки CMyPoint до початку координат. Текст функції наступний:
// функція, що обчислює відстань від точки до початку координат // точка є вхідним параметром double GetLength(CMyPoint mp) { double length; int tx, ty; tx = mp.GetX(); ty = mp.GetY(); length = Math::Sqrt(tx*tx + ty*ty); return length; }
Використання функції в іншому програмному коді
CMyPoint p1(5,5); // оголосити екземпляр класу CMyPoint double len; // передача точки p1 у функцію, викликається конструктор копіювання, len = 7,07... len = GetLength(p1);
⇑
6. Приклад повернення об’єкту класу з функції за значенням з допомогою конструктора копіювання
Реалізувати функцію GetCenterPoint(), яка повертає точку, що є серединою відрізку, проведеного між точкою CMyPoint та початком координат.
Оголошення класу таке саме як у п. 4.
Реалізація двох варіантів функцій GetCenterPoint() та GetCenterPoint2().
// функція, що повертає середину відрізка CMyPoint GetCenterPoint(CMyPoint mp) { int tx, ty; tx = mp.GetX() / 2; ty = mp.GetY() / 2; // повернення з функції, конструктор копіювання не викликається // замість нього викликається конструктор з 2 параметрами return CMyPoint(tx, ty); } // функція, що повертає середину відрізка, заданого точками CMyPoint GetCenterPoint2(int x, int y) { CMyPoint mp(x/2, y/2); // створюється тимчасовий об'єкт, який ініціалізується значенням з mp, // у результаті викликається конструктор копіювання return mp; // у цьому випадку конструктор копіювання не викликається //return CMyPoint(x/2,y/2); }
Демонстрація використання функцій
CMyPoint mp(18,-8); CMyPoint mpC; // викликається конструктор копіювання при передачі mp в функцію mpC = GetCenterPoint(mp); int cx, cy; cx = mpC.GetX(); // cx = 9 cy = mpC.GetY(); // cy = -4 // викликається конструктор копіювання при поверненні з функції mpC = GetCenterPoint2(-9, 13); cx = mpC.GetX(); // cx = -4 cy = mpC.GetY(); // cy = 6
У першому варіанті GetCenterPoint() конструктор копіювання викликається тільки при передачі параметру mp за значенням. При поверненні з функції GetCenterPoint() з допомогою оператора return, конструктор копіювання не викликається. Замість нього викликається конструктор з двома параметрами, що оголошений у класі.
У другому варіанті GetCenterPoint2() конструктор копіювання викликається при поверненні з функції оператором return. В операторі return створюється тимчасовий об’єкт класу CMyPoint, який одразу ініціалізується значенням mp, у результаті цього викликається конструктор копіювання.
⇑
7. Як здійснюється копіювання об’єктів, якщо конструктор копіювання не оголошений у класі?
Якщо у класі не оголошений конструктор копіювання, то використовується конструктор копіювання, який автоматично генерується компілятором. Цей конструктор копіювання здійснює побітове копіювання для отримання копії об’єкта.
Побітове копіювання є коректним для класів, в яких немає динамічного виділення пам’яті. Однак, якщо у класі є динамічне виділення пам’яті (клас використовує покажчики), то побітове копіювання призводить до того, що покажчики двох об’єктів вказують на ту саму ділянку пам’яті. А це є помилка.
⇑
Зв’язані теми
- Поняття класу. Оголошення класу. Об’єк класу. Класи в середовищі CLR. Інкапсуляція даних в класі
- Конструктор класу. Конструктор за замовчуванням. Параметризовані конструктори. Приклади класів, що містять конструктори
- Об’єкти класу як члени даних класу. Приклади