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

 

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

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

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

Каков бы ни был источник проблемы, ее можно избежать, если вы знаете, чего следует опасаться. Нижеследующие правила обрисовывают некоторые ситуации, в которых вам следует быть особенно бдительными.
Правило 29. Избегайте возврата «дескрипторов» внутренних данных
Сцена из объектно-ориентированного романа: Объект А: Дорогая, оставайся такой всегда! Объект В: Не волнуйся, дорогой, я const.
Тем не менее, как и в реальной жизни, А размышляет: «Можно ли доверять В?», И, совсем как в реальной жизни, ответ часто зависит от природы В: состава его Функций-членов.
Предположим, что В - это константный объект String:
class String { public:
String(const char *value); Il Возможную реализацию |
-String () ; Il вы можете найти в правиле 11.
operator char *() const; Il Преобразование String в char*.
private:
char *data; J
}; I
const String B ("Hello World") ; // в - это константный объект.
шшт
mm
HI
Классы и функции: реализация
Поскольку В объявлен как const, было бы лучше всего, если бы значение в отныне и навсегда оставалось равным "Hello World". Конечно, при этом предполагается, что программисты, работающие с В, будут вести «честную игру». В частности, многое зависит от того, станут ли они посредством «грязных приемов» обходить константность (см. правило 21):
// Делаем alsoB другим именем для В, но без константности. Strings alsoB = const_cast<String&> (В) ;
Если, впрочем, никто не совершает таких злодеяний, то вполне можно поручиться, что В никогда не изменится. Или все-таки нет? Рассмотрим следующую последовательность событий:
char *str = В; // Вызов В.operator char*() .
strcpy(str, "Hi Mom") ; Il Модифицируем то, на что указывает str.
Действительно ли значение В По-прежнему равно "Hello World", или оно неожиданно превратилось в "Hi Mom"? Ответ полностью зависит от реализации String::operator char*.
Ниже приведена небрежная реализация, которая ведет себя неправильно. Тем не менее она работает очень эффективно, поэтому многие программисты попадаются в эту ловушку:
// Быстрая, но некорректная реализация, inline String::operator char* () const { return data; }
Проблема с этой функцией заключается в том, что она возвращает дескриптор (в данном случае указатель) для информации, содержащейся внутри объекта String, для которого функция вызывается. Дескриптор дает пользователю неограниченный доступ к тому, на что указывает закрытый член data. Другими словами, после строчки
char *str = В;
ситуация выглядит следующим образом:
Ясно, что любая модификация памяти, на которую указывает str, изменит также и фактическое значение В. Таким образом, несмотря на то, что В объявлен как const и вызываются только константные функции-члены В, по ходу выполнения программы он все равно может принять другое значение. В частности, если изменится значение, на которое указывает str, то В также подвергнется изменению.
str
IHIeIlIIIoI [WIoIrIIIfJIXQl
Правило 29
В том, как написана функция String: : operator char*, нет ничего плохого. Плохо то, что она может быть использована для константных объектов. Если бы функция не была объявлена как const, то проблемы бы не возникло, поскольку функцию было бы невозможно вызвать для объектов, подобных В.
Тем не менее преобразование объекта String (даже константного) в эквивалентный указатель char* кажется вполне разумным, так что вы предпочтете оставить объявление этой функции const. Если вам это необходимо, следует переписать реализацию так, чтобы избежать возврата дескриптора внутренних данных:
// Более медленная, но и более безопасная реализация.
inline String: :operator char*() const
{
char *copy = new char [strlen(data) + 1] ; strcpy(copy, data); return copy;
}
Приведенная в качестве примера реализация безопасна, поскольку она возвращает указатель на память, содержащую копию данных, на которые указывает указатель объекта String. Изменение значения объекта String посредством возвращаемого этой функцией указателя невозможно. За безопасность, как всегда, приходится платить: данная версия String: :operator char* медленнее, чем простая версия, приведенная выше. При вызове этой функции нужно использовать delete для возвращаемого указателя.
Если вы считаете, что такая версия operator char* чересчур медленна, или вас нервирует (по понятным причинам) потенциальная возможность утечки памяти, то представляется целесообразным использовать другое исправление - возвращать указатель на const char:
class String { public:
operator const char *() const;
с
};
inline String::operator const char*() const { return data; }
Эта функция быстра и безопасна, и, хотя ее спецификация отличается от изначальной, для большинства приложений она вполне подойдет. Кроме того, такой Подход по духу близок решению проблемы string/char*, предложенному Комитетом по стандартизации С++: стандартный тип содержит функцию-член c_str, Возвращающую версию const char* данной строки string. Более подробная Информация о стандартном типе string приводится в правиле 49.
Указатель - это не единственный способ возврата дескриптора внутренних Данных. Ссылки предоставляют столь же широкое поле для злоупотреблений. Вот Наиболее распространенный способ действий, опять подразумевающий исполь-ование класса String:
Предыдущая << 1 .. 47 48 49 50 51 52 < 53 > 54 55 56 57 58 59 .. 105 >> Следующая
Реклама
Авторские права © 2009 AdsNet. Все права защищены.
Rambler's Top100