Рейтинговые книги
Читем онлайн Освой самостоятельно С++ за 21 день. - Джесс Либерти

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 51 52 53 54 55 56 57 58 59 ... 170

Calling FunctionOne...

Simple Cat Copy Constructor...

Function One. Returning...

Simple Cat Copy Constructor...

Simple Cat Destructor...

Simple Cat Destructor...

Calling FunctionTwo...

Function Two. Returning...

Simple Cat Destructor...

Примечание:Номера строк не выводятся. Мы добавили их для удобства проведения анализа программы.

Анализ: В строках 6-12 объявляется весьма упрошенный класс SimpleCat. Конструктор, конструктор-копировщик и деструктор — все компоненты класса выводят на экран свои информативные сообщения, чтобы было точно известно, когда они вызываются.

В строке 34 функция main() выводит свое первое сообщение, которое является первым и в результатах работы программы. В строке 35 создается экземпляр объекта класса SimpleCat. Это приводит к вызову конструктора, что подтверждает сообщение, выводимое этим конструктором (строка 2 в результатах работы программы).

В строке 36 функция main() "докладывает" о вызове функции FunctionOne, которая выводит свое сообщение (строка 3 в результатах работы программы). Поскольку функция FunctionOne() вызывается с передачей объекта класса SimpleCat по значению, в стек помещается копия объекта SimpleCat как локального для вызываемой функции. Это приводит к вызову конструктора копии, который "вносит свою лепту" в результаты работы программы (сообщение в строке 4).

Выполнение программы переходит к строке 46, которая принадлежит телу вызванной функции, выводящей свое информативное сообщение (строка 5 в результатах работы программы). Затем эта функция возвращает управление программой вызывающей функции, и объект класса SimpleCat вновь возвращается как значение. При этом создается еще одна копия объекта за счет вызова конструктора-копировщика и, как следствие, на экран выводится очередное сообщение (строка 6 в результатах работы программы).

Значение, возвращаемое из функции FunctionOne(), не присваивается ни одному объекту, поэтому ресурсы, затраченные на создание временного объекта при реализации механизма возврата, просто выброшены на ветер, как и ресурсы, затраченные на его удаление с помощью деструктора, который заявил о себе в строке 7 в результатах работы программы. Поскольку функция FunctionOne() завершена, локальная копия объекта выходит за область видимости и разрушается, вызывая деструктор и генерируя тем самым сообщение, показанное в строке 8 в результатах работы программы.

Управление программой возвращается к функции main(), после чего вызывается функция FunctionTwo(), но на этот раз параметр передается как ссылка. При этом никакой копии объекта не создается, поэтому отсутствует и сообщение от конструктора- копировщика. В функции FunctionTwo() выводится сообщение, занимающее строку 10 в результатах работы программы, а затем выполняется возвращение объекта класса SimpleCat (снова как ссылки), поэтому нет никаких обращений к конструктору и деструктору.

Наконец программа завершается и объект Frisky выходит за область видимости, генерируя последнее обращение к деструктору, выводящему свое сообщение (строка 11 в результатах работы программы).

Проанализировав работу этой программы, можно сделать вывод, что при вызове функции FunctionOne() делается два обращения к конструктору копии и два обращения к деструктору, поскольку объект в эту функцию передается как значение, в то время как при вызове функции FunctionTwo() подобных обращений не делается.

Передача константного указателя

Несмотря на то что передача указателя функции FunctionTwo() более эффективна, чем передача по значению, она таит в себе немалую опасность. При вызове функции FunctionTwo() совершенно не имелось в виду, что разрешается изменять передаваемый ей объект класса SimpleCat, задаваемый в виде адреса объекта SimpleCat. Такой способ передачи открывает объект для изменений и аннулирует защиту, обеспечиваемую при передаче объекта как значения.

Передачу объектов как значений можно сравнить с передачей музею фотографии шедевра вместо самого шедевра. Если какой-нибудь хулиган испортит фотографию, то никакого вреда при этом оригиналу нанесено не будет. А передачу объекта как ссылки можно сравнить с передачей музею своего домашнего адреса и приглашением гостей посетить ваш дом и взглянуть в вашем присутствии на драгоценный шедевр.

Решить проблему можно, передав в функцию указатель на константный объект класса SimpleCat. В этом случае к данному объекту могут применяться только константные методы, не имеющие прав на изменение объекта SimpleCat. Эта идея демонстрируется в листинге 9.11.

Листинг 9.11. Передача константных указателей

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

2: // Передача константных указателей на объекты

3:

4: #include <iostream.h>

5:

6: class SimpleCat

7: {

8:    public:

9:       SimpleCat();

10:      SimpleCat(SimpleCat&);

11:      ~SimpleCat();

12:

13:   int GetAge() const { return itsAge; }

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

15:

16:   private:

17:      int itsAge;

18: };

19:

20: SimpleCat::SimpleCat()

21: {

22:    cout << "Simple Cat Constructor...n";

23:    itsAge = 1;

24: }

25:

26: SimpleCat::SimpleCat(SimpleCat&)

27: {

28:    cout << "Simple Cat Copy Constructor...n";

29: }

30:

31: SimpleCat::~SimpleCat()

32: {

33:    cout << "Simple Cat Destructor...n";

34: }

35:

36: const SimpleCat * const FunctionTwo (const SimpleCat * const theCat);

37:

38: int main()

39: {

40:    cout << "Making а cat...n";

41:    SimpleCat Frisky;

42:    cout << "Frisky is " ;

43:    cout << Frisky.GetAge();

44:    cout << " years oldn";

45:    int age = 5:

46:    Frisky.SetAge(age);

47:    cout << "Frisky is " ;

48:    cout << Frisky.GetAge();

49:    cout << " years old n";

50:    cout << "Calling FunctionTwo...n";

51:    FunctionTwo(&Frisky);

52:    cout << "Frisky is ";

53:    cout << Frisky.GetAge();

54:    cout << " years_aldn";

55:    rsturn 0;

56: }

57:

58: // functionTwo, passes a const pointer

59: const SimpleCat * const FunctionTwo (const SimpleCat * const theCat)

60: {

61:    cout << "Function Two, Returning...n";

62:    cout << "Frisky is now " << theCat->GetAge();

63:    cout << " years old n";

64:    // theCat->SotAge(8): const!

65:    return theCat;

66: }

Результат:

Making a cat...

Simple Cat constructor...

Frisky is 1 years old

Frisky is 5 years old

Calling FunctionTwo...

FunctionTwo. Returning...

Frisky is now 5 years old

Frisky is 5 years old

Simple Cat Destructor...

Анализ: В класс SimpleCat были добавлены две функции доступа к данным: метод GetAge() (строка 13), который является константной функцией, и метод SetAge() (строка 14), который не является константным. В этот класс была также добавлена переменная-член itsAge (строка 17).

Конструктор, конструктор-копировщик и деструктор по-прежнему определены для вывода на экран своих сообщений. Однако конструктор-копировщик ни разу не вызывался, поскольку объект был передан как ссылка и поэтому никаких копий объекта не создавалось. В строке 41 был создан объект со значением возраста, заданным по умолчанию. Это значение выводится на экран в строке 42.

В строке 46 переменная itsAge устанавливается с помощью метода доступа SetAge, а результат этой установки выводится на экран в строке 47. В этой программе функция FunctionOne не используется, но вызывается функция FunctionTwo(), которая слегка изменена. Ее объявление занимает строку 36. На этот раз и параметр, и значение возврата объявляются как константные указатели на константные объекты.

Поскольку и параметр, и возвращаемое значение передаются как ссылки, никаких копий не создается и конструктор-копировщик не вызывается. Однако указатель в функции FunctionTwo() теперь является константным, следовательно, к нему не может применяться неконстантный метод SetAge(). Если обращение к методу SetAge() в строке 64 не было бы закомментировано, программа не прошла бы этап компиляции.

Обратите внимание, что объект, создаваемый в функции main(), не является константным и объект Frisky может вызвать метод SetAge(). Адрес этого обычного объекта передается функции FunctionTwo(), но, поскольку в объявлении функции FunctionTwo() заявлено, что передаваемый указатель должен быть константным указателем на константный объект, с этим объектом функция обращается так, как если бы он был константным!

Ссылки в качестве альтернативы

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

Поскольку, как вам известно, объект никогда не бывает нулевым, внутреннее содержание функции упростилось бы, если бы ей вместо указателя передавалась ссылка. Подтверждение этим словам вы найдете в листинге 9.12.

1 ... 51 52 53 54 55 56 57 58 59 ... 170
На этой странице вы можете бесплатно читать книгу Освой самостоятельно С++ за 21 день. - Джесс Либерти бесплатно.

Оставить комментарий