为什么会想到这个问题?因为我总是不自觉地将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

 #include "Box.h"

 #include <iostream>
#include <stdexcept> using namespace std; int main() {
Box *box = NULL;
try {
box = new Box(-);
} catch (invalid_argument &e) {
cout << "e.what() : " << e.what() << endl;
box->dostuff();
}
if (box != NULL) delete box;
return ;
}

Box.h

 #ifndef BOX_H
#define BOX_H class Box
{
public:
Box(const int &a);
~Box();
void dostuff(); private: int a;
int *b;
}; #endif // BOX_H

Box.cpp

 #include "Box.h"

 #include <iostream>
#include <stdexcept> using namespace std; Box::Box(const int &_a): a(_a), b(NULL)
{
if (a < )
throw invalid_argument("a < 0");
b = new int();
cout << "Box created" << endl;
} Box::~Box()
{
if (b != NULL) delete b;
cout << "Box destroyed" << endl;
} void Box::dostuff() {
if (b == NULL) {
cout << "b == NULL" << endl;
}
else {
cout << "b = " << b << endl;
}
}

main1.cpp

 #include "Box.h"

 #include <iostream>
#include <stdexcept> using namespace std; int main() {
Box *box = NULL;
try {
box = new Box(-);
} catch (invalid_argument &e) {
cout << "e.what() : " << e.what() << endl;
// box->dostuff();
}
if (box != NULL) delete box;
return ;
}

Box1.h

 #ifndef BOX_H
#define BOX_H #include <memory> class Bottle {
public:
Bottle();
~Bottle();
}; class Hat {
public:
Hat();
~Hat();
}; class Box
{
public:
Box(const int &a);
~Box();
void dostuff(); private: class What;
class Why; int a;
Bottle bottle;
Hat hat;
What *what;
Why *why;
int *b;
}; #endif // BOX_H

Box1.cpp

 #include "Box.h"

 #include <iostream>
#include <stdexcept> using namespace std; class Box::What {
public:
What() { cout << "What created" << endl; }
~What() { cout << "What destroyed" << endl; }
}; class Box::Why {
public:
Why() { cout << "Why created" << endl; }
~Why() { cout << "Why destroyed" << endl; }
}; Bottle::Bottle() { cout << "Bottle created" << endl; }
Bottle::~Bottle() { cout << "Bottle destroyed" << endl; } Hat::Hat() { cout << "Hat created" << endl; }
Hat::~Hat() { cout << "Hat destroyed" << endl; } // Pay attention to the order of the initializer: the same as the declaration
// order, otherwise the compiler will give warnings (there's a reason for that)
// the reason is the compiler always initializes data members following the order
// in which they're declared
Box::Box(const int &_a): a(_a), what(new What()), why(new Why()), b(NULL)
{
if (a < )
throw invalid_argument("a < 0");
b = new int();
cout << "Box created" << endl;
} // Notice the order of deletes: It's BETTER be the reverse order as they're created
// Without the right definition of destructor, when exception thrown from the constructor
// members cannot be destroyed properly. (Of course, also the same in normal situation).
Box::~Box()
{
if (b != NULL) delete b;
if (why != NULL) delete why;
if (what != NULL) delete what;
cout << "Box destroyed" << endl;
} void Box::dostuff() {
if (b == NULL) {
cout << "b == NULL" << endl;
}
else {
cout << "b = " << b << endl;
}
}

main2.cpp

 #include "Box.h"

 #include <iostream>
#include <stdexcept> using namespace std; int main() {
Box *box = NULL;
try {
box = new Box(-);
} catch (invalid_argument &e) {
cout << "e.what() : " << e.what() << endl;
// box->dostuff();
}
if (box != NULL) delete box;
return ;
}

Box2.h

 #ifndef BOX_H
#define BOX_H #include <memory> class Bottle {
public:
Bottle();
~Bottle();
}; class Hat {
public:
Hat();
~Hat();
}; class Box
{
public:
Box(const int &a);
~Box();
void dostuff(); private: class What;
class Why; int a;
Bottle bottle;
Hat hat;
std::auto_ptr<What> what;
std::auto_ptr<Why> why;
int *b;
}; #endif // BOX_H

Box2.cpp

 #include "Box.h"

 #include <iostream>
#include <stdexcept> using namespace std; class Box::What {
public:
What() { cout << "What created" << endl; }
~What() { cout << "What destroyed" << endl; }
}; class Box::Why {
public:
Why() { cout << "Why created" << endl; }
~Why() { cout << "Why destroyed" << endl; }
}; Bottle::Bottle() { cout << "Bottle created" << endl; }
Bottle::~Bottle() { cout << "Bottle destroyed" << endl; } Hat::Hat() { cout << "Hat created" << endl; }
Hat::~Hat() { cout << "Hat destroyed" << endl; } // Pay attention to the order of the initializer: the same as the declaration
// order, otherwise the compiler will give warnings (there's a reason for that)
// the reason is the compiler always initializes data members following the order
// in which they're declared
Box::Box(const int &_a): a(_a), what(new What()), why(new Why()), b(NULL)
{
if (a < )
throw invalid_argument("a < 0");
b = new int();
cout << "Box created" << endl;
} // Notice the order of deletes: It's BETTER be the reverse order as they're created
// Without the right definition of destructor, when exception thrown from the constructor
// members cannot be destroyed properly. (Of course, also the same in normal situation).
Box::~Box()
{
if (b != NULL) delete b;
if (why.get() != NULL) why.reset(NULL); // might be unnecessary, see auto_ptr's documentation
if (what.get() != NULL) what.reset(NULL);
cout << "Box destroyed" << endl;
} void Box::dostuff() {
if (b == NULL) {
cout << "b == NULL" << endl;
}
else {
cout << "b = " << b << endl;
}
}

c++ what happens when a constructor throws an exception and leaves the object in an inconsistent state?的更多相关文章

  1. In p = new Fred(), does the Fred memory “leak” if the Fred constructor throws an exception?

    No. If an exception occurs during the Fred constructor of p = new Fred(), the C++ language guarantee ...

  2. org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.hs.model.StudentModel]: No default constructor found; nested exception is java.lang.NoSuchMethodException: c

    root cause org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [c ...

  3. throws/throw Exception 异常应用

    throws通常用于方法的声明,当方法中发生异常的时候,却不想在方法中对异常进行处理的时候,就可以在声明方法时, 使用throws声明抛出的异常,然后再调用该方法的其他方法中对异常进行处理(如使用tr ...

  4. Nhibernate 4.0 教程入门

    Nhibernate 4.0 教程 目录 1.      下载Nhibernate 4.04. 1 2.      入门教程... 2 3.      测试项目详解... 3 4.      总结.. ...

  5. spring源码分析(一)IoC、DI

    创建日期:2016.08.06 修改日期:2016.08.07 - 2016.08.12 交流QQ:992591601 参考书籍:<spring源码深度解析>.<spring技术内幕 ...

  6. Static Constructors

    A static constructor is used to initialize any static data, or to perform a particular action that n ...

  7. (C++) Interview in English. - Constructors/Destructors

    Constructors/Destructors. 我们都知道,在C++中建立一个类,这个类中肯定会包括构造函数.析构函数.复制构造函数和重载赋值操作:即使在你没有明确定义的情况下,编译器也会给你生成 ...

  8. How a C++ compiler implements exception handling

    Introduction One of the revolutionary features of C++ over traditional languages is its support for ...

  9. xmlhttp

    File an issue about the selected textFile an issue about the selected text XMLHttpRequest Living Sta ...

随机推荐

  1. 【计算几何】【凸包】bzoj1670 [Usaco2006 Oct]Building the Moat护城河的挖掘

    #include<cstdio> #include<cmath> #include<algorithm> using namespace std; #define ...

  2. [CF627D]Preorder Test

    题目大意: 一个$n(n\le2\times10^5)$个结点的树,每个结点有一个权值$w_i$.可以任选一点为根,并选择一些结点交换其子结点的顺序,使得该树DFS序上第$m$个结点的权值最大.求最大 ...

  3. 倒置输入的数 Exercise07_02

    import java.util.Scanner; /** * @author 冰樱梦 * 时间:2018年下半年 * 题目:倒置输入的数 * */ public class Exercise07_0 ...

  4. Access转Sqlite的最简单的方法(不需要DB Manager)

    在网上查了很多方法,大部分都是需要DB Manager,但是DB Manager这个牛逼保护机制做得好,没有免费的注册码可用.还好下面有另一个办法实现Access转Sqlite: 首先要下载SQLit ...

  5. Problem H: 阶乘和

    #include<stdio.h> int main() { ; ; ; int n; scanf("%d",&n); ;i<=n;i++) { ret= ...

  6. Android 架构 1.架构

    项目分为了四个层级:模型层.接口层.核心层.界面层.模型层定义了所有的模型:接口层封装了服务器提供的API:核心层处理所有业务逻辑:界面层就处理界面的展示.几个层级之间的关系如下图所示: 层级关系:模 ...

  7. python列表和分片

    列表的分片 str = " print("打印第0个元素:" ,str[0]) print("负数表示倒数第N个元素,-1表示倒数第一个元素:" ,s ...

  8. Nginx用为缓存服务器

    Nginx 缓存组成 共享内存 数据定位 存储键和缓存对象的元数据 磁盘空间: 存储数据   nginx做为反向代理时,能够将来自upstream的响应缓存至本地,并在后续的客户端请求同样内容时直接从 ...

  9. Coherence代理节点在离开集群时的恢复

    Coherence的架构参考 在极端压力之下,有时候代理节点会忙于处理请求而不响应其他的心跳,同步,导致其他节点传输的报文没有回应,而被认为是离开集群,从而影响业务. 写了一段代码,能让进程在监听到有 ...

  10. 基于CentOS与VmwareStation10搭建Oracle11G RAC 64集群环境:3.安装Oracle RAC-3.2.安装 cvuqdisk 软件包

    3.2.安装 cvuqdisk 软件包 3.2.1. 准备Oracle  Grid安装包 上传Grid .Oracle 安装文件: sftp> put E:\Software\linux.x64 ...