C++构造函数深度探究
1、引子:
以下代码中的输出语句输出0吗,为什么?
struct Test
{
int _a;
Test(int a) : _a(a) {}
Test()
{
Test(0);
}
}; Test obj;
cout << obj._a << endl;
输出为:-858993460
2、剖析
上面代码的输出为一个垃圾值,也就是说obj调用构造函数并没有对成员进行初始化工作,虽然默认无参构造Test()内部调用了Test(int a),但从结果看,初始化工作并不成功。这是为什么呢?
在执行构造函数时,Test()并不会调用"this"对象(即obj对象)的Test::Test(int a),而是会用Test::Test(int a)来创建一个新的临时实例对象,然后当这条语句执行完后,这个新的临时对象马上就会被销毁。这样一来,"this"对象就没有被初始化,成员_a就是垃圾值,以后使用"this"对象就有可能产生一些问题。
3.重点:构造函数互相调用
分析完这个题目之后,我们会想到另一个问题。也是我们今天重点关注的问题:
class Test
{
int _a;
int _b;
int _c;
public: Test(int a, int b) : _a(a), _b(b),_c(0) {}
Test(int a, int b, int c);
};
如果我们C++类中有两个构造函数,分别为Test(int a, int b)和Test(int a, int b, int c)。如果我们的构造函数Test(int a, int b, int c)要完成所有成员(a,b,c)的赋值初始化工作,可以这样写:
Test::Test(int a, int b, int c)
: _a(a)
, _b(b)
, _c(c)
{
}
但是,这样写又重复了构造函数Test(int a, int b)的工作,类成员少的情况下还好,如果成员非常多,重复写的话代码量过大,而且代码可读性降低了。然而我们可以看到构造函数Test(int a, int b)已经完成了成员a和成员b的赋值初始化工作,为了减少代码量,就想着让3个参数的构造函数调用2个参数的构造函数,然后在执行一些自己的代码,这就如同派生类先调用基类的同名函数,再执行自己特有的代码。但是这种机制如何实现呢?
之前我们得出过结论:构造函数调用另一个构造函数并不能完成当前对象的初始化工作,只是初始化了临时对象。下面我们就进入本文的核心问题:如何在构造函数中调用本类的另一个构造函数来初始化当前对象?
方法一:使用placement new技术,在3个参数中显式调用2个参数的构造函数。
3参数构造函数可以这样实现:
Test::Test(int a, int b, int c)
{
new (this) Test(a, b);
...
}
构造函数分为2个执行阶段:一是在初始化列表的初始化阶段,二是在构造函数体内的赋值阶段。上述方法是在第二个阶段调用2个参数的构造函数。
placement new是operator new的一个重载版本,只是我们很少用到它。如果你想在已经分配的内存中创建一个对象,使用new是不行的。也就是说placement new允许你在一个已经分配好的内存中(栈或堆中)构造一个新的对象。原型中void*p实际上就是指向一个已经分配好的内存缓冲区的的首地址。placement new技术的形式是 new(void *p) Type(...),表示在p所指的内存区域调用Type构造函数,该过程没有内存请求。
这个方法本质就是在对象地址处,调用2个参数的构造函数重新生成一个新的对象然后覆盖该对象。这个实现方法有投机取巧的嫌疑。
方法二:使用C++11新特性——委托构造函数(Delegating constructors)。可以在构造函数初始化列表直接调用,类似于调用基类构造函数。
Test::Test(int a, int b, int c) : Test(a, b)
{
...
}
上述说了构造函数有2个执行阶段,该方法是在第一个阶段进行的,更加方便。但是注意不能在Test(a, b)后面在接_c(c)了,因为调用2个参数的构造函数之后,就相当于该对象已经初始化完成了,不能在初始化列表放入其他成员的初始化形式。只能放在构造函数体中的赋值阶段。该方法目前只能用在VS2013中。
这个方法利用了C++11标准中的新特性——委托构造函数(Delegating constructors)。目前只能再VS2013及以上的版本使用,这个方法局限性很大,不过确实很方便。
C++构造函数深度探究的更多相关文章
- C++ 默认拷贝构造函数 深度拷贝和浅拷贝
C++类默认拷贝构造函数的弊端 C++类的中有两个特殊的构造函数,(1)无参构造函数,(2)拷贝构造函数.它们的特殊之处在于: (1) 当类中没有定义任何构造函数时,编译器会默认提供一个无参构造函数且 ...
- 【转】C++的拷贝构造函数深度解读,值得一看
建议看原帖 地址:http://blog.csdn.net/lwbeyond/article/details/6202256 一. 什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很 ...
- vue 权限管理深度探究
实现思路如下:1.网页路由(route)中定义的每个路由都有meta属性,属性值防止可访问该路由的值.2.路由的全局前置守卫(beforeEach)会判断路由用户是否登录(未登录跳转至登录界面),以及 ...
- python装饰器的深度探究
1.讲装饰器一般讲到这种代码就可以了,但这篇博客会介绍更多: def deco(func): def wrapper(): print("start") func() #调用函数 ...
- 深度探究apk安装过程
一.先验知识 0.PcakageaManagerService版本号变化 1.概述 2.PackageManagerService服务启动流程 3. PackageManagerService入口 二 ...
- javascript构造函数深度克隆递归
<script type="text/javascript"> var obj={ name:'段丛磊', gex:18, sss:['李伟',18], fun:fun ...
- Socket深度探索 4 PHP(转)
[连载] Socket 深度探索 4 PHP (一) [连载] Socket 深度探究 4 PHP (二) [连载] Socket 深度探究 4 PHP (三)
- Android 自定义View 四个构造函数详解
https://blog.csdn.net/zhao123h/article/details/52210732 在开发android开发过程中,很多人都会遇到自定义view,一般都需要继承自View类 ...
- ELK笔记
ELK笔记 ELKStack高级实战培训http://files.cnblogs.com/files/MYSQLZOUQI/ELKStack%E9%AB%98%E7%BA%A7%E5%AE%9E%E6 ...
随机推荐
- CSS学习摘要-定位实例
CSS学习摘要-定位实例 注:全文摘自MDN-CSS定位实例 列表消息盒子 我们研究的第一个例子是一个经典的选项卡消息框,你想用一块小区域包括大量信息时,一个非常常用的特征.这包括含有大信息量的应用, ...
- Oracle创建Rman备份专用账户
有时会因为信息安全需要,创建备份所需的专用账户,不适用sys等用户.可以使用如下方式:create user rman_ycr identified by oracle;grant create se ...
- 结合领域驱动设计的SOA分布式软件架构
引言 本文主要是参考Martion Fowler所著的<企业应用架构模式>与Eric Evans所著的<领域驱动设计>这两本泰山之作,加上本人在近年实际的工作过程中开发SOA系 ...
- 基于 Webpack 4 搭建 Vue 开发环境
自从工作之后,就已经很久没有写过博客了.时间被分割得比较碎,积累了一段时间的学习成果,才写下了这篇博客. 之前有写过 Webpack4 的文章,但是都比较偏入门,唯一的一篇实战篇 -- 基于Webpa ...
- 如何运行vue项目(从gethub上download的开源项目)
前提:入坑vue.js,从GitHub上download一个vue.js的开源项目,发现不知如何在浏览器运行,通过查阅网上教程,发现网上的很多是教你怎么新建项目,并没有一个是教如何打开已有的项目.自已 ...
- 1070. [SCOI2007]修车【费用流】
Description 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同 的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序, ...
- 【转】【iOS开发】打开另一个APP(URL Scheme与openURL)
目标 平常我们做iOS开发,会经常遇到打开其他的APP的功能.本篇文章讲的就是打开别人的APP的一些知识.我们的目标是: 打开别人的APP 让别人打开我们的APP iOS9的适配问题 使用URL Sc ...
- 最简单的方式在linux上升级node.js版本
node的升级频率太高,n模块来升级是最方便的,网上看了很多资料介绍使用n模块,但是安装n模块之后却经常找不到这个命令 很多同学安装之后直接去使用n会发现命令不存在,就停留在这一步无法前进了. 解决 ...
- PAT乙级1006
1006 换个格式输出整数 (15 分) 让我们用字母 B 来表示“百”.字母 S 表示“十”,用 12...n 来表示不为零的个位数字 n(<10),换个格式来输出任一个不超过 3 位的正 ...
- HihoCoder - 1336 二维数状数组(单点更新 区间查询)
You are given an N × N matrix. At the beginning every element is 0. Write a program supporting 2 ope ...