|
Программирование >> Полиморфизм без виртуальных функций в с++
11.4. Создание и копирование объектов Меня часто просили запретить некоторые операции в языке. Некоторые хотели оптимизировать реализацию классов, для чего необходимо снять разрешение на проведение над объектами классов таких операций, как копирование, наследование и распределение в стеке. В других случаях, когда с по.мощью объектов представлялись сущности реального мира, для обеспечения требуемой семантики не было нужды во всех операциях, которые поддерживает C-f-+. Ответ на все подобные просьбы был найден во время работы над версией 2.0. Если вы хотите что-то запретить, сделайте соответствующую операцию закрытой функцией-членом (см. раздел 2.10). 11.4.1. Контроль допустимости копирования Чтобы запретить копирование объектов класса X, достаточно сделать закры-ты.ми оператор присваивания и копирующий конструктор. class X { Х£с operator= (const Х&) ; присваивание X(const X&j; копирующий конструктор . . . public: X(int) ; . . . void f() { X a(l); правильно: можно создавать объекты класса X X b = а; ошибка: X::X(const Х&;) закрыт b = а; ошибка: X::operator=(const Х&) закрыт Конечно, внутри функций-членов класса X может копировать объекты данного класса, но в реальных ситуациях это допустимо, а иногда и необходимо. Не помню, кто первый додумался до этого решения; скорее всего, не я [Stroustrup, 1986, стр. 172]. Считаю неправильным, что операции копирования определены по умолчанию и во многих своих классах запрещаю копирование объектов. Однако присваивание по умолчанию и копирующие конструкторы перешли в С++ от С и эти возможности часто применяются. 11.4.2. Управление распределением памяти С помощью объявления ряда операций закрытыми можно добиться и других эффектов. Например, если объявить закрытым деструктор, то будет запрещено размещение объектов в стеке и в глобальной памяти, а также случайное употребление delete: class On free store { ~On free store(); закрытый деструктор - . . public: static void fгее(On free store* p) { delete p; } . . . On free store globl; ошибка: деструктор закрыт void f() { On free store loc; ошибка: деструктор закрыт On free store* p = new On free store; правильно . . . delete p; ошибка: деструктор закрыт On free store::fгее(p); правильно Разумеется, подобный класс, как правило, будет использоваться вместе с хорошо оптимизированным распределителем свободной памяти или при наличии некоторой семантики, выигрывающей оттого, что объект находится в свободной памяти. Противоположного эффекта - разрешения глобальных и локальных переменных при запрете на размещение объектов в свободной памяти - можно достичь с помощью необычного использования operator new (): class No free store { class Dummy { }; void* operator new(size t,Dummy); ... No free store glob2; правильно void g() { No free store loc; правильно No free store* p = new No free store; ошибка: No free store::operator new(size t) отсутствует 11.4.3. Управление наследованием Закрытый деструктор предотвращает также и наследование. Например: class D : public On free store { . . . D d; ошибка: нельзя вызвать закрытый деструктор базового класса Это позволяет классу с закрытым деструктором стать логическим дополнением абстрактного класса. Наследовать классу On f ree store невоз.можно, поэтому при вызовах виртуальных функций данного класса необязательно использовать механизм виртуализации. Однако я не думаю, что какой-нибудь из современных компиляторов выполняет такую опти.мизацию. Впоследствии Эндрю Кениг обнаружил, что можно запретить наследование даже вне зависимости от того, в какой памяти может размещаться объект: class Usable lock { friend Usable; private: Usable lock() {} class Usable : public virtual Usable lock { . . . public: Usable(); Usable(char*); .. . Usable a; class DD : public Usable { }; DD dd; ошибка: DD::DD() недоступен Usable lock::Usable lock() - закрытый член Этот пример основан на правиле, что производный класс может вызывать конструктор виртуального базового класса (явно или неявно). Однако такие примеры - скорее, предмет для интеллектуальных дебатов, чем для применения на практике. 11.4.4. Почленное копирование Первоначально присваивание и инициализация были по умолчанию определены как побитовое копирование. Это приводило к неприятностям, когда объект класса, в котором определен оператор присваивания, использовался в качестве члена класса, в котором такой оператор не был определен: class X {/*...*/ Х& operator=(const Х&); }; struct Y { X а; }; void f{Y yl, Y у2) { yl = У2; Здесь у2 . a копируется в yl. a побитово. Очевидно, что это неправильно и является результатом недосмотра при проектировании оператора присваивания и копирующего конструктора. После ряда споров и упреков со стороны Эндрю
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |