2012-03-28 2 views
7

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

template<typename T> 
class SomewhatSafeVector : public std::vector<T> 
{ 
public: 
    typedef std::vector<T> base; 

    T& operator[](unsigned n) { 
     if (n >= base::size()) 
     { 
      throw IndexOutOfBounds(); 
     } 
     return base::operator[](n); 
    } 
}; 
+1

Неважно, хорошо ли это или нет, но вы все равно не должны выводить из стандартных библиотек. Более того, если у вас есть проблемы с доступом к динамическим контейнерам в пределах их границ, вам может захотеться взглянуть на ваше алгоритмическое мышление с большой картинкой (подумайте «0-1-много» и «диапазоны»), так как доступ вне границ обычно * логическая * ошибка. –

+4

Я думаю, что в вашем конкретном примере наследование не является изрядно изящным решением, поскольку наследование предназначено для повторного использования интерфейса, а не для повторного использования. Вы явно не используете интерфейс повторно, так как ваш 'operator []' выдает исключение, которое 'std :: vector' не будет. Если вы хотите повторно использовать код, просто используйте простые общие функции или (как в этом случае), сделайте 'std :: vector' членом' SomewhatSafeVector'. –

+0

@ KerrekSB: Во-первых, почему бы и нет? Во-вторых, у меня таких проблем нет. Но я думаю, что проверенные контейнеры с ограничениями были бы хорошей идеей для учебных и отладочных целей. –

ответ

6

Я всегда слышал, что вы не должны наследовать от класса без виртуальных деструкторов

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

Вы можете отлично использовать наследование без деструктора virtual в базовом классе. С другой стороны, если базовый класс вообще не имеет метода virtual, наследование, вероятно, является неправильным инструментом для задания. Например: в вашем случае, если я использую SafeVector<T> sv; sv[3];, тогда это безопасно, однако, если я это делаю std::vector<T>& v = sv; v[3];, это не так ... это потому, что вы просто скрываете метод базового класса, а не переопределяете его (выверните свое предупреждение уровень, они дадут вам знать).

Правильный способ здесь - использовать композицию, а затем создать методы пересылки для элемента реализации для тех методов, которые вы действительно используете. На практике это становится утомительным, потому что C++ не поддерживает делегирование (using attribute.insert;), так что многие прибегают к наследованию ...

Другой альтернативой является предоставление более безопасных методов в качестве бесплатных методов, поскольку вы всегда можете добавлять бесплатные методы без ограничений. Это может показаться менее идиоматичным для людей с мышлением «OO», и некоторые операторы не могут быть настолько добавлены.

5

Если вы не намерены использовать класс полиморфного (без удаления базовых указателей на производные объекты), то она не является Неопределенное поведение.

Ссылка:

C++ 03 Стандарт: 5.3.5 Удалить

5.3.5/1:

Оператор удаления выражение разрушает наиболее производный объект (1.8) или массив, созданный новым выражением.
удалить выражение:
:: выбрать удаление CAST-выражение
:: выбрать удалить [] литая выражение

5.3.5/3:

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

4

Вы можете использовать этот объект полиморфно, вы просто можете 't delete это полиморфно. Если вы не удаляете указатели на объекты своего класса через std::vector<>*, тогда вы в безопасности.

Помимо: Вы могли бы упростить ваш operator[] таким образом:

T& operator[](unsigned n) { return this->at(n); } 
2

Да, если вы никогда не использовать полиморфизм (то есть, никогда не вентиляционный ссылка или указатель), нет никакого способа, чтобы выполнить опасное разрушение ,

Обычно классы Mixin используются таким образом, и CRTP редко подразумевает виртуальный деструктор, чтобы назвать пару шаблонов.

Смежные вопросы