Отсутствие виртуального деструктора, когда единственный производный класс не добавляет дополнительных переменных

У меня есть класс Literal, который на самом деле является оболочкой для (const int). Я хочу иметь второй класс PositiveLiteral, который наследуется от Literal, но имеет конструктор, утверждающий, что его значение положительное.

class Literal {
public:
    Literal(int x):x(x){}
    virtual ~Literal(){}
    // Other methods
private:
    const int x;
}

class PositiveLiteral : public Literal {
public:
    PositiveLiteral(int x):Literal(x) {
        assert(x > 0)
    }
}

Таким образом, функции, которые ожидают положительный литерал, могут просто принимать PositiveLiteral в качестве аргумента. Тогда мне не нужно вставлять в свой код явные утверждения, и, кроме того, там, где эти утверждения не работают, я сразу понимаю, почему.

В противном случае я не ожидаю наследования от Literal, за исключением этого случая. Тем не менее, поскольку существует наследование, я должен дать Literal виртуальный деструктор, чтобы избежать неопределенного поведения, которое кажется глупым, потому что у PositiveLiteral нет связанной с ним дополнительной информации, которой нет у Literal. Это просто способ поддерживать утверждение, не делая его явным.

Какой другой способ выполнить ту же задачу без необходимости использования виртуального метода в том, что должно быть простым классом-оболочкой?


person dspyz    schedule 11.12.2013    source источник


Ответы (1)


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


Настоящая проблема лежит на уровне дизайна. Поскольку верно то, что каждое значение PositiveLiteral является значением Literal, если у вас есть ссылка на Literal, которая на самом деле является PositiveLiteral, вы можете присвоить ей отрицательное значение.

В литературе и обсуждениях на форумах это когда-то было известно как проблема эллипса и круга, хотя сходство не очевидно.

Во-первых, чтобы прояснить проблему, только для неизменяемых значений PositiveLiteral является Literal. Дело не в том, что изменяемый PositiveLiteral является изменяемым Literal.

Затем практическое решение C++ состоит в том, чтобы обеспечить преобразование значений вместо использования наследования.

Например, это решение используется для интеллектуальных указателей.


Дополнение: я не увидел, что в коде ОП значение равно const. Так что такой проблемы нет.

Практическая проблема заключается в том, что по крайней мере один компилятор, Visual C++, имеет тенденцию глупо предупреждать о своей неспособности генерировать оператор присваивания копирования; его можно заткнуть, объявив приватным без реализации. :-)

person Cheers and hth. - Alf    schedule 11.12.2013
comment
Спасибо, я не совсем был в этом уверен (если я правильно понял, вы говорите, что даже при наличии полиморфизма мне не нужен виртуальный деструктор, если будет достаточно деструктора по умолчанию). Обратите внимание, что x является константой в моем классе Literal, поэтому значения действительно неизменяемы. Тем не менее, что вы подразумеваете под преобразованием стоимости? Я гуглил преобразование значений С++, и ни одна из первых пар ссылок здесь не подходит. - person dspyz; 11.12.2013
comment
@dspyz: о, я не видел, что это было const. второй не удалось увидеть это сегодня утром на SO. Мне определенно нужно больше кофе! - person Cheers and hth. - Alf; 11.12.2013
comment
преобразование стоимости - это то, где у вас есть, например. operator Literal() const в классе PositiveLiteral, чтобы обеспечить неявное преобразование в Literal. Его также можно выразить с помощью конструктора Literal, принимающего PositiveLiteral аргумент. - person Cheers and hth. - Alf; 11.12.2013
comment
На самом деле, можете ли вы подтвердить для меня: я смотрю на оба этих SO stackoverflow.com/questions/461203/ и этот пост, на который указывает принятый ответ: gotw.ca/publications/mill18.htm, и никто не упоминает, что вам не нужен виртуальный деструктор, когда достаточно деструктора по умолчанию. Они оба упустили это из виду? - person dspyz; 11.12.2013
comment
нет, не проглядели. обе ссылки говорят то же самое, что и я выше. однако, хотя в целом можно доверять тому, что говорит Херб Саттер (в конце концов, он является председателем международного комитета по стандартизации C++, а также главным архитектором компилятора Visual C++), по моему опыту, около половины ответов SO правдоподобны, но технически неверны, так что ТАК как авторитет не очень хорошая идея... - person Cheers and hth. - Alf; 11.12.2013
comment
Хорошо, но что, если я потенциально могу удалить через ссылку на базовый класс. Тогда что я могу сделать? По-прежнему кажется, что виртуальный dtor не нужен, потому что у PositiveLiteral нет дополнительных полей. - person dspyz; 11.12.2013
comment
Кроме того, я неправильно понял. То, что сказал Ура, было правильным, но моя интерпретация была неправильной. Это не имеет ничего общего с деструктором по умолчанию или нет. Это связано с тем, удаляются ли экземпляры полиморфно (этот комментарий предназначен для всех, кто читает это обсуждение). - person dspyz; 11.12.2013
comment
Любой код, который удаляется через ссылку на класс Bass, когда базовый класс не имеет виртуального деструктора, а объект относится к производному классу, является ошибкой. Не сам класс. Например, вывод из std::string и удаление с помощью указателя на std::string: ошибка заключается в коде удаления, а не в std::string. :-) Конечно, динамически выделять std::string особого смысла нет, так что он для этого не предназначен (принцип не платите за то, чем не пользуетесь). И я думаю, что то же самое для вашего класса. - person Cheers and hth. - Alf; 11.12.2013
comment
Это имеет большой смысл. Спасибо. Я знаком с синтаксисом C++ некоторое время и понимаю, как работают конструкции C++, но что касается дизайна, я все еще не понимаю, как правильно делать самые простые вещи. Сегодня я начал читать «Эффективный C++», и он ответил на многие мои вопросы. Но, придя из Java, мне все еще неловко из-за неопределенного поведения, и я боюсь делать что-либо, за что нельзя винить. - person dspyz; 11.12.2013