从构造函数捕获异常意味着我的实例之后超出了作用域

Catching exceptions from a constructor means that my instance is out of scope afterward

本文关键字:作用域 之后 实例 构造函数 捕获异常 意味着 我的      更新时间:2023-10-16

我有一个类,它的构造函数可能抛出异常。下面是一些捕获异常的代码:

try {
    MyClass instance(3, 4, 5);
}
catch (MyClassException& ex) {
    cerr << "There was an error creating the MyClass." << endl;
    return 1;
}

但是当然,try/catch之后的代码不能看到instance,因为它现在超出了作用域。解决这个问题的一种方法是分别声明和定义instance:

MyClass instance;
try {
    MyClass instance(3, 4, 5);
}
...

只是我的类没有合适的零参数构造函数。事实上,这里的这种情况是唯一一个这样的构造函数有意义的情况:MyClass对象是不可变的,也就是说它的数据成员在构造之后都不会改变。如果我要添加一个零参数构造函数,我需要引入一些实例变量,比如is_initialized_,然后在继续之前检查每个方法以确保该变量是true。对于这样一个简单的模式来说,这似乎太冗长了。

处理这种事情的习惯方法是什么?我需要忍受它并允许我的类的实例在初始化之前被声明吗?

你应该在 try块内做你需要做的一切:

try {
    MyClass instance(3, 4, 5);
    // Use instance here
}
catch (MyClassException& ex) {
    cerr << "There was an error creating the MyClass." << endl;
    return 1;
}

毕竟,只有在try块内,instance才被成功地创建,从而可以使用。

我想知道你的catch块是否真的在处理异常。如果你不能做任何事情来解决这种情况,你应该让它传播。

使用new:

动态分配实例
std::unique_ptr<MyClass> instance;
try
{
    instance.reset(new MyClass(3, 4, 5));
}
catch (const MyClassException& ex)
{
    std::cerr << "There was an error creating the MyClass." << std::endl;
    return 1;
}
// use instance as needed...

您可以使用一个通用的帮助函数来捕获异常和未来的std::optional(或boost::optional),以发出实例创建成功或失败的信号:

template< typename T, typename... Args >
std::optional< T > try_make( Args&&... args )
{
    try {
        return T{ std::forward< Args >( args )... };
    }
    catch( ... ) {
        return {};
    }
}

基本上这样使用:

auto instance = try_make< MyClass >(3, 4, 5);

其中instance现在是optional<MyClass>。要测试结果并将实例的可用性与错误情况分开也很简单:

if( auto instance = try_make< MyClass >( 3, 4, 5 ) ) {
    // use *instance, but this code is *not* in the try/catch block!
}
else {
    // creating the instance failed
}

当然异常信息会丢失,但是您可以选择一个不那么通用的函数,并根据您的需要在catch块中添加一些日志记录。

Remy答案的变体,但使用std::optional保存动态分配:

std::optional<MyClass> instance_opt;
try {
    // could use `instance = MyClass(3, 4, 5)`, but that requires you to write a move constructor
    instance_opt.emplace(3, 4, 5);
}
catch (const MyClassException& ex) {
    std::cerr << "There was an error creating the MyClass." << std::endl;
    return 1;
}
MyClass& instance = *instance_opt;
// use instance as needed...