c – 如果在构造函数中抛出异常,最好的做法是防止内存泄漏?

前端之家收集整理的这篇文章主要介绍了c – 如果在构造函数中抛出异常,最好的做法是防止内存泄漏?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我知道如果在构造函数中抛出异常,析构函数将不会被调用(简单类,不继承).因此,如果在构造函数中抛出异常,并且有一些堆内存未被清除的机会.那么这里最好的做法是什么?让我们假设我必须在构造函数调用一些函数,它可能会抛出异常.在这种情况下,我总是使用共享指针吗?有什么办法谢谢!

解决方法

我会坚持 RAII成语.

如果您避免“裸”资源(如运算符新,裸指针,裸互斥体等),并将所有内容都包含在具有适当RAII行为的容器或类中,那么即使存在异常,也不会有您所描述的问题.

也就是说,不要在构造函数获取裸体资源.而是创建一个本身跟随RAII的对象的实例.这样,即使您的构造函数失败(即创建实例的构造函数),将调用初始化对象的析构函数.

所以,这是不好的做法:

  1. #include<iostream>
  2. #include<stdexcept>
  3.  
  4. struct Bad {
  5. Bad() {
  6. double *x = new double;
  7. throw(std::runtime_error("the exception was thrown"));
  8. }
  9.  
  10. ~Bad() {
  11. delete x;
  12. std::cout<<"My destructor was called"<<std::endl;
  13. }
  14.  
  15. double *x;
  16. };
  17.  
  18. int main() {
  19. try {
  20. Bad bad;
  21. } catch (const std::exception &e) {
  22. std::cout<<"We have a leak! Let's keep going!"<<std::endl;
  23. }
  24. std::cout<<"Here I am... with a leak..."<<std::endl;
  25. return 0;
  26. }

输出

  1. We have a leak! Let's keep going!
  2. Here I am... with a leak...

与这个愚蠢和愚蠢的良好实施相比:

  1. #include<iostream>
  2. #include<stdexcept>
  3.  
  4. struct Resource {
  5.  
  6. Resource() {
  7. std::cout<<"Resource acquired"<<std::endl;
  8. }
  9.  
  10. ~Resource() {
  11. std::cout<<"Resource cleaned up"<<std::endl;
  12. }
  13.  
  14. };
  15.  
  16. struct Good {
  17. Good() {
  18. std::cout<<"Acquiring resource"<<std::endl;
  19. Resource r;
  20. throw(std::runtime_error("the exception was thrown"));
  21. }
  22.  
  23. ~Good() {
  24. std::cout<<"My destructor was called"<<std::endl;
  25. }
  26. };
  27.  
  28.  
  29. int main() {
  30. try {
  31. Good good;
  32. } catch (const std::exception &e) {
  33. std::cout<<"We DO NOT have a leak! Let's keep going!"<<std::endl;
  34. }
  35. std::cout<<"Here I am... without a leak..."<<std::endl;
  36. return 0;
  37. }

输出

  1. Acquiring resource
  2. Resource acquired
  3. Resource cleaned up
  4. We DO NOT have a leak! Let's keep going!
  5. Here I am... without a leak...

我的观点如下:尝试将需要解放的所有资源封装到构造函数不抛出的类中,析构函数正确地释放资源.然后,在析构函数可能抛出的其他类中,只需创建包装资源的实例,并将保证获取的资源包装器的析构函数将被清理.

以下可能是一个更好的例子:

  1. #include<mutex>
  2. #include<iostream>
  3. #include<stdexcept>
  4.  
  5. // a program-wide mutex
  6. std::mutex TheMutex;
  7.  
  8. struct Bad {
  9. Bad() {
  10. std::cout<<"Attempting to get the mutex"<<std::endl;
  11. TheMutex.lock();
  12. std::cout<<"Got it! I'll give it to you in a second..."<<std::endl;
  13. throw(std::runtime_error("Ooops,I threw!"));
  14. // will never get here...
  15. TheMutex.unlock();
  16. std::cout<<"There you go! I released the mutex!"<<std::endl;
  17. }
  18. };
  19.  
  20. struct ScopedLock {
  21. ScopedLock(std::mutex& mutex)
  22. :m_mutex(&mutex) {
  23. std::cout<<"Attempting to get the mutex"<<std::endl;
  24. m_mutex->lock();
  25. std::cout<<"Got it! I'll give it to you in a second..."<<std::endl;
  26. }
  27.  
  28. ~ScopedLock() {
  29. m_mutex->unlock();
  30. std::cout<<"There you go! I released the mutex!"<<std::endl;
  31. }
  32. std::mutex* m_mutex;
  33. };
  34.  
  35. struct Good {
  36. Good() {
  37. ScopedLock autorelease(TheMutex);
  38. throw(std::runtime_error("Ooops,I threw!"));
  39. // will never get here
  40. }
  41. };
  42.  
  43.  
  44. int main() {
  45. std::cout<<"Create a Good instance"<<std::endl;
  46. try {
  47. Good g;
  48. } catch (const std::exception& e) {
  49. std::cout<<e.what()<<std::endl;
  50. }
  51.  
  52. std::cout<<"Now,let's create a Bad instance"<<std::endl;
  53. try {
  54. Bad b;
  55. } catch (const std::exception& e) {
  56. std::cout<<e.what()<<std::endl;
  57. }
  58.  
  59. std::cout<<"Now,let's create a whatever instance"<<std::endl;
  60. try {
  61. Good g;
  62. } catch (const std::exception& e) {
  63. std::cout<<e.what()<<std::endl;
  64. }
  65.  
  66. std::cout<<"I am here despite the deadlock..."<<std::endl;
  67. return 0;
  68. }

输出(用gcc 4.8.1编译,使用-std = c 11):

  1. Create a Good instance
  2. Attempting to get the mutex
  3. Got it! I'll give it to you in a second...
  4. There you go! I released the mutex!
  5. Ooops,I threw!
  6. Now,let's create a Bad instance
  7. Attempting to get the mutex
  8. Got it! I'll give it to you in a second...
  9. Ooops,let's create a whatever instance
  10. 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”)

猜你在找的C&C++相关文章