Наследование от классов без виртуальных деструкторов - PullRequest
7 голосов
/ 28 марта 2012

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

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);
    }
};

Ответы [ 4 ]

7 голосов
/ 28 марта 2012

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

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

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

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

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

5 голосов
/ 28 марта 2012

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

Ссылка:

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

5.3.5 / 1:

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

5.3.5 / 3:

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

4 голосов
/ 28 марта 2012

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

В сторону : Вы можете упростить operator[] таким образом:

T& operator[](unsigned n) { return this->at(n); }
2 голосов
/ 28 марта 2012

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...