C++初始化列表各情况分析
今天回顾了下C++初始化列表的知识,接下来我对这一知识作一总结。
我们在定义了一个类的时候,需要对类的成员进行初始化。关于初始化,有两种方法,一种在初始化列表中进行,另一种就是在构造函数中进行,对于这两种情况,各有各的使用场合,接下来先说说在什么情况下优先使用初始化列表。
第一种情况:当类中含有引用时,必须要在初始化列表中对数据成员进行绑定。
如下代码所示:
- 1 #include<iostream>
- 2 using namespace std;
- 3 class Base
- 4 {
- 5 private:
- 6 int &data;
- 7 public:
- 8 Base(int &temp):data(temp)
- 9 {}
- 10 //Base(int &temp) {data=temp;}
- 11 };
分析:
类的构造过程是在初始化列表中进行的,对应于第八行,到了第九行,其实构造就已经结束了,换句话说也就是Base这个对象已经产生了,接下来如果写data=temp就是赋值的行为了,但是对于引用来说,必须在对象进行创建的时候进行绑定,所以,他初始化的机会仅有一次,那就是在初始化列表中。
第二种情况,有常量类型的数据成员时,必须在初始化列表中进行初始化,如下图:
- 1 #include<iostream>
- 2 using namespace std;
- 3 class Base
- 4 {
- 5 private:
- 6 const int data;
- 7 public:
- 8 Base(int &temp):data(temp){}
- 9 };
第三种情况:类Derive继承了类Base,而在类Base中,含有带参数的构造函数,那么在类Derive中,必须在初始化列表中显示的调用类Base的构造函数,如下代码:
- 1 #include<iostream>
- 2 using namespace std;
- 3 class Base
- 4 {
- 5 public:
- 6 Base(int x){}
- 7 };
- 8 class Derive:public Base
- 9 {
- 10 public:
- 11 Derive():Base(4)
- 12 {}
- 13 };
对于这种情况说明一下,在第11行的时候,如果基类Base的构造函数有默认的话,那么改行如果不显示调用基类Base的构造函数,编译器也会为其合成一个出来,只是参数是默认的而已,如果想要自己定义的数据,还是必须显示的调用才行,如下代码:
- 1 #include<iostream>
- 2 using namespace std;
- 3 class Base
- 4 {
- 5 public:
- 6 Base(int x=0){}
- 7 };
- 8 class Derive:public Base
- 9 {
- 10 public:
- 11 Derive() /*:Base(4)*/
- 12 {}
- 13 };
第四种情况:当一个类A中含有另外一个类B,且另外一个类B含有带参数的构造函数,那么该类A要在初始化列表中对类B进行显示的构造。(和第三种情况类似,只是一个包含,一个继承)。
- 1 #include<iostream>
- 2 using namespace std;
- 3 class Base
- 4 {
- 5 private:
- 6 int data;
- 7 public:
- 8 Base(int temp):data(temp){}
- 9 };
- 10 class Derive
- 11 {
- 12 private:
- 13 Base b;
- 14 public:
- 15 Derive(int x):b(x) {}
- 16 };
接下来分析一下为什么要这么做,他有什么好处,请看以下代码:
- 1 #include<iostream>
- 2 using namespace std;
- 3 class Base
- 4 {
- 5 public:
- 6 Base(int temp = 0)
- 7 {
- 8 cout << "调用了构造函数:" << this << endl;
- 9 }
- 10 Base(const Base& another)
- 11 {
- 12 cout << this<<"调用了拷贝构造函数:" << &another << endl;
- 13 }
- 14 Base& operator=(const Base& another)
- 15 {
- 16 cout << this<<"调用了拷贝赋值运算符:" << &another << endl;
- 17 return *this;
- 18 }
- 19 ~Base()
- 20 {
- 21 cout << "调用了析构函数" << endl;
- 22 }
- 23 };
- 24 class Derive
- 25 {
- 26 private:
- 27 Base b;
- 28 public:
- 29 Derive(int x=0)
- 30 {
- 31 b=x;
- 32 }
- 33 };
- 34 int main()
- 35 {
- 36 Derive d;
- 37 return 0;
- 38 }
输出:
如果在初始化列表中对对象进行初始化,如下代码:
- #include<iostream>
- using namespace std;
- class Base
- {
- public:
- Base(int temp = 0)
- {
- cout << "调用了构造函数:" << this << endl;
- }
- Base(const Base& another)
- {
- cout << this<<"调用了拷贝构造函数:" << &another << endl;
- }
- Base& operator=(const Base& another)
- {
- cout << this<<"调用了拷贝赋值运算符:" << &another << endl;
- return *this;
- }
- ~Base()
- {
- cout << "调用了析构函数" << endl;
- }
- };
- class Derive
- {
- private:
- Base b;
- public:
- Derive(int x=0):b(x){}
- };
- int main()
- {
- Derive d;
- return 0;
- }
输出:
为什么性能相差如此之大?编译器为我们做了什么?
分析:
- 1 class Derive
- 2 {
- 3 private:
- 4 Base b;
- 5 public:
- 6 Derive(int x=0)
- 7 {
- 8 b=x;
- 9 }
- 10 };
第六行是一个初始化列表,前面说过,类的定义是在这里完成的,如果有默认,自己不显示调用,编译器也会主动为我们调用。这就是第六行的作用,所以不管我们掉不调用,都会有构造函数生成,
接下来到了函数体里面,这个对象就已经存在了,如果对这个对象进行操作,那就不是刚开始的初始化了。这里b=x,b在第六行已经创建好了,因为类Base中含有一个参数的构造函数,所以x会被隐式的转化,因此接下来调用还是构造函数,接下来就是赋值行为了,调用了赋值运算符。相比于在初始化列表中对对象进行初始化,在这里前者效率体现的淋漓尽致。
所以含有类成员时, 尽量能在初始化列表中初始化的都在初始化列表中进行。
但是对于一些基础类型,在效率上相差不大,两种方法都可以,但还是建议在初始化列表中进行,显得高端大气,上档次。
初始化列表也不是在任何情况下都是最好的,比如,请看一下代码:
- #include<iostream>
- using namespace std;
- class Derive
- {
- private:
- int x;
- int y;
- public:
- Derive(int data):y(data),x(y){
- cout<<"x:"<<x<<endl;
- cout<<"y:"<<y<<endl;
- }
- };
- int main()
- {
- Derive d(3);
- return 0;
- }
如果运行的话,得到的值可能与你想要的相差甚远,运行效果如下:
原因分析:
初始化列表中对象构造的顺序也是有讲究的,他是依据类数据成员定义的顺序,并不是依据初始化列表中的顺序,上面类成员数据的顺序是先x再y,但是初始化列表中是先y后x,所以最后得出的数据可能并不是我们所期望的。
C++初始化列表各情况分析的更多相关文章
- C++中使用初始化列表的情况
http://blog.csdn.net/iceshirley/article/details/5688696 要理解这个问题,从概念上,我们要知道一点,那就是构造函数的执行过程会分成两个阶段:隐式或 ...
- C++中为什么构造函数初始化列表
已经有个构造函数负责初始化,为什么还需要构造函数初始化表呢? 在以下三种情况下需要使用初始化成员列表:一,需要初始化的数据成员是对象的情况:二,需要初始化const修饰的类成员:三,需要初始化引用成员 ...
- 【C++】类的特殊成员变量+初始化列表
参考资料: 1.黄邦勇帅 2.http://blog.163.com/sunshine_linting/blog/static/448933232011810101848652/ 3.http://w ...
- c++类初始化列表初探
目录 1 初始化和赋值 1.1 结论 2 构造函数初始化列表 2.1 结论 3 必须使用初始化列表的情况 3.1 结论 4 成员初始化顺序 5 参考资料 1 初始化和赋值 初始化:创建一个对象并赋予一 ...
- C++:四种必须使用初始化列表情况
[c++]必须在类初始化列表中初始化的几种情况 1. 类成员为const类型 2. 类成员为引用类型 复制代码 #include <iostream> using namesp ...
- 【c++】必须在类初始化列表中初始化的几种情况
转自:http://www.cnblogs.com/kaituorensheng/p/3477630.html 1. 类成员为const类型 2. 类成员为引用类型 #include <iost ...
- C++:只用初始化列表初始化变量的几种情况
1.类成员函数中const变量的初始化(也就是第一点) 有几个容易混淆的地方: (1)const 的变量只能通过构造函数的初始化列表进行初始化:(貌似在C++11中可以正常编译) (2)static ...
- Cocos2d-X3.0 刨根问底(五)----- Node类及显示对象列表源码分析
上一章 我们分析了Cocos2d-x的内存管理,主要解剖了 Ref.PoolManager.AutoreleasePool这三个类,了解了对象是如何自动释放的机制.之前有一个类 Node经常出现在各种 ...
- 从汇编看c++初始化列表初始化成员变量
简略来说,编译器会对初始化列表按照成员变量的声明顺序重新一一排序,安插到构造函数中进行初始化操作,而且这些初始化操作在构造函数里面用户自己定义的任何代码之前. 下面是c++源码: class X { ...
随机推荐
- ESP8266学习实战之UdpClient与UdpSever(FreeRTOS)
Udpclient 任务流程 ①判断是否获取ip地址 新建状态变量 STATION_STATUS stastatus; 调用wifi接口,并判断是否获取IP地址 ·do { stastatus = w ...
- svn服务器用户名密码更改后,如何更新本地用户名密码
在提交时,IDE会给出这样的提示,说明用户名密码已更改 在命令行输入 svn ls https:XXX(项目的地址),具体步骤如下图
- JAVA将byte数组(byte[])按照指定大小分割成多个byte数组
/** * 将byte数组按照指定大小分割成多个数组 * @param bytes 要分割的byte数组 * @param subSize 分割的块大小 单位:字节 * @return 指定大小的by ...
- 【LeetCode】536. Construct Binary Tree from String 解题报告(C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 统计字符串出现的次数 日期 题目地址:https:// ...
- python学习第四天:python基础(字符编码和乱码到底咋回事儿)
字符编码 这得从字符编码开始说起: 字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题.因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理. 最早的计算机在设计时采 ...
- 洛谷 P3431:[POI2005]AUT-The Bus(离散化+DP+树状数组)
题目描述 The streets of Byte City form a regular, chessboardlike network - they are either north-south o ...
- Chapter 5 Interaction
目录 5.1 Interaction requires a joint intervention 5.2 Identifying interaction 5.3 Counterfactual resp ...
- Java初学者作业——声明变量储存商品信息并进行输出
返回本章节 返回作业目录 需求说明: 声明变量存储商品信息(商品名称.商品价格和商品库存数量). 输出商品信息. 实现思路: 打印商品商品信息实现步骤: 声明变量存储商品信息.为变量赋值. 输出变量的 ...
- 【MySQL作业】SELECT 数据查询——美和易思模糊查询应用习题
点击打开所使用到的数据库>>> 1.根据商品名关键字查找商品信息. 查询带"美"字的商品信息: SELECT * FROM goods WHERE goodsNa ...
- C#读取注释的方法
友好的注释能提高代码的可读性,几乎所有的编程语言都支持注释. 在C#中,注释不是可执行代码的一部分,因此注释不会被编译到程序集中去,但是我们可以提取注释[右键项目]-[属性]-[生成]-[输出]-[X ...