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

 

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

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

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

ню
Классы и функции: реализация
class String { public:
char& operator[](int index) const { return datafindex); } private:
char *data;
};
String s = "I'm not constant"; S[O] = 'X';
const String cs = "I'm constant cs[0] = 'x' ;
Il Правильно, s неконстантно.
Il Модифицирует константную строку, //но компилятор этого не замечает.
Заметьте, что String: :operator[] возвращает результат по ссылке. Это означает, что пользователь данной функции получает другой идентификатор того же внутреннего элемента data [index], и такой идентификатор может быть Использован для модификации внутренних данных предположительно константного объекта. С подобной проблемой мы столкнулись ранее, но на этот раз «обвиняемый» - ссылка, а не указатель.
Общее решение подобного рода проблем аналогично решению проблемы указателей: либо сделать функцию неконстантной, либо переписать ее так, чтобы она не возвращала дескриптора. О том, как написать String: :operator [], чтобы он работал и с const-, и с не const-объектами, рассказано в правиле 21.
Практика показывает, что программистам приходится отслеживать на предмет возврата дескрипторов не только функции-члены с const. Даже функции без const должны «примириться» с тем фактом, что действие дескриптора истекает одновременно с окончанием срока жизни соответствующего объекта. Это может произойти быстрее, чем ожидает пользователь, особенно если объект временно создан компилятором.
Например, взгляните на функцию, возвращающую объект класса string:
String someFamousAuthor() {
switch (rand() % 3) {
-case 0 :
return "Margaret Mitchell'
case 1:
return "Stephen King";
Il Случайным образом выбирает Il и возвращает имя автора. // rand() имеется в <stdlib.h> // (и <cstdlib> - см. правило 49)
// Написала "Унесенные ветром", // настоящий классик.
// Его рассказы не давали уснуть // миллионам людей.
case 2:
return "Scott Meyers";
}
return "";
Il Гм! Этот пункт чем-то / / отличается от двух других. . . / / Мы никак не можем попасть сюда, но, увы, // любой путь исполнения в функции, возвращающей // значение, должен возвращать значение.
I
I
Правило 29
тшштшшшт
Давайте оставим на время в стороне вашу вполне обоснованную обеспокоенность тем, насколько «случайны» величины, возвращаемые rand, и, пожалуйста, будьте снисходительны к моей мании величия - попытке причислить себя к настоящим писателям. Вместо этого сфокусируемся на том, что значение, возвращаемое sorneFamousAuthor, - это временный об7>ект String. Время жизни таких объектов обычно простирается только до конца выражения, содержащего вызов создающей их функции, в данном случае - до конца выражения, содержащего вызов sorneFamousAuthor.
Рассмотрим еще один случай использования этой функции. Предполагается, что String объявляет функцию-член operator const char*, описанную выше:
const char *рс - sorneFamousAuthor ()• ; cout « рс; // Ой. . .
Хотите - верьте, хотите - нет, но предсказать с какой-либо определенностью, что будет делать этот код, вы не сможете. К тому времени, когда вы попытаетесь распечатать последовательность символов, на которую указывает рс, она уже будет не определена. Неопределенность объясняется особой последовательностью событий, имеющей место при инициализации рс:
1. Создается временный объект String, который должен содержать значение, возвращаемое функцией sorneFamousAuthor.
2. Затем он конвертируется в const char* посредством operator const ^ char* из String, а рс инициализируется результирующим указателем.
3. Временный объект String удаляется, что означает вызов деструктора. В деструкторе удаляется указатель data (код приведен в правиле 11). Однако data указывает на ту же память, что и рс, поэтому рс теперь указывает на память с неопределенным содержимым.
Поскольку рс был инициализирован дескриптором временного объекта, а временные объекты вскоре после своего создания удаляются, дескриптор становится недействительным до того, как рс получает возможность с ним что-нибудь делать. В каком-то смысле рс «мертв еще до своего появления на свет». Вот какую опасность таят в себе дескрипторы временных объектов.
Таким образом, для функций-членов, содержащих const, возврат дескрипторов - опрометчивое решение, поскольку оно приводит к разрушению абстракции. Возврат дескриптора может привести к проблемам даже для функции-члена, не содержащей const, особенно если используются временные объекты. Дескрипторы, как и указатели, могут «зависать», и, так же как вы избегаете висящих указателей, следует избегать и висящих дескрипторов.
Однако нет причин быть чрезмерно осторожными. В нетривиальной программе нельзя устранить все возможности появления висящих указателей и редко когда можно полностью гарантировать отсутствие висящих дескрипторов. И все-таки, если вы будете избегать возвращения дескрипторов без особых на то причин, выиграют и ваши программы, и ваша репутация.
НІ
EEEHHHiHBIlIi Классы и функции: реализация
class Person { public:
Address * personAddress() { return &address; }
Правило 30. Не используйте функции-члены, возвращающие неконстантные указатели или ссылки на члены класса с более ограниченным доступом
Предыдущая << 1 .. 48 49 50 51 52 53 < 54 > 55 56 57 58 59 60 .. 105 >> Следующая
Реклама
Авторские права © 2009 AdsNet. Все права защищены.
Rambler's Top100