C++ 构造函数的异常是一个比较难缠的问题,很多时候,我们可能不去考虑这些问题,如果被问到,有人可能会说使用RAII管理资源。

但你真的考虑过如果构造函数失败了,到底会发生什么吗,前面构造成功的成员、基类对象应该怎样回收?

最近在知乎上看到有人提到这个问题:

http://www.zhihu.com/question/22889420

看了陈硕的回答,抱着怀疑的心态写代码加以验证。

在此之前,先不急着上代码,啰嗦几句话。

首先问4个问题,这是从 Herb sutter 的《More Exceptrional C++》看到的,我觉得问的很好,类似保安的哲理问题:你是谁、你从哪里来、你要到哪里去?

1:对象生命周期何时开始

一个构造函数成功执行完毕,并成功返回之时,也就是构造函数成功执行到函数体尾,没有发生异常。

2:对象生命周期何时结束

当一个对像的析构函数开始执行,也就是达到析构函数开始指出,这里暂且不讨论析构函数是否发生异常,只要进入析构函数体,该对象生命周期就已经结束

3:在生命周期开始之前,与生命结束之后,对象处于什么状态

这时候“对象”已不是对象。理论上“它”根本就不存在

4:接着第三个答案,如果构造函数异常,对象处于什么状态?

构造函数异常,即构造函数甚至没有到达函数体的尾部,即对象的生命周期还没有开始,所以他根本不是一个的对象,或者说它什么都不是,

所以更不会执行析构函数了。

那么问题来了,如果构造失败,之前成功分配的资源怎么办呢?

Herb sutter的答案是:这个是语言本身来负责回收了,编译器来实现,没程序员的事,即使之前成功构造的对像也不会执行析构函数。

下面是陈硕列举构造函数失败可能发生的场景,他举了5个例子,我这里写了4个,我的结论如下

1:构造函数的初始化列表里抛异常,前面已经构造好的成员由编译器负责回收,不会调用析构函数

2:数组元素构造时抛异常,前面已经构造好的元素由编译器回收,不会调用对象的析构函数。

3:多继承中某个基类的构造函数抛异常,已经构造成功的基类对象由编译器回收,不会调用析构函数

4:智能指针,STL 容器 存放auto_ptr<T>, shared_ptr<T> 对象, 类型T构造失败,则前面构成成功的智能对象有编译器回收,不会调用析构函数。

 

第一种:构造函数初始化列表抛出异常,前面成功构造的对像由编译器负责回收,不会调用析构函数

 #include<iostream>

 using namespace std;

 class B{

 public:
B(){
cout << "construct B default" << endl;
throw ; //故意在默认构造函数中抛出异常
}
B(int num){
age = num;
cout << "constructor B ,age =" << num << endl;
}
~B(){
cout << "destructor B ,age=" << age << endl;
}
private:
int age;
}; class A{
public:
A():_data(new char[]), b(B()), bp(new B()){
cout << "construct A " << endl; *_data = '\0'; }
~A(){
cout << "destructor A" << endl;
delete [] _data;
delete bp; }
private:
char *_data;
B b;
B *bp;
}; int main(void){ A a;
}

第十行出,我故意throw 3

所以在25行class A构造函数的初始化列表,调用B(10)发生异常,而B()则会异常退出。结果如下

B的默认构造函数和B(int)都执行完毕,但析构函数没有执行,当然A的构造也失败了,更不会执行析构函数,这些资源都是编译器负责回收了

如果你不信的会,我注释第9行,是另一种结果

 第2种:数组元素构造发生异常,前面构造成功的对象由编译器负责回收,不会调用析构函数

   class C{
public:
C(){
//得到指定范围[m,n]的随机数:r = rand()%(n - m + 1) + m;
int r = rand()%(-)+;
num = r;
cout << "constuctor C ,num = " << num << endl;
if(!(r % )){
throw r;
}
}
~C(){
cout << "destructor C, num = " << num << endl;
}
private:
int num;
}; int main(void){ C arr[]; }

当随机数r 整除 4是,即throw异常,则前面成功构造的对象不会析构

若注释第9行的throw r 结果是:

第3、4两种场景,在第二篇文章验证:《C++构造函数异常(二)》

C++构造函数异常(一)的更多相关文章

  1. C++构造函数异常(二)

    继续上一篇文章提到的构造异常话题,下面继续谈另外两个场景,即多继承构造异常,以及智能指针构造异常 第3:对多继承当中,某个基类构造异常,而其他基类已构造成功,则构造成功的基类不会析构,由编译器负责回收 ...

  2. 赋值、复制构造函数和构造函数 & 异常安全的赋值

    异常安全的赋值 需要注意,复制赋值和复制构造,相兼容. 赋值时候,要带上自检查.

  3. C++ 异常机制分析

    C++异常机制概述 异常处理是C++的一项语言机制,用于在程序中处理异常事件.异常事件在C++中表示为异常对象.异常事件发生时,程序使用throw关键字抛出异常表达式,抛出点称为异常出现点,由操作系统 ...

  4. 《Java大学教程》—第15章 异常

    自测题:1.    什么是异常?P357异常是在程序生命周期内出现的事件,它会导致程序行为不可靠. 2. 已检查异常和未检查异常的区别是什么?P359在编译器允许程序被编译通过前,要求程序员必须编写代 ...

  5. C++ 异常~一 转

    C++ 异常机制分析   阅读目录 C++异常机制概述 throw 关键字 异常对象 catch 关键字 栈展开.RAII 异常机制与构造函数 异常机制与析构函数 noexcept修饰符与noexce ...

  6. C++异常:exception

    基本知识 下图表示了标准异常的继承关系 exception是所有标准异常的基类,自定义异常也需要继承exception,如下例: #include "pch.h" #include ...

  7. C++ 异常机制分析(C++标准库定义了12种异常,很多大公司的C++编码规范也是明确禁止使用异常的,如google、Qt)

    阅读目录 C++异常机制概述 throw 关键字 异常对象 catch 关键字 栈展开.RAII 异常机制与构造函数 异常机制与析构函数 noexcept修饰符与noexcept操作符 异常处理的性能 ...

  8. 【转】C++ 异常机制分析

    阅读目录 C++异常机制概述 throw 关键字 异常对象 catch 关键字 栈展开.RAII 异常机制与构造函数 异常机制与析构函数 noexcept修饰符与noexcept操作符 异常处理的性能 ...

  9. C++ 异常机制(上)

    目录 一.概念 二.异常的好处 三.基本语法 四.栈解旋 五.异常接口声明 六.异常对象的内存模型 七.异常对象的生命周期 一.概念 异常:存在于运行时的反常行为,这些行为超过了函数的正常的功能范围. ...

随机推荐

  1. 同时连接gitlab和github

    ---恢复内容开始--- 原文地址:https://juejin.im/post/5ac0cf356fb9a028df22c246 1. 分别生成gitlab和github的ssh key 生成第一个 ...

  2. play framework 在idea简单运行配置(mac为例)

    文章目录 play 最基本的构建 在idea中配置 配置jdk相关 配置play 运行 运行 play 最基本的构建 https://blog.csdn.net/dataiyangu/article/ ...

  3. git---从已有分支拉出新分支

    文章目录 开发中,经常需要从一个已有的分支拉出一个新分支,去这个新分支做一些开发改动,这里示例为: 从master分支,重新拉取出一个新的分支,名字为dev,具体命令如下: 切换到被copy的分支(m ...

  4. 面试题:实现strcpy,strlen,strcmp,strcat,memcpy 之c-style字符串

    哪里可以看到c库函数的源码? gnu的c运行库glibc,但是源码的实现却是复杂的,需要考虑效率,stlen源码分析. c-style字符串有个约定,以空字符结尾,即 '\0' . ' }; &quo ...

  5. LeetCode 95. Unique Binary Search Trees II 动态演示

    比如输入为n, 这道题目就是让返回由1,2,... n的组成的所有二叉排序树,每个树都必须包含这n个数 这是二叉树的排列组合的题目.排列组合经常用DFS来解决. 这道题比如输入为3,也就是求start ...

  6. CPU的架构:x86、arm、mips、龙芯等

    在公司经常听其他工程师讲x86,arm平台啥的,作为一个算法工程师,我听过却不知道这是啥!!!(汗颜) 现在偷偷学起: x86,arm,mips等这些都是CPU的架构,不管是手机电脑还是一些嵌入式的设 ...

  7. 实验报告(七)&第九周课程总结

    班级 计科二班 学号 20188425 姓名 IM 完成时间2019/10/24 评分等级 实验任务详情: 完成火车站售票程序的模拟. 要求: (1)总票数1000张: (2)10个窗口同时开始卖票: ...

  8. 再往DjVu鼓吹者的头上敲一棒子

    最近在某论坛又看到有人在鼓吹DjVu,甚至声称拿到PDG就转成DjVu,忍不住想再敲打敲打. 早几年前就已经有人举出过实例,证明PDG.TIFF转DjVu会因为有损压缩而产生错别字,似乎时间长了一堆新 ...

  9. Django建表

    最近在学习Django,遇到了些问题一起来看看吧. 1.自定义表名 Django 建表默认会以 app_name + Class_name 解决方法 #coding:utf8 from django. ...

  10. SpringBoot2.x整合定时任务和异步任务处理

    SpringBoot2.x整合定时任务和异步任务处理 一.项目环境 springboot2.x本身已经集成了定时任务模块和异步任务,可以直接使用 二.springboot常用定时任务配置 1.在启动类 ...