Программирование >>  Оптимизация возвращаемого значения 

1 ... 56 57 58 [ 59 ] 60 61 62 ... 96


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

Эти принципы похожи на принципы открытого наследования (см. правило 35). Разрешается преобразовывать объект производного класса в объект базового класса, но не наоборот, и с объектом производного класса можно делать все, что и с объектом базового класса, но обычно с объектом производного класса допускается делать что-то еше. При реализации интеллектуальных указателей можно воспользоваться этой схожестью, создав каждый интеллектуальный указатель на класс Т при помоши открытого наследования от соответствующего интеллектуального указателя на класс const-T (см. рис. 5.6).


Рис. 5.6

template<class Т> class SmartPtrToConst {

protected: union {

const T* constPointee; T* pointee;

template<class T> class SmartPtr:

public SmartPtrToConst<T> {

Интеллектуальные указатели

на const объекты.

Обычные функции - члены

интеллектуального указателя.

Для доступа

к SmartPtrToConst.

Для доступа к SmartPtr.

Интеллектуальные указа- тели на не-const объекты.

/ Элементов данных нет.



При такой реализации интеллектуальный указатель на объект не-const-T должен содержать обычный указатель на не-const-T, а интеллектуальный указатель на const-T - обычный указатель на const-T. Можно было бы сделать это, поместив в базовый класс обычный указатель на const-T, а в производный -обычный указатель на не-const-T. Но получилось бы расточительно, так как объекты SmartPtr содержали бы тогда два обычных указателя: один, унаследованный от SmartPtrToConst, а другой из самого SmartPtr.

Эту проблему нетрудно разрешить при помощи объединения (union) - старого оружия языка С, которое может быть столь же полезно и в C+-I-. Объединение является защищенным, поэтому оба класса имеют к нему доступ, и оно содержит оба необходимых типа обычных указателей. Объекты SmartPtrToConst<T> используют указатель constPointee, а объекты SmartPtr<T> - указатель pointee. Таким образом, вы имеете преимущества двух различных указателей, отводя память только под один. (См. еще один аналогичный пример в правиле 10.) В этом и заключается красота объединений. Конечно же, функции-члены должны ограничиваться использованием только соответствующего указателя, но компиляторы не могут наложить такое ограничение, и это делает использование объединений рискованным.

При такой реализации вы получаете нужное поведение:

SmartPtr<CD> pCD = new CD( Famous Movie Themes );

SmartPtrToConst<CD> pConstCD = pCD; Нормально.

Оценка

На этом завершается рассмотрение интеллектуальных указателей, но напоследок зададимся вопросом: а имеет ли смысл их использовать, в особенности, если ваши компиляторы не поддерживают шаблоны функций-членов?

Часто интеллектуальные указатели стоят возможного риска. Например, код для подсчета ссылок в правиле 29 благодаря их применению становится намного проще. Кроме того, как показывает практика, иногда интеллектуальные указатели используются ограниченно, и большинство возможных проблем (проверка на равенство нулю, преобразование к обычным указателям, преобразования типов, основанные на наследовании, и поддержка указателей на const) при этом не возникают. В целом, конечно, реализация, понимание и поддержка интеллектуальных указателей довольно сложны. Код, использующий интеллектуальные указатели, труднее отлаживать, чем код на основе обычных указателей. Как бы вы ни пытались, у вас не получится разработать универсальный интеллектуальный указатель, который сможет заменить его обычный аналог незаметно для пользователя.

Тем не менее, интеллектуальные указатели позволяют выполнять в программе действия, которые было бы сложно реализовать иным способом. Если такие указатели использовать разумно, они будут полезны каждому программисту на языке С++.



Правило 29. Используйте подсчет ссылок

Подсчет ссылок - это метод, который позволяет нескольким объектам, имеющим одинаковое значение, хранить его в одном и том же месте. Он обычно применяется по двум причинам. Во-первых, чтобы упростить jeT системных ресурсов для объектов в Kje. После выделения памяти для объекта при помощи оператора new важно отслеживать владельца этого объекта, так как только владелец отвечает за вызов оператора delete для удаления объекта. Но владелец объекта может меняться в процессе работы программы (например, при передаче указателей в качестве параметров), поэтому отслеживание владельца объекта - достаточно сложная задача. Облегчить ее выполнение можно при помощи таких классов, как auto ptr (см. правило 9), но практика показала, что в большинстве программ все еще не удается правильно реализовать данный метод. Подсчет ссылок избавляет программиста от необходимости отслеживать владельца объекта, поскольку в этом слзае объект сам является своим владельцем и автоматически самоуничтожается, если никто больше его не использует. Таким образом, подсчет ссылок по сути является простой формой сборки мусора (garbage collection).

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

Как и большинство простых идей, она имеет множество нюансов. В деталях и заключена успешная реализация подсчета ссылок. Но прежде чем углубиться в детали, нужно овладеть основами. Лучше всего начать с рассмотрения того, как можно создать несколько объектов с одним и тем же значением. Вот один из способов:

class String { Стандартный тип string не

public: обязательно должен использовать

методы из этого правила.

String(const char *value = ); StringSc operator = (const StringSc rhs) ,-

private:

char *data;

string a, b, c, d, e;

a = b = c = d = e = Hello ;

Очевидно, что объекты a-e имеют одно и то же значение Hello . Его представление зависит от реализации класса String, но обычно каждый объект String имеет собственную копию этого значения. Например, оператор присваивания класса string может быть реализован так:

StringSc String: :operator= (const StringSc rhs) {



1 ... 56 57 58 [ 59 ] 60 61 62 ... 96

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика