前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++初始化列表深入探索

C++初始化列表深入探索

作者头像
Ch_Zaqdt
发布2020-03-12 18:05:36
6010
发布2020-03-12 18:05:36
举报
文章被收录于专栏:Zaqdt_ACMZaqdt_ACM

必须要使用初始化列表的情况:

当初始化一个引用的成员 当初始化一个const的成员 当调用一个基类的对象(该类继承于基类),且基类有含有参数的构造函数时 当调用一个类的对象成员时,且该对象成员所在的类有含有参数的构造函数时

       通过下面的代码,可以更详细的说明这四种情况:

代码语言:javascript
复制
#include <iostream>
using namespace std;

class B {
public:
	B(int tmp){}
};

class Base {
public:
	Base(int tmp){}
};

class A : public Base{    // 第三种情况
public:
	int &m_iX;            // 第一种情况
	const int m_iY;       // 第二种情况
	B m_mX;               // 第四种情况
	A(int& tmp) :m_iX(tmp), m_iY(10), m_mX(10), Base(10) {

	}
};

int main()
{
	int x = 10;
	A a(x);
	return 0;
}

       那么以上这四种情况是必须要用初始化列表的方式去初始化的,如果在函数体中去初始化会报错。

使用初始化列表的优势:

最主要的优势就是可以提高运行效率,尤其是对含有类对象的成员时,效率会有很大的提升,下面也用一个示例来证明一下,首先我们先将初始化写在函数体内,观察一下结果:

代码语言:javascript
复制
#include <iostream>
using namespace std;

class B {
public:
	int m_iX;
	B(int tmp = 0) : m_iX(tmp){
		cout << "this : " << this << "  ";
		cout << "构造函数" << endl;
	}
	B& operator=(const B& tmp) {
		cout << "this : " << this << "  ";
		cout << "赋值构造函数" << endl;
		return *this;
	}
	~B() {
		cout << "this : " << this << "  ";
		cout << "析构函数" << endl;
	}
};

class A {
public:
	B m_mX;
	A(int tmp) {
		m_mX = 100;
	}
};

int main()
{
	A a(10);
	return 0;
}

       上面的代码构建了一个类B,并在类A中定义了一个类B的对象,然后并在函数体中对m_mX进行了初始化,运行结果如下图所示:

       我们暂且先不判断放在函数体中的初始化的好坏,我们先来使用初始化列表的方式对其初始化一下看看结果是什么样的,初始化列表方法运行结果:

     可以发现少了三行代码,那么我们站在编译器的角度来分析一下具体原因,其实通过多的那三行输出,就可以发现编译器为我们实例化了一个临时对象,并对其进行了初始化,然后再将结果通过赋值构造函数给了m_mX对象,然后再析构掉临时对象,那么编译器在背后实现的代码如下:

代码语言:javascript
复制
class A {
public:
	B m_mX;
	A(int tmp){
		//m_mX = 100;
		B m_mX;            // 不会调用默认构造函数
		m_mX.B::B();       // 调用第一个构造函数
		B tmpobj;          // 不会调用默认构造函数
		tmpobj.B::B(100);  // 调用第二个构造函数
		m_mX = tmpobj;     // 调用赋值构造函数
		tmpobj.B::~B();    // 调用第一个析构函数
	}
};

       所输出的5行语句就是这么来的,其中有两行不会调用默认构造函数这里就不解释了,不清楚的可以看这篇博客:传送门,由此可见,m_mX=100这一行代码被转换成了这么多行,我们再来看看使用初始化列表的方法来初始化的话,编译器是怎么做的:

代码语言:javascript
复制
class A {
public:
	B m_mX;
	A(int tmp){
		// 如果使用初始化列表:
		B m_mX;             // 不会调用构造函数
		m_mX.B::B(100);     // 调用第一个构造函数

		// m_mX = 100;
	}
};

       显然这里编译器直接调用构造函数进行初始化就可以了,免去了多余的实例化临时对象,因此效率上会有大的提升,那么对于一些简单的成员进行初始化的时候(比如int类型),其实二者没有太大的区别,但是还是建议都是用初始化列表进行初始化。

初始化列表的细节问题:

  1. 虽然初始化列表的写法比较奇怪,但是站在编译器的角度来看,实际上初始化列表的代码还是被安插在函数体中去执行

2.当自己在函数体中已经写了一部分的代码,那么初始化列表的代码是优先于自己写的代码执行的。

3.对于对象成员的初始化顺序,是按照对象成员的定义顺序执行的,而不是按照初始化列表的顺序执行的。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-03-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 必须要使用初始化列表的情况:
  • 使用初始化列表的优势:
  • 初始化列表的细节问题:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档