|
Программирование >> Оптимизация возвращаемого значения
(см. правило 27). Для успешной работы данного класса вы должны спроектировать все так, чтобы объекты RCOb j ect могли быть только динамическими. Обычные способы реализации этого обсуждаются в правиле 27, но есть и особые меры, которые будут применены в рассматриваемом примере и описаны в резюме к этому разделу. Чтобы воспользоваться новым базовым классом для подсчета ссылок, изменим класс StringValue: он будет наследовать свойства для подсчета ссылок от класса RCObject. class String { private: struct StringValue: public RCObject { char *data; StringValue(const char *initValue); -StringValue(); String::StringValue::StringValue(const char *initValue) { data = new char[strlen(initValue) + 1] ; strcpY(data, initValue); String::StringValue::-StringValue() { delete [] data; Эта версия класса StringValue почти идентична предыдущей. Только функции - члены класса StringValue больше не работают с полем refCount. Эти действия теперь выполняет класс RCObject. Не бледнейте, увидев, что вложенный класс (StringValue) наследует от класса (RCObj ect), не связанного с классом (String), внутри которого он находится. Хоть такой подход на первый взгляд кажется непонятным, все здесь совершенно нормально. Вложенный класс - это такой же класс, как и любой другой, поэтому он может наследовать от какого угодно класса. Спустя некоторое время подобные отношения наследования будут восприниматься вами как вполне естественные. Автоматизация работы со счетчиком ссылок Класс RCObj ect позволяет вам размещать счетчик ссылок и предоставляет функции - члены класса, при помощи которых можно работать со счетчиками ссылок, но вызовы этих функций должны вставляться в другие классы вручную. А вызов функций addRef erence и removeRef erence для объектов StringValue все еще осуществляется конструктором копирования и оператором присваивания класса String. Это не слишком удобно. Хотелось бы переместить и эти функции в какой-нибудь класс, освободив авторов таких классов, как string, от всех забот по подсчету ссылок. Можно ли это сделать? Должен ли язык С++ поддерживать повторное использование кода? Он может и делает это. Не существует простого способа сделать так, чтобы все операции по подсчету ссылок были выведены из прикладных классов, но есть способ удалить большинство из них из большинства классов. (В некоторых прикладных классах можно удалить весь код для подсчета ссылок, но, увы, наш класс String не является таким. Все дело портит одна функция-член, и я полагаю, что вы не слишком удивитесь, узнав, что это наш старый враг - не-const версия operator [ ]. Соберитесь с духом, мы в конце концов усмирим злодея.) Обратите внимание, что каждый объект String содержит указатель на объект StringValue, представляющий значение объекта String: class String { private: struct StringValue: public RCObject { ... }; StringValue *value; Значение объекта String. Если с одним из ссылающихся на объект указателей происходит что-нибудь интересное, вы должны оперировать полем refCount объекта StringValue. Что-нибудь интересное включает в себя копирование, присваивание или уничтожение указателя. Если бы был способ сделать, чтобы сам указатель как-то обнаруживал эти операции и автоматически выполнял необходимые действия с полем refCount, вы могли бы быть свободны. Увы, указатели - довольно тупые создания, и вероятность того, что они обнаружат что-либо, а тем более автоматически среагируют на найденное, очень невелика. К счастью, существует возможность заставить их работать лучше: заменить их объектами, которые действуют, как указатели, но делают больше, чем они. Такие объекты называются интеллектуальными указателями, и о них подробно рассказано в правиле 28. В данном слзае вам нужно знать только, что объекты интеллектуальных указателей, так же как и настоящие указатели (которые часто называются обычными указателями), поддерживают операции выбора элемента (->) и разыменования (*), и, как и обычные указатели, они имеют строго заданный тип: нельзя сделать так, чтобы интеллектуальный указатель на объект типа Т ссылался на объект другого типа. Вот шаблон для объектов, которые ведут себя как интеллектуальные указатели на объекты для подсчета ссылок; Класс шаблона для интеллектуальных указателей на объекты Т. Класс Т должен поддерживать интерфейс RCObject, обычно при помощи наследования от класса RCObject. template<class Т> class RCPtr { public: RCPtr{Т* realPtr = 0) ; RCPtr (const RCPtrSc rhs) ; -RCPtr(); RCPtrSc operator= (const RCPtrSc rhs) T* operator->() const; TSc operator* 0 const; private: T *pointee; void init( Cm. правило 28. См. правило 28. Обычный указатель, который эмулирует этот объект. Общий код инициализации. Этот шаблон позволяет объектам интеллектуальных указателей управлять тем, что происходит при их создании, присваивании и уничтожении. Когда происходят такие события, объекты могут автоматически выполнять соответствующие операции с полем refCount в объектах, на которые они указывают. Например, при создании объекта RCPtr счетчик ссылок для объекта, на который он указывает, должен быть увеличен. Нет необходимости обременять прикладных разработчиков требованием вр5ную выполнять такие CKjHbie операции, поскольку конструкторы RCPtr могут сделать это сами. Код для двух конструкторов почти идентичен (отличаются только списки инициализации членов), поэтому вместо того, чтобы писать их дважды, можно поместить этот код в закрытую функцию - член класса init и вызвать ее из обоих конструкторов: template<class Т> RCPtr<T>::RCPtr(Т* realPtr): pointee(realPtr) { init О ; template<class T> RCPtr<T>::RCPtr(const RCPtrSc rhs): pointee(rhs.pointee) { initO ; template<class T> void RCPtr<T>::init() { if (pointee == 0) { return; if (pointee->isShareable() == false) { pointee = new T(*pointee); pointee->addReference(); Если обычный указатель равен null, то интеллектуальный указатель тоже нулевой. Если значение не может использоваться совместно, скопировать его. Теперь существует новая ссылка на значение. Размещение общего кода в отдельной функции, такой как init, кажется блестящим решением, но его глянец тускнеет, если функция ведет себя некорректно, как это происходит в данном примере.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |