Cleaning data after exception on class constructor
为什么此代码不调用类析构函数中的CloseHandles?在代码测试中,我显式调用"((myClass*)pthis)->CloseHandles();",但变量m_bFinished的值错误。为什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | #include <windows.h> #include <exception> class MyClass { public: explicit MyClass( void **pThis) { *pThis = this; m_bFinished = false; //code open handle here //an error occurs throw new std::exception("Exception thrown!"); } ~MyClass() { if ( ! m_bFinished ) CloseHandles(); } void CloseHandles() { if ( m_bFinished ) return; //close handles here. m_bFinished = true; } private: bool m_bFinished; }; int main(int argc, char* argv[]) { MyClass * pMyClass; void * pThis = NULL; try { pMyClass = new MyClass(&pThis); } catch(std::exception * e) { //delete pThis; if ( pThis ) { ((MyClass*)pThis)->CloseHandles(); } } return 0; } |
因为类的析构函数在其构造函数抛出时不运行-对象尚未完全初始化。
另外,您实际上不是抛出
1 2 | // dynamically allocates std::exception and throws a pointer to it throw new std::exception("Exception thrown!"); |
编辑:我注意到你也捕捉到了一个指针,所以这不是问题所在。但是,没有采用字符串文字的
在任何情况下,如果一个构造函数可能在分配了一个原始资源之后抛出,那么您就有一个潜在的泄漏。
您需要将资源包装在管理它的类中——可能是一个智能指针或类似的raii包装器。并使用成员初始值设定项列表!
另一个选项是构造函数委托(C++中的新的11)。当一个对象的任何构造函数完成执行时,该对象被认为是完全构造的。这意味着,如果从委托给另一个构造函数的构造函数中抛出异常(在这里您将获取句柄),则将调用析构函数。
用一些代码说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | struct Handle { Handle() : handle(new int()) {} ~Handle() { delete handle; } int* handle; }; class MyClass { Handle h; MyFlass() : h() // handle initialized here { /**** code that may throw ****/ // this will properly close handles because // the destructors of already initialized // members (like h) will be called } ~MyClass() { /* not called if constructor throws */ } }; |
以及一个构造函数委托示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <iostream> class MyClass { private: int* handle; MyClass(int) // dummy parameter just for overloading : handle(new int()) { /* better not throw from here */ } public: MyClass() : MyClass(0) // handle initialized here { /**** code that may throw ****/ throw 42; } ~MyClass() { delete handle; std::cout <<"dtor!"; } }; int main() { try { MyClass m; } catch (int) {}; } |
输出为