zoukankan      html  css  js  c++  java
  • c++ what happens when a constructor throws an exception and leaves the object in an inconsistent state?

    为什么会想到这个问题?因为我总是不自觉地将c++和java进行对比。java对这种情况的处理方式是constructor返回一个null,然后已经构造的objects交给Garbage Collector处理,那么c++没有Garbage Collector,会是怎么样的一种情况呢?

    为了找到这个问题的答案,我做了个小实验,代码见main.cpp, Box.h, Box.cpp

    运行之前,我的设想是box->b的值为"NULL",因此程序输出如下:

    e.what() : a < 0

    b == NULL

    而事实是,在输出e.what() : a < 0之后,程序便崩溃了

    打上断点一瞧,执行到box->dostuff()里面的时候,这些主存地址都已经不可访问(也就是说已经被操作系统回收了,不再属于这个程序的可访问主存范围),截图如下:

    根据c++ primer 4th edition section 17.1.2 "Exceptions and Constructors" 我引用如下:

    If an exception occurs while constructing an object, then the object might be only partially constructed. Some of its members might have been initialized, and others might not have been initialized before the exception occurs. Even if the object is only partially constructed, we are guaranteed that the constructed members will be properly destroyed.

    我最开始以为加粗的句子是说要让我们自己来guarantee that the constructed memebers will be properly destroyed,原来这个工作不需要我们做(?)。然后我又重新修改了程序来验证这一点——"we are guaranteed that the constructed members will be properly destroyed.",见main1.cpp,Box1.h,Box1.cpp

    但是执行的结果如下:

    也就是说,what和why所占用的主存空间泄漏了,memory leak

    也就是说,c++ primer所说的“we are guaranteed that the constructed members will be properly destroyed.不适用于new出来的object!

    怎么办?参考这个问题:http://stackoverflow.com/questions/188693/is-the-destructor-called-if-the-constructor-throws-an-exception

    所以改写程序为:main2.cpp,Box2.h,Box2.cpp,运行结果如下(完美解决!):

    至于auto_ptr的实现方法,之前我写过一篇随笔(http://www.cnblogs.com/qrlozte/p/4095618.html),其实就是c++ primer 4th edition section 13.5.1 "Defining Smart Pointer Classes"所陈述的内容,大概的思路都在c++ primer的这个章节里面了,值得一看!

    main.cpp

     1 #include "Box.h"
     2 
     3 #include <iostream>
     4 #include <stdexcept>
     5 
     6 
     7 using namespace std;
     8 
     9 int main() {
    10     Box *box = NULL;
    11     try {
    12         box = new Box(-1);
    13     } catch (invalid_argument &e) {
    14         cout << "e.what() : " << e.what() << endl;
    15         box->dostuff();
    16     }
    17     if (box != NULL) delete box;
    18     return 0;
    19 }

    Box.h

     1 #ifndef BOX_H
     2 #define BOX_H
     3 
     4 class Box
     5 {
     6     public:
     7         Box(const int &a);
     8         ~Box();
     9         void dostuff();
    10 
    11     private:
    12 
    13         int a;
    14         int *b;
    15 };
    16 
    17 #endif // BOX_H

    Box.cpp

     1 #include "Box.h"
     2 
     3 #include <iostream>
     4 #include <stdexcept>
     5 
     6 using namespace std;
     7 
     8 Box::Box(const int &_a): a(_a), b(NULL)
     9 {
    10     if (a < 0)
    11         throw invalid_argument("a < 0");
    12     b = new int(99);
    13     cout << "Box created" << endl;
    14 }
    15 
    16 Box::~Box()
    17 {
    18     if (b != NULL) delete b;
    19     cout << "Box destroyed" << endl;
    20 }
    21 
    22 void Box::dostuff() {
    23     if (b == NULL) {
    24         cout << "b == NULL" << endl;
    25     }
    26     else {
    27         cout << "b = " << b << endl;
    28     }
    29 }

    main1.cpp

     1 #include "Box.h"
     2 
     3 #include <iostream>
     4 #include <stdexcept>
     5 
     6 
     7 using namespace std;
     8 
     9 int main() {
    10     Box *box = NULL;
    11     try {
    12         box = new Box(-1);
    13     } catch (invalid_argument &e) {
    14         cout << "e.what() : " << e.what() << endl;
    15         // box->dostuff();
    16     }
    17     if (box != NULL) delete box;
    18     return 0;
    19 }

    Box1.h

     1 #ifndef BOX_H
     2 #define BOX_H
     3 
     4 #include <memory>
     5 
     6 class Bottle {
     7     public:
     8         Bottle();
     9         ~Bottle();
    10 };
    11 
    12 class Hat {
    13     public:
    14         Hat();
    15         ~Hat();
    16 };
    17 
    18 class Box
    19 {
    20     public:
    21         Box(const int &a);
    22         ~Box();
    23         void dostuff();
    24 
    25     private:
    26 
    27         class What;
    28         class Why;
    29 
    30         int a;
    31         Bottle bottle;
    32         Hat hat;
    33         What *what;
    34         Why *why;
    35         int *b;
    36 };
    37 
    38 #endif // BOX_H

    Box1.cpp

     1 #include "Box.h"
     2 
     3 #include <iostream>
     4 #include <stdexcept>
     5 
     6 using namespace std;
     7 
     8 class Box::What {
     9     public:
    10         What() { cout << "What created" << endl; }
    11         ~What() { cout << "What destroyed" << endl; }
    12 };
    13 
    14 class Box::Why {
    15     public:
    16         Why() { cout << "Why created" << endl; }
    17         ~Why() { cout << "Why destroyed" << endl; }
    18 };
    19 
    20 Bottle::Bottle() { cout << "Bottle created" << endl; }
    21 Bottle::~Bottle() { cout << "Bottle destroyed" << endl; }
    22 
    23 Hat::Hat() { cout << "Hat created" << endl; }
    24 Hat::~Hat() { cout << "Hat destroyed" << endl; }
    25 
    26 // Pay attention to the order of the initializer: the same as the declaration
    27 // order, otherwise the compiler will give warnings (there's a reason for that)
    28 // the reason is the compiler always initializes data members following the order
    29 // in which they're declared
    30 Box::Box(const int &_a): a(_a), what(new What()), why(new Why()), b(NULL)
    31 {
    32     if (a < 0)
    33         throw invalid_argument("a < 0");
    34     b = new int(99);
    35     cout << "Box created" << endl;
    36 }
    37 
    38 // Notice the order of deletes: It's BETTER be the reverse order as they're created
    39 // Without the right definition of destructor, when exception thrown from the constructor
    40 // members cannot be destroyed properly. (Of course, also the same in normal situation).
    41 Box::~Box()
    42 {
    43     if (b != NULL) delete b;
    44     if (why != NULL) delete why;
    45     if (what != NULL) delete what;
    46     cout << "Box destroyed" << endl;
    47 }
    48 
    49 void Box::dostuff() {
    50     if (b == NULL) {
    51         cout << "b == NULL" << endl;
    52     }
    53     else {
    54         cout << "b = " << b << endl;
    55     }
    56 }

    main2.cpp

     1 #include "Box.h"
     2 
     3 #include <iostream>
     4 #include <stdexcept>
     5 
     6 
     7 using namespace std;
     8 
     9 int main() {
    10     Box *box = NULL;
    11     try {
    12         box = new Box(-1);
    13     } catch (invalid_argument &e) {
    14         cout << "e.what() : " << e.what() << endl;
    15         // box->dostuff();
    16     }
    17     if (box != NULL) delete box;
    18     return 0;
    19 }

    Box2.h

     1 #ifndef BOX_H
     2 #define BOX_H
     3 
     4 #include <memory>
     5 
     6 class Bottle {
     7     public:
     8         Bottle();
     9         ~Bottle();
    10 };
    11 
    12 class Hat {
    13     public:
    14         Hat();
    15         ~Hat();
    16 };
    17 
    18 class Box
    19 {
    20     public:
    21         Box(const int &a);
    22         ~Box();
    23         void dostuff();
    24 
    25     private:
    26 
    27         class What;
    28         class Why;
    29 
    30         int a;
    31         Bottle bottle;
    32         Hat hat;
    33         std::auto_ptr<What> what;
    34         std::auto_ptr<Why> why;
    35         int *b;
    36 };
    37 
    38 #endif // BOX_H

    Box2.cpp

     1 #include "Box.h"
     2 
     3 #include <iostream>
     4 #include <stdexcept>
     5 
     6 using namespace std;
     7 
     8 class Box::What {
     9     public:
    10         What() { cout << "What created" << endl; }
    11         ~What() { cout << "What destroyed" << endl; }
    12 };
    13 
    14 class Box::Why {
    15     public:
    16         Why() { cout << "Why created" << endl; }
    17         ~Why() { cout << "Why destroyed" << endl; }
    18 };
    19 
    20 Bottle::Bottle() { cout << "Bottle created" << endl; }
    21 Bottle::~Bottle() { cout << "Bottle destroyed" << endl; }
    22 
    23 Hat::Hat() { cout << "Hat created" << endl; }
    24 Hat::~Hat() { cout << "Hat destroyed" << endl; }
    25 
    26 // Pay attention to the order of the initializer: the same as the declaration
    27 // order, otherwise the compiler will give warnings (there's a reason for that)
    28 // the reason is the compiler always initializes data members following the order
    29 // in which they're declared
    30 Box::Box(const int &_a): a(_a), what(new What()), why(new Why()), b(NULL)
    31 {
    32     if (a < 0)
    33         throw invalid_argument("a < 0");
    34     b = new int(99);
    35     cout << "Box created" << endl;
    36 }
    37 
    38 // Notice the order of deletes: It's BETTER be the reverse order as they're created
    39 // Without the right definition of destructor, when exception thrown from the constructor
    40 // members cannot be destroyed properly. (Of course, also the same in normal situation).
    41 Box::~Box()
    42 {
    43     if (b != NULL) delete b;
    44     if (why.get() != NULL) why.reset(NULL); // might be unnecessary, see auto_ptr's documentation
    45     if (what.get() != NULL) what.reset(NULL);
    46     cout << "Box destroyed" << endl;
    47 }
    48 
    49 void Box::dostuff() {
    50     if (b == NULL) {
    51         cout << "b == NULL" << endl;
    52     }
    53     else {
    54         cout << "b = " << b << endl;
    55     }
    56 }
  • 相关阅读:
    自定义CopyOnWriteHashMap
    NIO中Buffer缓冲区的实现
    TOMCAT原理详解及请求过程
    XSS的原理分析与解剖
    mysql分页查询优化
    java如何正确停止一个线程
    Centos搭建ElasticSearch
    redis集群原理
    Idea-每次修改JS文件都需要重启Idea才能生效解决方法
    java 加密 解密 Illegal key size
  • 原文地址:https://www.cnblogs.com/qrlozte/p/4107077.html
Copyright © 2011-2022 走看看