如何正确抛出一个不仅需要构造函数的异常?

Cla*_*diu 10 c++ exception throw

我有一个Exception类,我想在它抛出之前设置更多信息.我可以创建Exception对象,调用它的一些函数然后抛出它而不用它的任何副本吗?

我发现的唯一方法是抛出一个指向对象的指针:

class Exception : public std::runtime_error
{
public:
    Exception(const std::string& msg) : std::runtime_error(msg) {}
    void set_line(int line) {line_ = line;}
    int get_line() const {return line_;}
private:
    int line_ = 0;
};

std::unique_ptr<Exception> e(new Exception("message"));
e->set_line(__LINE__);
throw e;
...
catch (std::unique_ptr<Exception>& e) {...}
Run Code Online (Sandbox Code Playgroud)

但通常避免通过指针抛出异常,那么还有其他方法吗?

还可以选择通过构造函数设置所有选项,但如果将更多字段添加到类中并且您希望对要设置的字段进行细粒度控制,则可以快速变为不可扩展:

throw Exception("message"); // or:
throw Exception("message", __LINE__); // or:
throw Exception("message", __FILE__); // or:
throw Exception("message", __LINE__, __FILE__); // etc.
Run Code Online (Sandbox Code Playgroud)

Jos*_*ich 8

C++异常类应该是可复制的或至少是可移动的.在您的示例中,使类可复制是添加默认复制构造函数的问题:

Exception(Exception const&) = default;
Run Code Online (Sandbox Code Playgroud)

如果需要在异常类中封装一些不可复制和不可移动的状态,请将此类状态包装到std :: shared_ptr中.


Ale*_*nko 5

你可以创建一个数据保持类,比如ExceptionData.然后创建ExceptionData对象并调用它的方法.然后在ctor中创建Exception对象std::move,如:

ExceptionData data;
data.method();
throw Exception(std::move(data));
Run Code Online (Sandbox Code Playgroud)

当然,ExceptionData需要移动,你必须接受ExceptionData &&(rvalue参考)ctor .

如果你真的需要避免复制它会起作用,但对我来说这感觉就像是初步优化.想想你的应用程序中抛出异常的频率是多少,为此事情复杂化是非常值得的.


Kar*_*oll 2

使用 std::move 怎么样?

Exception e("message");
e.set_line(__LINE__);
throw std::move(e);
Run Code Online (Sandbox Code Playgroud)

或者,您可以创建一个类似 Java 的构建器,如下所示:

class ExceptionBuilder;

class Exception : public std::runtime_error
{
public:
    static ExceptionBuilder create(const std::string &msg);

    int get_line() const {return line_;}
    const std::string& get_file() const { return file_; }
private:
    // Constructor is private so that the builder must be used.
    Exception(const std::string& msg) : std::runtime_error(msg) {}

    int line_ = 0;
    std::string file_;

    // Give builder class access to the exception internals.
    friend class ExceptionBuilder;
};

// Exception builder.
class ExceptionBuilder
{
public:
    ExceptionBuilder& with_line(const int line) { e_.line_ = line; return *this; }
    ExceptionBuilder& with_file(const std::string &file) { e_.file_ = file; return *this; }
    Exception finalize() { return std::move(e_); }
private:
    // Make constructors private so that ExceptionBuilder cannot be instantiated by the user.
    ExceptionBuilder(const std::string& msg) : e_(msg) { }
    ExceptionBuilder(const ExceptionBuilder &) = default;
    ExceptionBuilder(ExceptionBuilder &&) = default;

    // Exception class can create ExceptionBuilders.
    friend class Exception;

    Exception e_;
};

inline ExceptionBuilder Exception::create(const std::string &msg)
{
    return ExceptionBuilder(msg);
}
Run Code Online (Sandbox Code Playgroud)

像这样使用:

throw Exception::create("TEST")
    .with_line(__LINE__)
    .with_file(__FILE__)
    .finalize();
Run Code Online (Sandbox Code Playgroud)