我知道如果在构造函数中抛出异常,析构函数将不会被调用(简单类,不继承).因此,如果在构造函数中抛出异常,并且有一些堆内存未被清除的机会.那么这里最好的做法是什么?让我们假设我必须在构造函数中调用一些函数,它可能会抛出异常.在这种情况下,我总是使用共享指针吗?有什么办法谢谢!
解决方法
我会坚持
RAII成语.
如果您避免“裸”资源(如运算符新,裸指针,裸互斥体等),并将所有内容都包含在具有适当RAII行为的容器或类中,那么即使存在异常,也不会有您所描述的问题.
也就是说,不要在构造函数中获取裸体资源.而是创建一个本身跟随RAII的对象的实例.这样,即使您的构造函数失败(即创建实例的构造函数),将调用初始化对象的析构函数.
所以,这是不好的做法:
- #include<iostream>
- #include<stdexcept>
- struct Bad {
- Bad() {
- double *x = new double;
- throw(std::runtime_error("the exception was thrown"));
- }
- ~Bad() {
- delete x;
- std::cout<<"My destructor was called"<<std::endl;
- }
- double *x;
- };
- int main() {
- try {
- Bad bad;
- } catch (const std::exception &e) {
- std::cout<<"We have a leak! Let's keep going!"<<std::endl;
- }
- std::cout<<"Here I am... with a leak..."<<std::endl;
- return 0;
- }
输出:
- We have a leak! Let's keep going!
- Here I am... with a leak...
与这个愚蠢和愚蠢的良好实施相比:
- #include<iostream>
- #include<stdexcept>
- struct Resource {
- Resource() {
- std::cout<<"Resource acquired"<<std::endl;
- }
- ~Resource() {
- std::cout<<"Resource cleaned up"<<std::endl;
- }
- };
- struct Good {
- Good() {
- std::cout<<"Acquiring resource"<<std::endl;
- Resource r;
- throw(std::runtime_error("the exception was thrown"));
- }
- ~Good() {
- std::cout<<"My destructor was called"<<std::endl;
- }
- };
- int main() {
- try {
- Good good;
- } catch (const std::exception &e) {
- std::cout<<"We DO NOT have a leak! Let's keep going!"<<std::endl;
- }
- std::cout<<"Here I am... without a leak..."<<std::endl;
- return 0;
- }
输出:
- Acquiring resource
- Resource acquired
- Resource cleaned up
- We DO NOT have a leak! Let's keep going!
- Here I am... without a leak...
我的观点如下:尝试将需要解放的所有资源封装到构造函数不抛出的类中,析构函数正确地释放资源.然后,在析构函数可能抛出的其他类中,只需创建包装资源的实例,并将保证获取的资源包装器的析构函数将被清理.
以下可能是一个更好的例子:
- #include<mutex>
- #include<iostream>
- #include<stdexcept>
- // a program-wide mutex
- std::mutex TheMutex;
- struct Bad {
- Bad() {
- std::cout<<"Attempting to get the mutex"<<std::endl;
- TheMutex.lock();
- std::cout<<"Got it! I'll give it to you in a second..."<<std::endl;
- throw(std::runtime_error("Ooops,I threw!"));
- // will never get here...
- TheMutex.unlock();
- std::cout<<"There you go! I released the mutex!"<<std::endl;
- }
- };
- struct ScopedLock {
- ScopedLock(std::mutex& mutex)
- :m_mutex(&mutex) {
- std::cout<<"Attempting to get the mutex"<<std::endl;
- m_mutex->lock();
- std::cout<<"Got it! I'll give it to you in a second..."<<std::endl;
- }
- ~ScopedLock() {
- m_mutex->unlock();
- std::cout<<"There you go! I released the mutex!"<<std::endl;
- }
- std::mutex* m_mutex;
- };
- struct Good {
- Good() {
- ScopedLock autorelease(TheMutex);
- throw(std::runtime_error("Ooops,I threw!"));
- // will never get here
- }
- };
- int main() {
- std::cout<<"Create a Good instance"<<std::endl;
- try {
- Good g;
- } catch (const std::exception& e) {
- std::cout<<e.what()<<std::endl;
- }
- std::cout<<"Now,let's create a Bad instance"<<std::endl;
- try {
- Bad b;
- } catch (const std::exception& e) {
- std::cout<<e.what()<<std::endl;
- }
- std::cout<<"Now,let's create a whatever instance"<<std::endl;
- try {
- Good g;
- } catch (const std::exception& e) {
- std::cout<<e.what()<<std::endl;
- }
- std::cout<<"I am here despite the deadlock..."<<std::endl;
- return 0;
- }
输出(用gcc 4.8.1编译,使用-std = c 11):
- Create a Good instance
- Attempting to get the mutex
- Got it! I'll give it to you in a second...
- There you go! I released the mutex!
- Ooops,I threw!
- Now,let's create a Bad instance
- Attempting to get the mutex
- Got it! I'll give it to you in a second...
- Ooops,let's create a whatever instance
- Attempting to get the mutex
现在,请不要按照我的例子,并创建自己的范围守卫. C(特别是C 11)设计了RAII,并提供了丰富的终身经理.例如,std :: fstream将自动关闭,[std :: lock_guard] [2]将在我的示例中尝试做,std :: unique_ptr或std :: shared_ptr将会处理破坏.
最好的建议?阅读RAII(并根据它设计),使用标准库,不要创建裸露的资源,并熟悉Herb Sutter关于“异常安全”所说的话(请继续阅读他的website或谷歌“Herb Sutter Exception Safety”)