|
Программирование >> Оптимизация возвращаемого значения
размещаемые в куче, обычно создаются в момент их определения и автоматически уничтожаются в конце их существования, поэтому достаточно запретить эти явные создания и уничтожения. Простейший способ не допустить такие вызовы - объявить конструкторы и деструкторы как закрытые, но это чрезмерное требование. Нет необходимости, чтобы они оба были закрытыми. Л5ше будет сделать деструктор закрытым, а конструкторы открытыми. Затем, так же как и в правиле 26, можно ввести привилегированную функцию псевдодеструктора, которая будет иметь доступ к настоящему деструктору. После этого для уничтожения созданных объектов клиенты станут вызывать псевдодеструктор. Если, например, нужно гарантировать, чтобы объекты, представляющие числа с неограниченной точностью, создавались только в Kje, можно сделать это так: class UPNumber { public: UPNumber(); UPNumber(int initValue); UPNumber(double initValue); UPNumber(const UPNumber& rhs) ; Псевдодеструктор (функция-член с атрибутом const, так как объекты с атрибутом const могут уничтожаться) . void destroy () const { delete this; } private: -UPNumber(); Пользователи этого класса тогда должны писать примерно следующий код: UPNumber п; Ошибка! (Здесь разрешается, но недопустимо, если позже деструктор п вызывается явно) . UPNumber *р = new UPNumber; Нормально. delete р; Ошибка! Попытка вызвать закрытый деструктор. p->destroy(); Нормально. В качестве альтернативы можно объявить все конструкторы как закрытые. Недостаток такого подхода состоит в том, что класс часто имеет несколько конструкторов, а автор класса должен помнить о том, чтобы объявить их все как закрытые. В их число может входить конструктор копирования и иногда также конструктор по умолчанию, если эти функции будут созданы компилятором. Функции, создаваемые компилятором, всегда являются открытыми. В результате проще объявить закрытым только деструктор, так как он всего один в классе. Ограничение доступа к деструктору класса или его конструкторам предотвращает создание объектов не в Kje. Но по причинами, изложенным в правиле 26, это также запрещает наследование и ограничение области действия (containment): class UPNumber {...}; class NonNegativeUPNumber: public UPNumber {...}; class Asset { private: UPNumber value; Объявляет закрытые конструкторы или деструктор. Ошибка! Деструктор или конструкторы не скомпилируются. Ошибка! Деструктор или конструкторы не скомпилируются. Ни одна из этих трудностей не является непреодолимой. Проблему наследования можно решить, сделав деструктор UPNumber закрытым (оставив конструкторы открытыми), и изменив классы, которые должны содержать объекты типа UPNumber, так, чтобы они содержали вместо этого указатели на объекты UPNumber: class UPNumber {...}; class NonNegativeUPNumber: public UPNumber {...}; class Asset { public: Asset(int initValue); -Asset(); Объявляет закрытый деструктор. Теперь нормально; производные классы имеют доступ к закрытым членам класса. private: UPNumber * value; Asset::Asset(int initValue) : value(new UPNumber(initValue): ( ... } Asset::-Asset() { value->destroy(); } Нормально. Также нормально. Определение, находится ли объекте куче Если вы решите следовать описанной стратегии, то должны уточнить, что подразумевается под понятием быть в куче . Если класс определен вышеуказанным образом, то допустимо определить объект NonNegativeUPNumber, находяший-ся не в куче. NonNegativeUPNumber п; Нормально. Теперь часть UPNumber объекта п типа NonNegativeUPNumber не будет находиться в куче. Приемлемо ли это? Ответ зависит от деталей структуры и реализации класса, но предположим, что это не допустимо, что все объекты типа UPNumber - даже части базового класса в производных объектах - должны находиться в куче. Как можно наложить это ограничение? Простого способа не существует. Конструктор UPNumber не может определить, вызывается ли он как часть базового класса объекта, расположенного в куче. То есть конструктор UPnumber не определяет, что следующие контексты различны: NonNegativeUPNumber *п1 = new NonNegativeUPNumber; В куче. NonNegativeUPNumber п2; Не в куче. Возможно, вы мне не верите. Возможно, думаете, что Л5Д1ше поиграть взаимодействием между оператором new, operator new и конструктором, вызываемым оператором new (см. правило 8). Возможно, вы думаете, что способны перехитрить их, изменив класс UPNumber следующим образом: class UPNumber { public: Исключение, которое генерируется при создании объекта не в куче. class HeapConstraintViolation {} ; static void * operator new{size t size); UPNumber(); private: static bool onTheHeap; Флаг в конструкторах, определяющий, создается ... / / ли объект в куче. Обязательное определение статического объекта класса. bool UPNumber::onTheHeap = false; void *UPNumber::operator new{size t size) onTheHeap = true; return::operator new(size) ; UPNumber::UPNumber() { if {!onTheHeap) { throw HeapConstraintViolation{) ; продолжить обычное создание объекта; onTheHeap = false; Сбросить флаг } для следующего объекта. Здесь не происходит ничего сложного. Основная идея - воспользоваться тем, что объект находится в Kje. Для выделения неинициализированной памяти применяется функция operator new, а затем вызывается конструктор для инициализации объекта в этой памяти. В частности, operator new устанавливает флаг onTheHeap равным true, и каждый из конструкторов проверяет данный флаг, чтобы определить, была ли неинициализированная память для объекта выделена при помощи operator new. Если нет, генерируется исключение
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |