Чтение онлайн

ЖАНРЫ

Освой самостоятельно С++ за 21 день.

Либерти Джесс

Шрифт:

12: int GetAge < return itsAge; }

13: int GetWeight { return itsWeight; }

14: private:

15: int itsAge;

16: int itsWeight;

17: };

18:

19: SimpleCat::SimpleCat(int age, int weight)

20: {

21: itsAge = age;

22: itsWeight = weight;

23: }

24:

25: SimpleCat &TheFunction;

26:

27: int main

28: {

29: SimpleCat &rCat = TheFunction;

30: int age = rCat.GetAge;

31: cout << "rCat " << age << " years old!\n"

32: return 0;

33: }

34:

35: SimpleCat &TheFunction

36: {

37: SimpleCat Frisky(5,9);

38: return Frisky;

39: }

Результат:

Compile error: Attempting to return a reference to a local object! (Ошибка

компиляции: попытка возвратить ссылку на локальный объект!)

Предупреждение:Эта программа не компилируется на компиляторе фирмы Borland, но для нее подходят компиляторы компании Microsoft. Однако профессиональный программист никогда не станет полагаться на уступки компилятора.

Анализ: В строках 7—17 объявляется класс SimpleCat. В строке 29 инициализируется ссылка на объект класса SimpleCat с использованием результатов вызова функции TheFunction, объявленной в строке25. Согласно объявлению эта функция возвращает ссылку на объект класса SimpleCat.

В теле функции TheFunction объявляется локальный объект типа SimpleCat и инициализируется его возраст и вес. Затем этот объект возвращается по ссылке. Некоторые компиляторы обладают достаточным интеллектом, чтобы распознать эту ошибку, и не позволят вам запустить данную программу на выполнение. Другие же (сразу видно, кто настоящий друг) спокойно разрешат вам выполнить эту программу с непредсказуемыми последствиями.

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

Возвращение ссылки на в области динамического обмена

Можно было бы попытаться решить проблему, представленную в листинге9.13, сориентировав функцию TheFunction на размещение объекта Frisky в области динамического обмена. В этом случае после возврата из функции TheFunction объект Frisky будет все еще жив.

Новый подход порождает новую проблему: что делать с памятью, выделенной для объекта Frisky, после окончания обработки этого объекта? Эта проблема показана в листинге 9.14.

Листинг 9.14. Утечка памяти

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

2: // Разрешение проблемы утечки памяти

3: #include <iostream.h>

4:

5: class SimpleCat

6: {

7: public:

8: SimpleCat (int age, int weight);

9: ~SimpleCat { }

10: int GetAge { return itsAge; }

11: int GetWeight { return itsWeight; }

12:

13: private:

14: int itsAge;

15: int itsWeight;

16: };

17:

18: SimpleCat::SimpleCat(int age, int weight)

19: {

20: itsAge = age;

21: itsWeight = weight;

22: }

23:

24: SimpleCat & TheFunction;

25:

26: int main

27: {

28: SimpleCat & rCat = TheFunction;

29: int age = rCat.GetAge;

30: cout << "rCat " << age << " years old!\n";

31: cout << "&rCat: " << &rCat << endl;

32: //

Как освободить эту память?

33: SimpleCat * pCat = &rCat;

34: delete pCat;

35: // Боже, на что же теперь ссылается rCat??

36: return 0;

37: }

38:

39: SimpleCat &TheFunction

40: {

41: SimpleCat * pFrisky = new SimpleCat(5,9);

42: cout << "pFrisky: " << pFrisky << endl;

43: return *pFrisky;

44: }

Результат:

pFrisky: 0x00431C60

rCat 5 years old!

&rCat: 0x00431C60

Предупреждение:Эта программа компилируется, компонуется и, кажется, работает. Но мина замедленного действия уже ожидает своего часа.

Анализ: Функция TheFunction была изменена таким образом, чтобы больше не возвращать ссыпку на локальную переменную. В строке 41 вычисляется некоторая область динамически распределяемой памяти и ее адрес присваивается указателю. Этот адрес выводится на экран, после чего указатель разыменовывается и объект класса SimpleCat возвращается по ссылке.

В строке 28 значение возврата функции TheFunction присваивается ссылке на объект класса SimpleCat, а затем этот объект используется для получения возраста кота, и полученное значение возраста выводится на экран в строке 30.

Чтобы доказать, что ссылка, объявленная в функции main, ссылается на объект, размещенный в области динамической памяти, выделенной для него в теле функции TheFunction, к ссылке rCat применяется оператор адреса (&). Вполне убедителен тот факт, что адрес объекта, на который ссылается rCat, совпадает с адресом объекта, расположенного в свободной области памяти.

До сих пор все было гладко. Но как же теперь освободить эту область памяти, которая больше не нужна? Ведь нельзя же выполнять операции удаления на ссылках. На ум приходит одно решение: создать указатель и инициализировать его адресом, полученным из ссылки rCat. При этом и память будет освобождена, и условия для утечки памяти будут ликвидированы. Все же одна маленькая проблема остается: на что теперь ссылается переменная rCat после выполнения строки 34? Как указывалось выше, ссылка всегда должна оставаться псевдонимом реального объекта; если же она ссылается на нулевой объект (как в данном случае), о корректности программы говорить нельзя.

Примечание:Не будет преувеличением определение программы как некорректной, если она содержит ссылку на нулевой объект (несмотря на то что она успешно компилируется), поскольку результаты ее выполнения непредсказуемы.

Для решения этой проблемы есть три пути. Первый состоит в объявлении объекта класса SimpleCat в строке 28 и возвращении этого объекта из функции TheFunction как значения. Второй — в объявлении класса SimpleCat в свободной области (в теле функции TheFunction), но сделать это нужно так, чтобы функция TheFunction возвращала указатель на данный объект. Затем, когда объект больше не нужен, его можно удалить в вызывающей функции с помощью оператора delete.

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