【问题标题】:What is the proper way to create an exception hierarchy?创建异常层次结构的正确方法是什么?
【发布时间】:2015-03-14 19:48:04
【问题描述】:

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

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

【问题讨论】:

  • 我想知道所有这些在一天结束时是否真的有用。您是否有代码或期望客户的代码不仅仅捕获std::exceptionBaseException,并根据抛出的异常类型采取不同的行动?
  • 多重继承没有争议,但要有效使用,虚拟基类应该是抽象的,并且不包含任何数据成员。
  • @ChristianHackl 是的,客户端代码可能想要捕获 IOError 而不是 MemoryError 作为示例。无论哪种方式 - 我对讨论异常层次结构的有用性不感兴趣,只是在 C++ 中实现异常层次结构的正确方法。我对替代方案不感兴趣,或者为什么我正在做的是一个坏主意。抱歉听起来很激进,但是当我对理论框内的答案明确感兴趣时,SO 人群有一种令人讨厌的跳出框框思考的倾向。
  • 您看过Boost.Exception 及其文档中的讨论吗?
  • @orlp 在这种情况下,我认为没有人在“跳出框框思考”。您的问题(尽管不是针对异常层次结构,而是针对一般继承)在标准化过程中得到了认真考虑。最后,我们认为,由于具有数据成员的类的虚拟继承没有有效用途,因此为了解决问题而使语言复杂化是没有意义的。

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


【解决方案1】:

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

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. 每个应该可构造的异常都定义了一个公共构造函数,它直接为每个祖先调用2中定义的构造函数。

  4. 所有拷贝构造函数都应该是noexcept。


使用此的示例层次结构:

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

【讨论】:

    猜你喜欢
    • 2014-11-03
    • 2015-05-22
    • 2014-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多