创建异常层次结构的正确方法是什么?

时间:2015-03-14 19:48:04

标签: c++ exception c++11 hierarchy

I understand that to properly catch exceptions using multiple inheritance I need to use virtual inheritance.
我不一定主张有争议地使用多重继承,但我不想设计使其无法使用的系统。请不要分散这个问题来提倡或攻击使用多重继承。

因此,假设我有以下基本异常类型:

class BaseException : public virtual std::exception {
public:
    explicit BaseException(std::string msg)
    : msg_storage(std::make_shared<std::string>(std::move(msg))) { }

    virtual const char* what() const noexcept { return msg_storage->c_str(); }

private:
    // shared_ptr to make copy constructor noexcept.
    std::shared_ptr<std::string> msg_storage;
};

BaseException创建例外层次结构的正确方法是什么?


我的问题在于构造异常类型。理想情况下,每个异常只构造其父级,但由于虚拟继承,这是不可能的。一个解决方案是构建链中的每个父母:

struct A : public virtual BaseException {
    explicit A(const std::string& msg) : BaseException(msg) { }
};

struct B : public virtual A {
    explicit B(const std::string& msg, int code)
    : BaseException(msg), A(msg), code_(code) { }

    virtual int code() const { return code_; }

private:
    int code_;
};

struct C : public virtual B {
    explicit C(const std::string& msg, int code)
    : BaseException(msg), A(msg), B(msg, code) { }
};

但这似乎非常重复且容易出错。此外,这使得异常类型的构造函数无法添加/更改子级传递的信息,然后再传递给各自的父级。

1 个答案:

答案 0 :(得分:2)

我找到了一个合理的解决方案,据我所知,这个解决方案没有被打破。它使用稍微修改的基本异常类型:

struct BaseException : virtual std::exception {
    explicit BaseException(std::string msg)
    : msg_storage(std::make_shared<std::string>(std::move(msg))) { }

    virtual const char* what() const noexcept { return msg_storage->c_str(); }

protected:
    BaseException();

private:
    std::shared_ptr<std::string> msg_storage;
};

然后规则是:

  1. 每个例外都会从其父例外公开和虚拟地继承。

  2. 每个异常声明受保护的默认构造函数,定义一个初始化所有数据成员的受保护构造函数。

  3. 每个应该是 Constructible 的异常都定义了一个公共构造函数,它直接调用2中为每个祖先定义的构造函数。

  4. 所有复制构造函数都应该是noexcept。


  5. 使用此示例层次结构:

    // A regular derived exception.
    struct RuntimeError : virtual BaseException {
        RuntimeError(std::string msg) : BaseException(std::move(msg))  { }
    protected: RuntimeError() { }
    };
    
    // Derived exception with data member.
    struct OSError : virtual RuntimeError {
        OSError(std::string msg, int e) : BaseException(std::move(msg)), e(e)  { }
        virtual int error_code() const noexcept { return e; }
    
    protected:
        OSError();
        OSError(int e) : e(e) { }
    
    private:
        int e;
    };
    
    // Non-constructible exception type.
    struct FatalError : virtual RuntimeError {
    protected: FatalError() { }
    };
    
    // A composed exception type.
    struct FatalOSError : virtual FatalError, virtual OSError {
        FatalOSError(std::string msg, int e)
        : BaseException(std::move(msg)), OSError(e)  { }
    protected: FatalOSError() { }
    };