Ініціалізація копіювання і присвоювання 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Ініціалізація копіювання і присвоювання



Компілятор С++ завжди підтримує програміста, виконуючи рутинну роботу. Якщо ж програміст зобов’язується контролювати ситуацію самостійно, він рахуватиметься з вашою думкою і може надати можливість деякі питання вирішувати самостійно, але за замовчуванням компілятор все робить сам. Двома надзвичайно важливими прикладами цього процесу є оператор присвоювання і конструктор копіювання.

Ми постійно використовуємо оператор присвоювання, можливо, навіть не задумуючись про те, що за ними стоїть з точки зору компілятора. Нехай а1 і а2 – деякі об’єкти. Попри те, що ми наказуємо компілятору «присвоїти а2 значення а1», вираз

а2=а1

змусить компілятор копіювати дані з а1 елемент за елементом в а2. Так за замовчуванням діє оператор присвоювання.

Ми знайомі з ініціалізацією змінних. Ініціалізація одного об’єкту іншим, як у виразі

alpha a2(a1);

викликає подібні дії. Компілятор створює новий об’єкт а2, потім елемент за елементом копіює дані з а1 в а2. Це саме те, що за замовчуванням виконує конструктор копіювання.

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

 

Перезавантаження оператора присвоювання

Наступний приклад (програма 13.15) продемонструє технологію перезавантаження оператора присвоювання.

 #include<iostream.h>

 #include<conio.h>

 #include<stdio.h>

 #include<bios.h>

 ///////////////

 class alpha

 {private:

 int data;

 public:

 alpha() //конструктор без аргументу

 { }

 alpha(int d) //конструктор з 1 аргументом

 {data=d;}

 void display()

 {cout<<data;}

 

 alpha operator=(alpha& a) //перезавантаження =

{ data = a. data;

cout <<”\ nЗапущений оператор =”;

return alpha(data);

}

};

////////////////

 

 int main()

 {clrscr();

 alpha a1(37);

 alpha a2;

 a2=a1; //запускається =

 cout<<”\na2=”;a2.display();

 alpha a3=a2; //не запускається =

 cout<<”\na3=”;a3.display();

 cout<<endl;

 bioskey(0);

 return 0;

 }

Програма 13.15

Клас alpha дуже простий, в ньому міститься тільки один елемент даних. Конструктори ініціалізують дані, а методи виводять їх значення на екран. От і все, що тут робиться. А новизна програми 13.15 полягає в функції operator=(), що перезавантажує оператор =.

В main() ми визначаємо змінну а1 і присвоюємо їй значення 37, визначаємо змінну а2, але значення їй не присвоюємо. Потім присвоюємо а2 значення а1

a2=a1;

Тим самим ми запускаємо нашу перезавантажувану функцію operator=()/

Ініціалізація – це не присвоювання.

В останніх двох рядках програми 13.15 ми ініціалізуємо об’єкт а3 об’єктом а2 і виводимо його значення на екран. Нехай нас не дивує синтаксис. Виразу

alpha a3=a2;

відповідає не присвоювання, а ініціалізація. При цьому ефект такий сам, як при виконанні виразу

alpha a3(a2);

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

 

Відповідальність

Коли перезавантажується оператор присвоювання, то передбачається, що за все, що раніше оператор виконував за замовчуванням, відповідає програміст. Клас alpha з програми 13.15 включає в себе тільки один елемент data, тому функція operator=() копіює його значення за допомогою такого виразу:

data=a.data;

Крім того, функція виводить на екран повідомлення «Запущений оператор =», завдяки якому ми можемо впевнитися, що функція виконується.

 

Передача за посиланням

Аргумент для функції operator=() передається за посиланням.

alpha operator=(alpha & a)

Це не є особливо необхідним (можна спробувати вилучити символ & і пересвідчитися, що в роботі програми нічого не поміняється), але звичайно саме так і чинять. Як ми знаємо, аргумент, що передається за значенням, створює в пам’яті власну копію для функції, якій він передається. Аргумент, що передається функції operator=() не є винятком. Якщо об’єкти великі, то їх копії займатимуть в пам’яті багато місця без всякої користі. Коли ж значення аргументу передається за посиланням, копії не створюються, що допомагає економити пам’ять.

Є і ще одна причина. В певних ситуаціях необхідно відслідковувати кількість створених об’єктів (ми це робили в програмі 13.14). Якщо ж компілятор генерує ще якісь об’єкти кожного разу, коли виконується оператор присвоювання, то ми ризикуємо дуже завищити оцінку їх кількості. За допомогою передачі за посиланням вдається ліквідувати прецеденти несправжнього створення об’єктів.

 

Повернення значень

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

Функція operator=() повертає результат шляхом створення тимчасового об’єкта alpha та його ініціалізації за допомогою одноаргументного конструктора у виразі

return alpha(data);

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

а3=а2=а1;

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

alpha& operator=(alpha& a)

На жаль ні, ми не можемо повернути результат за посиланням при роботі з локальними змінними даної функції.

Локальні змінні, тобто створені всередині функції і не оголошені статичними, знищуються після виходу з функції. Але повернення за посиланням повертає у викликаючу програму тільки адресу даних, яка для локальних змінних вказує на дані, що знаходяться всередині функції. При поверненні з функції вказівник зберігає вже таке значення, що не має сенсу. Компілятор може сигналізувати про таку проблему попередженням. Розв’язати таку проблему можна з використанням вказівника this.

 

Обмеження на успадкування

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

 

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

Можна визначити об’єкти і одночасно їх ініціалізувати іншими за допомогою двох типів виразів:

alpha a3(a2); //ініціалізація копіювання

alpha a3=a2; //альтернативний запис ініціалізації копіювання

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

Як і оператор присвоювання, конструктор копіювання може бути перезавантаженим. В програмі 13.16 показано, як це робиться:

 #include<iostream.h>

 #include<conio.h>

 #include<stdio.h>

 #include<bios.h>

 ///////////////

 class alpha

 {private:

 int data;

 public:

 alpha()

 { }

 alpha(int d)

 {data=d;}

 alpha(alpha& a) //конструктор копіювання

 {data=a.data;

 cout<<”\nЗапущений конструктор копіювання”;

 }

 void display()

 {cout<<data;}

 

 void operator=(alpha& a)

 {data=a.data;

 cout<<”\nЗапущений оператор =”;

 }

 };

////////////////

 

 int main()

 {clrscr();

 alpha a1(37);

 alpha a2;

 a2=a1; //Запуск перезавантаженого =

 cout<<”\na2=”;a2.display();

 alpha a3(a1); //запуск конструктора копіювання

 //alpha a3=a1; //еквівалентне визначення а3

 cout<<”\na3=”;a3.display();

 cout<<endl;

 bioskey(0);

 return 0;

 }

Програма 13.16

В цій програмі перезавантажується і оператор присвоювання і конструктор копіювання. Перезавантажене присвоювання схоже на аналогічне з програми 13.15. У конструктора копіювання є один аргумент – об’єкт типу alpha, що передається за посиланням. Його опис:

alpha(alpha&)

Він має вигляд Х(&X).

Вираз

а2=а1:

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

alpha a3(a1);

запускає конструктор копіювання. З цією ж метою може бути використаний еквівалентний вираз:

alpha a3=a1;

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

 

Аргументи функції

Конструктор копіювання запускається при передачі за значенням аргументу функції. Він створює копію об’єкту, з якою функція і працює. Таким чином, якщо б функція

void func(alpha);

була б оголошена в 13.17 і коли б вона викликалася виразом:

func(a1);

то конструктор копіювання створив би копію об’єкта а1 для його використання func(). Зрозуміло, що конструктор копіювання не запускається при передачі аргументу за посиланням чи при передачі вказівника на об’єкт. В цих випадках ніякі копії не створюються і функція працює з вихідною змінною

 



Поделиться:


Последнее изменение этой страницы: 2021-12-15; просмотров: 67; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.220.16.184 (0.019 с.)