2016-10-02 5 views
1

J'écris une petite hiérarchie de classes d'exception pour une application C++ je développe, et je vais avoir du mal à tirer indirectement de std::runtime_error. Voici le code analogue à ce que j'ai écrit jusqu'à présent:classe Dérivant de base virtuelle sans constructeur par défaut

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

La classe RuntimeException compile sans problème, mais IllegalArgumentException refuse de compiler sur VS2015, générant l'erreur: no default constructor exists for class "std::runtime_error" pour les deux constructeurs de IllegalArgumentException. Cela remet en cause ma compréhension des hiérarchies d'héritage C++, car je m'attendais à ce que ce code compile bien.

Ma compréhension est que IllegalArgumentExceptiondevrait compilez parce que, bien qu'il est vrai que std::runtime_error n'a pas de constructeur par défaut, son constructeur est appelé par le constructeur pour RuntimeException. Mais évidemment, ce doit être faux, car le compilateur le rejette. Il semble vouloir que j'appelle le constructeur std::runtime_error directement du constructeur IllegalArgumentException (l'erreur de compilateur disparaît quand je le fais), mais cela semble faux car alors j'appellerais le constructeur pour std::runtime_error deux fois: une fois dans le constructeur pour RuntimeException, et encore dans le constructeur pour IllegalArgumentException.

Est-ce sécuritaire et/ou efficace de faire? Si non, pourquoi le compilateur semble-t-il l'encourager? Je pourrais tirer juste de std::exception et mettre en œuvre moi-même std::string comme une variable membre, mais je pensais que ce serait plus facile de tirer d'une classe standard qui a déjà mis en oeuvre. Est-ce la mauvaise approche à adopter? En outre, le fait que je suis à la fois tirer pratiquement boost:exception et std::runtime_error contribuer à cette question?

Répondre

2

Lorsque vous utilisez l'héritage virtual l'appel constructeur de la base virtual est la responsabilité de la classe plutôt que la plus dérivée de la responsabilité d'une classe intermédiaire. La raison est évidente: l'utilisation de l'héritage virtual indique qu'il existe une attente qu'il existe réellement plusieurs classes dérivées utilisant la classe de base. Laquelle de ces classes dérivées serait responsable de la construction de la base virtual?

Ainsi, le constructeur de l'une des classes dérivées doit fournir un argument à la base virtual, par exemple:

IllegalArgumentException::IllegalArgumentException(std::string const& what) 
    : std::runtime_error(what) 
    , RuntimeException(what) { 
} 

Pour éviter d'avoir des bases intermédiaires appeler le constructeur des classes de base virtual destinés à virtual héritage fournissent souvent un constructeur par défaut. Bien sûr, cela ouvre la possibilité que la classe la plus dérivée repose à tort sur le bon constructeur appelé par l'une de ses bases.

+0

Merci pour cela, votre explication est très claire. À la lumière de cela, je vais très probablement finir par dériver de 'std :: exception' et utiliser mon propre' std :: string'. Cela semble le moyen le plus simple d'y aller. –