Листинг 10.5. Конструктор-копировщик



1: // Листинг 10.5.

2: // Конструктор-копировщик

3:

4: #include <iostream.h>

5:

6: class CAT

7: {

8: public:

9: CAT(); // конструктор по умолчанию

10: CAT (const CAT &); // конструктор-копировщик

11: ~CAT(); // деструктор

12: int GetAge() const { return *itsAge; }

13: int GetWeight() const { return *itsWeight; }

14: void SetAge(int age) { *itsAge = age; }

15:

16: private:

17: int *itsAge;

18: int *itsWeight;

19: };

20:

21: CAT::CAT()

22: {

23: itsAge = new int;

24: itsWeight = new int;

25: *itsAge = 5;

26: *itsWeight = 9;

27: }

28:

29: CAT::CAT(const CAT & rhs)

30: {

31: itsAge = new int;

32: itsWeight = new int;

33: *itsAge = rhs.GetAge(); // открытый метод доступа

34: *itsWeight = *(rhs.itsWeight); // закрытый метод доступа

35: }

36:

37: CAT::~CAT()

38: {

39: delete itsAge;

40: itsAge = 0;

41: delete itsWeight;

42: itsWeight = 0;

43: }

44:

45: int main()

46: {

47: CAT frisky;

48: cout << "frisky's age: " << frisky.GetAge() << endl;

49: cout << "Setting frisky to 6...\n";

50: frisky.SetAge(6);

51: cout << "Creating boots from frisky\n";

52: CAT boots(frisky);

53: cout << "frisky's age: " << frisky.GetAge() << endl;

54: cout << "boots' age; " << boots.GetAge() << endl;

55: cout << "setting frisky to 7...\n";

56: frisky.SetAge(7);

57: cout << "frisky's age: " << frisky.GetAge() << endl;

58: cout << "boot's age: " << boots.GetAge() << endl;

59: return 0;

60: }

 

Результат:

frisky's age: 5

Setting frisky to 6...

Creating boots from frisky

frisky's age: 6

boots' age: 6

setting frisky to 7...

frisky's age: 7

boots' age: 6

 

Анализ: В строках программы с 6 по 19 объявляется класс CAT. Обратите внимание, что в строке 9 объявляется конструктор по умолчанию, а в строке 10 — конструктор-копировщик.

В строках 17 и 18 объявляется две переменные-члены, представляющие собой указатели на целочисленные значения. В реальной жизни трудно вообразить, для чего может понадобиться создание переменных-членов как указателей на целочисленные значения. Но в данном случае такие переменные являются отличными объектами для демонстрации методов управления переменными-членами, сохраненными в динамической области памяти.

Конструктор по умолчанию в строках с 21 по 27 выделяет для переменных области динамической памяти и инициализирует эти переменные.

Работа копировщика начинается со строки 29. Обратите внимание, что в копировщике задан параметр rhs. Использовать в параметрах копировщиков символику rhs, что означает right-hand side (стоящий справа), — общепринятая практика. Если вы посмотрите на строки 33 и 34, то увидите, что в выражениях присваивания имена параметров копировщика располагаются справа от оператора присваивания (знака равенства).

Вот как работает копировщик. Строки 31 и 32 выделяют свободные ячейки в области динамической памяти. Затем, в строках 33 и 34 в новые ячейки переписываются значения из существующего класса CAT.

Параметр rhs соответствует объекту классу CAT, который передается в копировщик в виде константной ссылки. Как объект класса CAT, rhs содержит все переменные- члены любого другого класса CAT.

Любой объект класса CAT может открыть доступ к закрытым переменным-членам для любого другого объекта класса CAT. В то же время для внешних обращений всегда лучше создавать открытые члены, где это только возможно. Функция-член rhs.GetAge() возвращает значение, сохраненное в переменной-члене itsAge, адрес которой представлен в rhs.

Процедуры, осуществляемые программой, продемонстрированы на рис. 10.3. Значение, на которое ссылалась переменная-член исходного класса CAT, копируется в новую ячейку памяти, адрес которой представлен в такой же переменной-члене нового класса CAT.

В строке 47 вызывается объект frisky из класса CAT. Значение возраста, заданное в frisky, выводится на экран, после чего в строке 50 переменной возраста присваивается новое значение — 6. В строке 52 методом копирования объекта frisky создается новый объект boots класса CAT. Если бы в качестве параметра передавался объект frisky, то вызов копировщика осуществлялся бы компилятором.

В строках 53 и 54 выводится возраст обеих кошек. Обратите внимание, что в обоих случаях в объектах frisky и boots записан возраст 6, тогда как если бы объект boots создавался не методом копирования, то по умолчанию было бы присвоено значение 5. В строке 56 значение возраста в объекте было изменено на 7 и вновь выведены на экран значения обоих объектов. Значение объекта frisky действительно изменилось на 7, тогда как в boots сохранилось прежнее значение возраста 6. Это доказывает, что переменная объекта frisky была скопирована в объект boots по новому адресу.

 

Рис. 10.3 Пример глубинного копирования

 

Когда выполнение программы выходит за область видимости класса CAT, автоматически запускается деструктор. Выполнение деструктора класса CAT показано в строках с 37 по 43. Оператор delete применяется к обоим указателям — itsAge и itsWeigth, после чего оба указателя для надежности обнуляются.

 

Перегрузка операторов

 

Язык C++ располагает рядом встроенных типов данных, включая int, real, char и т.д. Для работы с данными этих типов используются встроенные операторы — суммирования (+) и умножения (<<). Кроме того, в C++ сушествует возможность добавлять и перегружать эти операторы для собственных классов.

Чтобы в деталях рассмотреть процедуру перегрузки операторов, в листинге 10.6 создается новый класс Counter. Объект класса Counter будет использоваться в других приложениях для подсчета циклов инкрементации, декрементации и других повторяющихся процессов.

Листинг 10.6. Класс Counter

1: // Листинг 10.6.

2: // Класс Counter

3:

4: int

5: #include <iostream.h>

6:

7: class Counter

8: {

9: public:

10: Counter();

11: ~Counter(){ }

12: int GetItsVal()const { return itsVal; }

13: void SetItsVal(int x) { itsVal = x; }

14:

15: private:

16: int itsVal;

17:

18: };

19:

20: Counter::Counter():

21: itsVal(0)

22: { }

23:

24: int main()

25: {

25: Counter i;

27: cout << "The value of i is " << i.GetItsVal() << endl;

28: return 0;

29: }

 

Результат:

The value of i is 0

 

Анализ: Судя по определению в строках программы с 7 по 18, это совершенно бесполезный класс. В нем объявлена единственная переменная-член типа int. Конструктор по умолчанию, который объявляется в строке 10 и выполняется в строке 20, инициализирует переменную-член нулевым значением.

В отличие от обычной переменной типа int, объект класса Counter не может использоваться в операциях приращения, прибавляться, присваиваться или подвергаться другим манипуляциям. В связи с этим выведение значения данного объекта на печать также сопряжено с рядом трудностей.

Запись Функции инкремента

 

Ограничения использования объекта нового класса, которые упоминались выше, можно преодолеть путем перегрузки операторов. Например, существует несколько способов восстановления возможности приращения объекта класса Counter. Один из них состоит в том, чтобы перегрузить функцию инкрементации, как показано в листинге 10.7.


Дата добавления: 2019-02-12; просмотров: 260; Мы поможем в написании вашей работы!

Поделиться с друзьями:






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