Информационный сайт

 

Реклама
bulletinsite.net -> Книги на сайте -> Программисту -> Мизрохи С.В. -> "Turbo Pascal и объектно-ориентированное программирование" -> 56

Turbo Pascal и объектно-ориентированное программирование - Мизрохи С.В.

Мизрохи С.В. Turbo Pascal и объектно-ориентированное программирование — М.: Финансы и статистика , 1992. — 192 c.
ISBN 5-279-00903-2
Скачать (прямая ссылка): efektispolzc2000.djvu
Предыдущая << 1 .. 50 51 52 53 54 55 < 56 > 57 58 59 60 61 62 .. 105 >> Следующая

Эта неприятность обычно имеет место, когда вы пытаетесь улучшить производительность функции, возвращая результат по ссылке, а не по значению. Приведем пример из правила 23, где подробно рассмотрено, когда можно, а когда не следует возвращать ссылку:
class Rational { Il Класс для рациональных чисел,
public:
Rational (int numerator = 0, int denominator = 1) ; -Rational ();
private:
int n, d; I/ Числитель и знаменатель.
Il Обратите внимание, что operator* ошибочно возвращает ссылку, friend const Rational& operator* (const RationalSc lhs,
const RationalSc rhs) ;
};
Il Некорректная реализация operator*.
inline const RationalSc operator* (const RationalSc lhs,
const RationalSc rhs)
{
Rational result (lhs.n * rhs.n, lhs.d * rhs.d); return result;
}
Здесь локальный объект result создается при входе в тело operator*. Однако локальные объекты автоматически удаляются при выходе из области
Правило 31 '11¦¦¦¦¦KSl
идимости. Объект result выходит из нее после выполнения оператора eturn, поэтому, когда вы пишете
Rational two = 2;
Rational four = two * two; Il To же самое, что и operator* (two,two) .
При вызове функции происходит следующее:
1. Создается локальный объект result.
2. Ссылка инициализируется другим идентификатором result и передается наружу как возвращаемое значение.
3. Локальный объект result удаляется, а место, которое он занимал в стеке, становится доступным для использования другими частями программы или даже другими программами.
4. Объект four инициализируется с использованием ссылки из шага 2.
Все идет хорошо, пока на шаге 4 не случается то, что в узких кругах высоких технологий называется «глобальным сбоем». Ссылка, инициализированная на шаге 2, перестает на заключительном этапе шага 3 ссылаться на допустимый объект, поэто-исход инициализации объекта four совершенно не определен. Урок очевиден: не возвращайте ссылки на локальные объекты. «Значит, - скажете вы, - проблема заключается в том, что объект, который я хочу использовать, выходит из области видимости слишком быстро. Это можно исправить. Я просто вызову new вместо использования локального объекта». Например:
// Другая некорректная реализация operator* .
inline const Rationale operator*(const Rational& lhs,
const Rational& rhs)
{
Il Создаем в куче новый объект.
Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d); Il Возвращаем его. return *result;
}
При таком подходе вы действительно избегаете проблемы, рассмотренной предыдущем примере, но на ее месте создаете новую. Для того чтобы предотвратить утечки памяти в программном обеспечении, вы должны быть уверены, что каждому указателю, созданному с помощью new, соответствует вызов delete. Загвоздка лишь в том, кто будет отслеживать, чтобы каждому вызову ^ew соответствовал вызов delete?
Щ Очевидно, что ответственность за вызов delete лежит на том, кто вызвал °perator *. На практике же с такого рода ответственностью кое у кого из программистов дело обстоит совершенно безнадежно. Для пессимизма есть две причины.
Во-первых, хорошо известно, что программисты - большие разгильдяи. Не то ^тобы вы или я были разгильдяями, но редко кто из нас не встречался по работе с -йїодьми, так сказать, немного рассеянными. И стоит ли надеяться, что такие специалисты (мы-то с вами знаем - они существуют) запомнят, что всякий раз, ког-^а они вызывают operator*, им следует получить возвращаемый адрес и затем
МЩШ'Л mm*
Классы и функции: реализация
использовать для него delete? Другими словами, что они должны использовать operator* следующим образом:
delete & four; Il Получаем указатель и удаляем его.
Шансы на это близки к нулю. Помните, что даже если один-единственный пользователь operator* не будет следовать этим правилам, произойдет утечка памяти.
Образование недоступных указателей - вторая и более серьезная проблема, от которой не застрахованы даже самые сознательные программисты. Часто результат действия функции operator* - временное промежуточное значение, объект, создаваемый только ради вычисления большего выражения. Например:
Rational one(1), two(2), three(3), four(4);
Rational product;
product = one * two * three * four;
Оценка выражения, присваиваемого product, требует трех раздельных вызовов operator*. Этот факт становится более очевидным, когда вы переписываете выражение в эквивалентной функциональной форме:
* product = operator*(operator*(operator*(one, two),three), four);
Мы знаем, что каждый вызов operator* возвращает объект, требующий удаления, но у нас нет никакой возможности применить delete, поскольку ни одного идентификатора этих объектов создано не было.
Единственное решение этой задачи - требовать написания кода, подобного нижеприведенному:
const RationalS tempi = one * two; const Rational& temp2 = tempi * three; const Rationale temp3 = temp2 * four; delete Sctempl; delete &temp2; delete &temp3;
Лучшее, на что можно надеяться, - это то, что вас проигнорируют. Более вероятно, что с вас живьем снимут кожу или приговорят к десяти годам тяжелой работы по написанию микрокоманд для вафельниц или тостеров.
Предыдущая << 1 .. 50 51 52 53 54 55 < 56 > 57 58 59 60 61 62 .. 105 >> Следующая
Реклама
Авторские права © 2009 AdsNet. Все права защищены.
Rambler's Top100