从其基类捕获的自己的异常类调用方法

Calling method from own exception class catched by its base class

本文关键字:异常 调用 方法 自己的 基类      更新时间:2023-10-16

我有两个函数a()b(),它们有自己的继承自std::logic_error的异常类(连续a_excb_exc)。

void a() { (...) throw a_exc(some_val) }
void b() { (...) throw b_exc(some_val) }
class a_exc : public std::logic_error
{
private:
  int foo;
public:
  a_exc(int val, const std::string& what_msg="Msg.")
    : std::logic_error(what_msg), foo(val) {}
  void show() { //show foo }
}
class b_exc : public std::logic_error
{
private:
  std::string bar;
public:
  a_exc(std::string val, const std::string& what_msg="Msg.")
    : std::logic_error(what_msg), bar(val) {}
  void show() { //show bar }
}

假设我有以下部分代码:

try {
  a(); 
  b(); 
}
catch (const std::logic_error& e)
{
    e.what();
    // e.show();
}

catch (const std::logic_error& e)同时捕获a_excb_exc。当然,这个块不能使用e.show(),因为捕获的对象是std::logic_error

我的问题是。我想知道当捕获的异常是a_excb_exc时,是否有机会在std::logic_error捕获块中调用show()方法。我知道,如果我为a_excb_exc创建单独的捕获块,调用show()是可能的,但我想使用一个捕获块来调用此方法。这可能吗?

可以,只要show()const成员函数:

catch (const std::logic_error& e)
{
    e.what();
    if(const a_exc* a = dynamic_cast<const a_exc*>(&e))
        a->show();
    else if(const b_exc* b = dynamic_cast<const b_exc*>(&e))
        b->show();
}

在Coliru上观看。但是,在catch异常处理程序中调用其他可能throw的函数通常是一个坏主意。

关于设计的几点思考

在catch块中查询异常类型在逻辑上与简单地提供两个catch块没有什么不同。

说明:

catch(X& x)
{
  if (dynamic_cast<Y*>(&x)) {
    // it's a Y
  }
  if (dynamic_cast<Z*>(&z)) {
    // it's a Z
  }
  else {
    // it's an X
  }
}

在逻辑上与

相同:
catch(Y& t)
{
  // it's a Y
}
catch(Z& z)
{
  // it's a Z
}
catch(X& x)
{
  // it's an X
}

除了第二个更清晰,更可维护,并且在后续副本上防止无意的切片。

第一个是使用"代码查找代码",这总是一个等待发生的维护灾难。

你的问题引发了更多的问题:

  1. a_excb_exc是两种相同的错误吗?如果是这样,这就支持多态基类,您可以优先捕获std::logic_error

  2. 你真的需要show()方法吗?您可以简单地在构造函数中构建what字符串,并将该字符串传递给std::logic_error的构造函数吗?如果可能的话,这是我推荐的路线。当您开始向异常添加特殊接口时,由于必须了解该接口,您就污染了整个代码库。如果你正在编写一个库,那么你已经污染了使用你的库的每个应用程序。

  3. 假设您确实需要show,并且a_excb_exc确实是两种相同的错误,我们仍然可以避免多态性。也许我们可以将'show'消息作为一个字符串,并在构造函数中构建它。现在只有数据了。

(完整)使用多态基类的例子(a_excb_exc种相同的东西)

#include <stdexcept>
#include <string>
struct showable_logic_error : std::logic_error
{
    using std::logic_error::logic_error;
    virtual void show() const = 0;
};
class a_exc : public showable_logic_error
{
private:
    int foo;
public:
    a_exc(int val, const std::string& what_msg="Msg.")
    : showable_logic_error(what_msg)
    , foo(val)
    {}
    void show() const override
    {
        //show foo
    }
};
class b_exc : public showable_logic_error
{
private:
    std::string bar;
public:
    b_exc(std::string val, const std::string& what_msg="Msg.")
    : showable_logic_error(what_msg)
    , bar(val)
    {}
    void show() const override
    { //show bar
    }
};
void a() { throw a_exc(1); }
void b() { throw b_exc("b"); }
int main()
{
    try
    {
        a();
    }
    catch(showable_logic_error const& e)
    {
        e.show();
    }
}

不需要多态性的完整示例:

#include <stdexcept>
#include <string>
#include <sstream>
struct message_builder
{
    template<class T>
    static std::string build_what(const std::string& whatstr, T&& info)
    {
        std::ostringstream ss;
        ss << whatstr << " : " << info;
        return ss.str();
    }
};
class a_exc
: public std::logic_error
, private message_builder
{
public:
    a_exc(int val, const std::string& what_msg="Msg.")
    : std::logic_error(build_what(what_msg, val))
    {}
};
class b_exc
: public std::logic_error
, private message_builder
{
private:
    std::string bar;
public:
    b_exc(std::string val, const std::string& what_msg="Msg.")
    : std::logic_error(build_what(what_msg, std::move(val)))
    , bar(val)
    {}
};
void a() { throw a_exc(1); }
void b() { throw b_exc("b"); }
int main()
{
    try
    {
        a();
    }
    catch(std::logic_error const& e)
    {
        e.show();
    }
}

您应该考虑创建派生类型:

struct show_exc : public std::logic_error
{
    virtual void show() = 0;
};
class a_exc : public show_exc
{
    int foo_;
public:
    virtual void show() override { /*...*/ };
};

,然后使用不同的catch:

catch (const show_exc& e) {
  // ..
}
catch (const std::logic_error& e) {
  // ..
}