Я пишу небольшую иерархию классов исключений для разрабатываемого приложения на C ++, и у меня возникают проблемы с косвенным выводом из std::runtime_error
, Вот код, аналогичный тому, что я написал до сих пор:
class RuntimeException : public virtual boost::exception, public virtual std::runtime_error {
public:
virtual ~RuntimeException() {}
RuntimeException() : runtime_error("A RuntimeException occurred.") {}
RuntimeException(const std::string& what) : runtime_error(what) {}
};
class IllegalArgumentException : public virtual RuntimeException {
public:
IllegalArgumentException() : RuntimeException("An IllegalArgumentException occurred.") {}
IllegalArgumentException(const std::string& what) : RuntimeException(what) {}
};
RuntimeException
класс компилируется без проблем, но IllegalArgumentException
отказывается от компиляции на VS2015, генерируя ошибку: no default constructor exists for class "std::runtime_error"
для обоих конструкторов IllegalArgumentException
, Это бросает вызов моему пониманию иерархий наследования C ++, так как я ожидал, что этот код хорошо скомпилируется.
Я понимаю, что IllegalArgumentException
должен компилировать, потому что, хотя это правда, что std::runtime_error
не имеет конструктора по умолчанию, его конструктор вызывается конструктором для RuntimeException
, Но очевидно, что это должно быть ложно, так как компилятор отклоняет его. Кажется, я хочу позвонить std::runtime_error
конструктор прямо из IllegalArgumentException
конструктор (ошибка компилятора исчезает, когда я это делаю), но это кажется неправильным, потому что тогда я буду вызывать конструктор для std::runtime_error
дважды: один раз в конструкторе для RuntimeException
и снова в конструкторе для IllegalArgumentException
,
Это безопасно и / или эффективно сделать? Если нет, то почему компилятор, кажется, поощряет это? я мог просто происходит от std::exception
и реализовать std::string
я как переменная-член, но я подумал, что будет проще получить из стандартного класса, который уже реализовал это. Это неправильный подход? Кроме того, тот факт, что я получаю практически из обоих boost:exception
а также std::runtime_error
способствуя этой проблеме?
Когда используешь virtual
наследование вызова конструктора virtual
база является обязанностью наиболее производный класс, а не ответственность любого промежуточного класса. Причина очевидна: использование virtual
Наследование указывает, что существует ожидание того, что на самом деле есть несколько производных классов, использующих базовый класс. Какой из этих производных классов будет отвечать за построение virtual
база?
Таким образом, конструктор любого из производных классов должен предоставить аргумент virtual
база, например:
IllegalArgumentException::IllegalArgumentException(std::string const& what)
: std::runtime_error(what)
, RuntimeException(what) {
}
Чтобы избежать наличия промежуточных баз, вызовите конструктор virtual
базовые классы, предназначенные для virtual
Наследование часто предоставляют конструктор по умолчанию. Конечно, это открывает возможность того, что самый производный класс неправильно полагается на надлежащий конструктор, вызываемый одной из его баз.
Других решений пока нет …