我们最近遇到了将C++框架移植到运行uClinux的ARM平台的问题,其中唯一受供应商支持的编译器是GCC 2.95.3.我们遇到的问题是,异常非常不可靠,导致一切从未被捕获到被不相关的线程(!)捕获.这似乎是一个记录在案的错误,即此处和此处.
经过一番考虑后,我们决定消除异常,因为我们已经达到了异常会对正在运行的应用程序造成大量破坏的程度.现在主要关注的是如何管理构造函数失败的情况.
我们尝试了懒惰的评估,其中每个方法都能够实例化动态资源并返回状态值,但这意味着每个类方法都必须返回一个返回值,这会在代码中产生很多 ifs并且在方法中非常烦人这通常不会导致错误.
我们研究了添加静态create方法,该方法返回指向创建对象的指针,如果创建失败则返回NULL,但这意味着我们不能再将对象存储在堆栈中,如果需要,仍需要传入对状态值的引用对实际错误采取行动.
根据谷歌的C++风格指南,他们不使用异常,只在他们的构造函数中做一些简单的工作,使用init方法进行非平凡的工作(在构造函数中做工作).但是,在使用这种方法时,我无法找到有关它们如何处理构造错误的信息.
有没有人在这里试图消除异常并提出一个很好的解决方案来处理施工失败?
Ste*_*sop 13
通常,对于堆栈中的对象,最终会得到这样的代码:
MyClassWithNoThrowConstructor foo;
if (foo.init(bar, baz, etc) != 0) {
// error-handling code
} else {
// phew, we got away with it. Now for the next object...
}
Run Code Online (Sandbox Code Playgroud)
这适用于堆上的对象.我假设您使用返回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
}
Run Code Online (Sandbox Code Playgroud)
显然,如果你可以,使用智能指针来节省必须记住删除,但如果你的编译器不能正确支持异常,那么你可能无法获得Boost或TR1.我不知道.
您也可能希望以不同方式构造逻辑,或者抽象组合的new和init,以避免在处理多个对象时深层嵌套的"箭头代码",并在两个故障情况之间共同处理错误.以上只是最辛苦形式的基本逻辑.
在这两种情况下,构造函数都将所有内容设置为默认值(它可以采用一些参数,前提是它对这些参数的作用不可能失败,例如,如果它只存储它们).然后,init方法可以执行可能失败的实际工作,并且在这种情况下返回0成功或任何其他失败值.
您可能需要强制执行整个代码库中的每个init方法以相同的方式报告错误:您不希望某些返回0成功或负错误代码,一些返回0成功或一个肯定的错误代码,一些返回bool,一些返回按值的对象,具有解释故障的字段,一些设置全局错误等.
您也许可以在线快速浏览一些Symbian类API文档.Symbian使用C++而没有例外:它确实有一个名为"Leave"的机制,它部分地弥补了这一点,但是从构造函数中保留是无效的,所以在设计非失败构造函数和推迟失败方面你有相同的基本问题对init例程的操作.当然使用Symbian时,init例程被允许保留,因此调用者不需要我在上面指出的错误处理代码,但是就C++构造函数和附加init调用之间的拆分工作而言,它是相同的.
一般原则包括: