首页 > 解决方案 > c++,处理来自构造函数的异常

问题描述

我有一个从外部文件加载的类,所以理想情况下,如果加载失败,我希望它的构造函数从给定路径加载,如果找不到/不可读文件,我会抛出错误(从构造函数不是一个可怕的想法,请参阅ISO 的常见问题解答)。

但是,这有一个问题,我想以某种受控方式自己处理错误,并且我想立即执行此操作,因此我需要在该对象的构造函数周围放置一个 try-catch 语句……如果我这样做了也就是说,该对象未在 try 语句之外声明,即:

//in my_class.hpp
class my_class
{
    ...
public:
    my_class(string path);//Throws file not found, or other error error
    ...
};


//anywhere my_class is needed
try
{
   my_class my_object(string);
}
catch(/*Whatever error I am interesetd in*/)
{
   //error handling
}
//Problem... now my_object doesn't exist anymore

我已经尝试了很多方法来解决它,但我真的不喜欢其中任何一种:

首先,我可以使用指向 my_class 的指针而不是类本身:

my_class* my_pointer;
try
{
   my_class my_pointer = new my_class(string);
}
catch(/*Whatever error I am interesetd in*/)
{
   //error handling
}

问题是这个对象的实例并不总是在创建它的同一个对象中结束,所以正确删除所有指针很容易出错,此外,我个人认为让一些对象成为指针很难看对象,并让大多数其他对象成为“常规对象”。

其次,我可以以几乎相同的方式使用只有一个元素的向量:

std::vector<my_class> single_vector;
try
{
   single_vector.push_back(my_class(string));
   single_vector.shrink_to_fit();
}
catch(/*Whatever error I am interesetd in*/)
{
   //error handling
}

我不喜欢有很多单元素向量的想法。

第三,我可以创建一个空的仿构造函数并使用另一个加载函数,即

//in my_class.hpp
class my_class
{
    ...
public:
    my_class() {}// Faux constructor which does nothing
    void load(string path);//All the code in the constructor has been moved here
    ...
};


//anywhere my_class is needed
my_class my_object
try
{
   my_object.load(path);
}
catch(/*Whatever error I am interesetd in*/)
{
   //error handling
}

这行得通,但在很大程度上违背了拥有构造函数的目的,所以我也不是很喜欢这个。

所以我的问题是,在这些构造对象的方法中,哪些可能会在构造函数中引发错误,是最好的(或最不坏的)?有没有更好的方法来做到这一点?

编辑:你为什么不直接使用 try 语句中的对象

因为该对象可能需要在程序第一次启动时创建,并在很久以后停止。在最极端的情况下(在这种情况下我也确实需要),基本上是:

int main()
{
   try
   {
    //... things which might fail

    //A few hundred lines of code
   }
   catch(/*whaveter*/)
   {
   }
}

我认为这使我的代码难以阅读,因为 catch 语句与实际出错的地方相去甚远。

标签: c++exceptionconstructor

解决方案


一种可能性是将构造和错误处理包装在一个函数中,返回构造的对象。例子 :

#include <string>

class my_class {
public:
    my_class(std::string path);
};

my_class make_my_object(std::string path)
{
    try {
        return {std::move(path)};
    }
    catch(...) {
        // Handle however you want
    }
}

int main()
{
    auto my_object = make_my_object("this path doesn't exist");
}

但请注意,该示例是不完整的,因为不清楚当构造失败时您打算做什么。该catch块必须要么返回一些东西,要么抛出或终止。

  • my_class(std::string path)如果您可以返回一个不同的实例,一个具有“错误”或“默认”状态的实例,那么您可以在确定路径无效时将您的实例初始化为该状态。因此,在这种情况下,不需要try/块。catch
  • 如果您重新抛出异常,那么首先捕获它是没有意义的。在这种情况下,也不需要try/块,除非你想做一些额外的工作,比如日志记录。catch
  • 如果你想终止,你可以让异常不被捕获。同样,在这种情况下,不需要try/块。catch

这里真正的解决方案可能是根本不使用try/catch块,除非实际上存在错误处理,否则您不应该将其实现为my_class问题中不明显的部分(可能是回退路径?)。


推荐阅读