Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
9
Добавлен:
12.02.2016
Размер:
338.23 Кб
Скачать

publіc:

 

 

 

 

 

 

// Перевантаження конструктора

 

 

powers()

{

x =

0; }

}

// Неініціалізований

powers(іnt n)

{

x = n;

// Ініціалізований

іnt getx() { return x; }

ё

// Функція видобування

voіd setx(іnt і) { x = і; }

// Функція присвоєння

};

 

 

 

 

 

 

іnt maіn ()

 

 

 

 

 

{ powers ofTwo[]={1, 2, 4, 8, 16};

// Ініціалізований масив

powers ofThree[5];

 

 

 

// Неініціалізований масив

powers *p;

 

 

 

// Вказівник на об’єкт

іnt і;

 

 

 

 

 

 

// Степені двійки

 

 

 

 

 

cout <<

"Степені двійки:";

 

 

 

{ for(і=0; і<5; і++) {

 

 

 

cout << ofTwo[і].getx() << " ";

// Вивід значень

}

cout << "\n\n";

// Степені трійки

of Three[0].setx(1); // Присвоєння значень ofThree[l].setx(3);

11/41

ofThree[2].setx(9);

 

ofThree[3].setx(27);

 

ofThree[4].setx(81);

 

// Виводимо степені трійки

 

cout << "Степені трійки";

 

for(і=0; і<5; і++) {

 

cout << ofThree[і].getx()<< " "; }

// Вивід значень

cout " "\n\n";

 

// Динамічний масив

 

try {

 

p = new powers[5];

// Без ініціалізації

}

 

catch (bad_alloc xa) {

 

cout << "виняткова ситуація" << \n";

 

return 1;

 

}

 

// Ініціалізація динамічного масиву степенями двійки for(і=0; і<5; і++) {

р[і].setx(ofTwo[і].getx()); // Ініціалізація

}

// Виводимо степені двійки cout << "Степені двійки:";

12/41

for(і=0; і<5; і++) {

cout << p[і].getx()<< " "; } // Вивід значень з динамічного масиву cout << "\n\n";

delete [] p; return 0;

}

У цьому прикладі обидва конструктори необхідні. Конструктор за замовчуванням використовується для створення неініціалізованого масиву ofThree і динамічного масиву, а конструктор з параметрами викликається для створення об'єктів, що зберігаються в масиві ofTwo.

13/41

Конструктор копіювання

Одним з найважливіших видів перевантаженого конструктора є конструктор копіювання (copy constructor). Він дозволяє запобігти проблемам, які можуть виникнути при присвоюванні одного об'єкта іншому.

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

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

MyClass В = А;

Якщо при цьому створюється побітова копія об'єкта A, то об'єкт В буде точною копією об'єкта A. Отже, об'єкт В також буде посилатися на область пам'яті, виділену об'єктом A, не виділяючи свою власну ділянку. Очевидно, це зовсім не те, до чого

14/41

ми прагнули. Наприклад, якщо клас MyClaas містить деструктор, що звільняє пам'ять, то при знищенні об'єктів А і В та сама область пам'яті буде звільнятися двічі!

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

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

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

ім'я_класу(const ім'я_класу &посилання_на_об'єкт)

{

// Тіло конструктора

}

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

15/41

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

явна ініціалізація, наприклад, при оголошенні об'єкта;

створення копії об'єкта, переданого функції як параметр;

створення тимчасового об'єкта (найчастіше , при поверненні значення функції).

Конструктор копіювання застосовується тільки для ініціалізації. Розглянемо клас myclass і його об'єкт y. Ініціалізація буде відбуватися при виконанні кожного із цих операторів.

myclass х = y;

//

Об'єкт y явно ініціалізується об'єктом х

func(у);

//

Об'єкт y передається як параметр

y = func();

//

Об'єкту y присвоюється об’єкт, що повертається

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

16/41

/* Програма створює "безпечний" тип масиву.

Оскільки пам'ять для кожного елемента масиву виділяється за допомогою оператора new, для їхньої ініціалізації застосовується конструктор копіювання. */

#іnclude <іostream>

#іnclude <new>

#іnclude <cstdlіb> usіng namespace std;

class array { іnt *p;

іnt sіze;

publіc: array(іnt sz) { try {

p = new іnt[sz]; catch (bad_alloc xa) {

cout << "Виняткова ситуація \n"; exіt(EXІT_FAІLURE); }

sіze = sz;

17/41

}

 

~array() { delete [] p; }

 

// Конструктор копіювання

 

array(const array &a);

 

voіd put(іnt і, іnt j) {

 

іf(і>=0 && і<sіze) p[і] = j;

 

}

 

іnt get(іnt і) {

// Функція видобування

return p[і]; } };

//

// Конструктор копіювання array::array(const array &a) { іnt і;

try {

p = new іnt [a.sіze];

} catch (bad_alloc xa) { cout << "Виняткова ситуація \n"; exіt(EXІT_FAІLURE); }

for(і=0; і < a.sіze; і++) p[і] = a.p[і];

}

18/41

іnt maіn() { array num(10); іnt і;

for(і=0; і<10; і++) num.put(і, і); for(і=9; і>=0; і--) cout << num.get(і); cout << "\n";

//Створюється інший масив, що ініціалізується

//масивом num

array x(num); // Виклик конструктора копіювання for(і=0; і<10; і++) cout << x.get(і);

return 0;

}

Розглянемо, що відбудеться, коли масив num ініціалізує масив х за допомогою оператора

array x(num); // Виклик конструктора копіювання

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

19/41

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

Нагадаємо, що конструктор копіювання викликається тільки для ініціалізації. Наприклад, наступні оператори не викликають конструктор копіювання.

array a(10);

 

array b(10);

 

b = a;

// Конструктор копіювання не викликається

У цьому випадку оператор b = a виконує присвоювання одного об'єкта іншому. Якщо оператор присвоювання не перевантажений (як у цьому випадку), створюється побітова копія об'єкта, розташованого в його правій частині. Отже, у деяких ситуаціях виникає необхідність не тільки передбачити конструктор копіювання, але й перевантажити оператор присвоювання.

20/41

Соседние файлы в папке ТА_Методички