C+中的异常消除+;建设者 我们最近面临的问题是把我们的C++框架移植到运行uClinux的ARM平台上,其中只有供应商支持的编译器是GCC 2.95.3。我们遇到的问题是异常非常不可靠,导致从根本没有被捕获到被无关线程捕获(!)。这似乎是一个记录在案的bug,即和

C+中的异常消除+;建设者 我们最近面临的问题是把我们的C++框架移植到运行uClinux的ARM平台上,其中只有供应商支持的编译器是GCC 2.95.3。我们遇到的问题是异常非常不可靠,导致从根本没有被捕获到被无关线程捕获(!)。这似乎是一个记录在案的bug,即和,c++,exception,C++,Exception,经过深思熟虑,我们决定消除异常,因为我们已经到了异常对正在运行的应用程序造成很大损害的地步。现在主要关心的是如何管理构造函数失败的情况 我们尝试过,每个方法都能够实例化动态资源并返回一个状态值,但这意味着每个类方法都必须返回一个返回值,这会在代码中产生大量的ifs,并且在通常不会导致错误的方法中非常烦人 我们研究了添加一个静态create方法,该方法在创建失败时返回一个指向已创建对象的指针或NULL,但这意味着我们不能再在堆栈上存储对象了,如果要处理实际错误,仍然需要传入对状态值的引用 根据谷歌

经过深思熟虑,我们决定消除异常,因为我们已经到了异常对正在运行的应用程序造成很大损害的地步。现在主要关心的是如何管理构造函数失败的情况

我们尝试过,每个方法都能够实例化动态资源并返回一个状态值,但这意味着每个类方法都必须返回一个返回值,这会在代码中产生大量的ifs,并且在通常不会导致错误的方法中非常烦人

我们研究了添加一个静态create方法,该方法在创建失败时返回一个指向已创建对象的指针或NULL,但这意味着我们不能再在堆栈上存储对象了,如果要处理实际错误,仍然需要传入对状态值的引用

根据谷歌的C++风格指南,只在构造函数中做琐碎的工作,使用init方法进行非平凡工作()。然而,我找不到任何关于他们在使用这种方法时如何处理构造错误的信息


这里有没有人尝试过消除异常并提出处理构造失败的好解决方案?

关于谷歌参考(你找不到他们是如何处理构造函数中的错误的):


这一部分的答案是,如果他们只在构造函数中做一些琐碎的工作,那么就不会有错误。因为这项工作很琐碎,所以他们非常有信心(我确信有彻底的测试支持)不会抛出异常。

我想这在很大程度上取决于通常发生的异常类型。我的假设是,它们主要与资源相关。如果是这种情况,我以前在嵌入式C系统上使用的解决方案是在程序开始时分配/提交所有可能需要的资源。因此,我知道所有必需的资源都在执行时可用,而不是在运行期间。这是一个贪婪的解决方案,可能会干扰与其他软件的互操作性,但对我来说效果很好。

如果一个构造函数只做一些琐碎的事情,比如初始化POD变量(并隐式调用其他琐碎的构造函数),那么它就不可能失败。看,;另请参见。

通常,对于堆栈上的对象,您会得到如下代码:

MyClassWithNoThrowConstructor foo;
if (foo.init(bar, baz, etc) != 0) {
    // error-handling code
} else {
    // phew, we got away with it. Now for the next object...
}
这适用于堆上的对象。我假设您使用返回NULL而不是抛出的内容覆盖全局运算符new,以避免记住在任何地方都使用nothrow new:

MyClassWithNoThrowConstructor *foo = new MyClassWithNoThrowConstructor();
if (foo == NULL) {
    // out of memory handling code
} else if (foo->init(bar, baz, etc) != 0) {
    delete foo;
    // error-handling code
} else {
    // success, we can use foo
}
显然,如果可能的话,可以使用智能指针来避免记住删除,但是如果编译器不正确地支持异常,那么您可能会在获取Boost或TR1时遇到问题。我不知道

您可能还希望以不同的方式构造逻辑,或者抽象new和init的组合,以避免在处理多个对象时出现深度嵌套的“箭头代码”,并共同处理两种故障情况之间的错误。以上只是最为细致的基本逻辑

在这两种情况下,构造函数都将所有内容设置为默认值(它可以接受一些参数,前提是它对这些参数所做的操作不可能失败,例如,如果它只存储这些参数)。然后,init方法可以执行实际工作,这可能会失败,在本例中,返回0 success或任何其他失败值

您可能需要强制整个代码库中的每个init方法以相同的方式报告错误:您不希望一些方法返回0成功或负错误代码,一些方法返回0成功或正错误代码,一些方法返回bool,一些方法返回具有解释错误字段的值的对象,一些设置全局错误号等

您也许可以在线快速查看一些Symbian类API文档。Symbian使用C++没有例外:它确实有一种称为“Lead”的机制,它部分弥补了这个缺点,但是从构造函数中离开是无效的,因此在设计非失败构造函数和推迟失败操作到init例程方面,你有相同的基本问题。当然,使用Symbian,允许init例程离开,所以调用方不需要上面所指出的错误处理代码,但是就C++构造函数和附加init调用之间的拆分工作而言,它是相同的。 一般原则包括:

  • 如果您的构造函数希望以可能失败的方式从某处获取值,请将其推迟到init,并在ctor中保留默认值初始化
  • 如果您的对象持有指针,请在ctor中将其设置为null,并在init中将其设置为“正确”
  • 如果您的对象持有引用,请将其更改为(智能)指针,以便它可以以null开头,或者让调用方将值作为参数传递到构造函数中,而不是在构造函数中生成它
  • 如果您的构造函数有对象类型的成员,那么您就可以了。它们的CTOR也不会抛出,所以用通常的方式在初始值设定项列表中构造成员(和基类)是完全可以的
  • 确保跟踪设置的内容和未设置的内容,以便在init失败时析构函数工作
  • 除了构造函数、析构函数和init之外的所有函数都可以假定init已成功,前提是您为类编写文档,说明在调用init并成功之前,调用init以外的任何方法都是无效的
  • 您可以提供多个init函数,这与c不同
    struct error_type {
        explicit error_type(int code):code(code) { }
    
        operator bool() const {
            return code == 0;
        }
    
        int get_code() { return code; }
        int const code;
    };
    
    #define checked_construction(T, N, A) \
       T N; \
       if(error_type const& error = error_type(N.init A))
    
    struct i_can_fail {
        i_can_fail() {
            // constructor cannot fail
        } 
    
        int init(std::string p1, bool p2) {
            // init using the given parameters
            return 0; // successful
        } 
    };
    
    void do_something() {
        checked_construction(i_can_fail, name, ("hello", true)) {
            // alright. use it
            name.do_other_thing();
        } else {
            // handle failure
            std::cerr << "failure. error: " << error.get_code() << std::endl;
        }
    
        // name is still in scope. here is the common code
    }
    
    class MyClass
    {
    public:
        MyClass() : m_resource(NULL)
        {
            m_resource = GetResource();
        }
        bool IsValid() const
        {
            return m_resource != NULL;
        }
    private:
        Resource * m_resource;
    };
    
    MyClass myobj;
    if (!myobj.IsValid())
    {
        // error handling goes here
    }