C++ 初始化列表详解
创始人
2024-05-09 04:32:37
0

目录

1.什么是初始化列表

2.什么时候需要使用初始化列表?

3.初始化列表的效率

4.初始化列表的初始化顺序


1.什么是初始化列表

class A {
public:A(int value):m_dada(value){}private:int m_dada;
};

如上图,红色圈起来的部分,就是构造函数的初始化列表,以冒号开始,冒号后面依次列出需要赋值的成员变量和值。

2.什么时候需要使用初始化列表?

(1)当有成员变量是引用类型时

(2)当有数据成员是常量时

(3)当父类的构造函数有参数时

(4)当成员变量所属类型的构造函数有参数时

2.1当有成员变量是引用类型时

可以看到类中有一个引用类型的变量m_data,直接编译会报错,提示“必须初始化引用”,即使在构造函数里面赋值也不行,此时必须使用初始化列表,下面看看使用初始化列表后的情况。

可以看到,使用初始化列表后,编译成功。

2.2当有数据成员是常量时

可以看到,类中有个const常量m_data,直接编译会报错,提示“必须初始化常量”。下面看看使用初始化列表后的情况。

可以看到,使用初始化列表后,编译成功。

2.3当父类的构造函数有参数时

可以看到,B类的父类是A,A的构造函数有一个参数,在实现B类的构造函数时,如果不处理A类的构造函数,就会编译报错,提示“A类没有合适的默认构造函数可用”。下面看下使用了初始化列表后的情况。

可以看到,使用初始化列表后,编译成功。

2.4当成员变量所属类型的构造函数有参数时

可以看到,B类有一个数据成员A m_a,在B类的构造函数中没有对这个m_a作处理,导致编译报错,提示“A没有合适的默认构造函数可以”。下面看看使用了初始化列表后的情况。

3.初始化列表的效率


#include "stdafx.h"#include 
using namespace std;class A {
public:A(){cout << "call A()" << endl;}~A(){cout << "call ~A()" << endl;}};class B
{
public:B(A value) : m_a(value){cout << "call B()" << endl;//m_a = value;}~B(){cout << "call ~B()" << endl;}private:A m_a;
};int main()
{A objA;B objB(objA);return 0;
}

上面这段代码,在B类的构造函数中,使用了初始化列表给成员变量A m_a进行赋值,运行结果如下:

可以看到在调用B类的构造函数时,调用了一次A类的析构函数,没有调用A类的构造函数,我想这是VS编译器的特殊设计造成的,换成其它编译器可能就不是这样了,这里我认为使用了初始化列表之后,应该在B类的构造函数中不会调用A类的构造函数和析构函数了,这样才符合使用初始化列表的效率更高的特点。

下面看一下没有使用初始化列表,而直接在B类构造函数中给m_a赋值的情况。

#include "stdafx.h"
#include 
using namespace std;class A {
public:A(){cout << "call A()" << endl;}~A(){cout << "call ~A()" << endl;}};class B
{
public:B(A value) {cout << "call B()" << endl;m_a = value;}~B(){cout << "call ~B()" << endl;}private:A m_a;
};int main()
{A objA;B objB(objA);return 0;
}

可以看到,如果直接在B类的构造函数中赋值,将会导致多一次A类构造函数的调用。

综上所述,使用初始化列表给成员变量设定初始值效率会更高,建议优先使用这种方法。对于基础类型的变量,比如int,bool类型,则没有必要非要采用初始化列表。

4.初始化列表的初始化顺序

编译器会将初始化列表一一转换成代码,并且这些代码会被放在用户编写的代码之前。初始化列表的初始化顺序是由数据成员的声明次序决定的,不是由初始化列表的排列次序决定的。

“初始化次序”和“初始化列表中的排列次序”如果不一致,将可能导致意想不到的风险。比如:

class C
{
private:int i;int j;
public:C(int value):j(value),i(j){cout << "i = " << i << endl;cout << "j = " << j << endl;}
};int main()
{C objC(10);return 0;
}

上述程序代码看起来像是要把j设初值为 value,再把i设初值为 j.问题在于,由于声明次序的缘故,初始化列表中的”i(j)”其实比”j(value)”更早执行。但因为j 一开始未有初值,所以i(j)的执行结果导致无法预知其值。

那么如何避免上面的问题呢,建议是如果要用一个成员变量的值给另外一个成员变量赋值,则建议将赋值的代码写在构造函数中,如下:

class C
{
private:int i;int j;
public:C(int value):j(value){i = j;cout << "i = " << i << endl;cout << "j = " << j << endl;}
};int main()
{C objC(10);return 0;
}

因为初始化列表的执行是在构造函数的用户代码之前,所以j会先被赋值。

接下来看看下面这段代码有没有什么问题。

class C
{
private:int i;int j;
public:C(int value):j(value){i = j;cout << "i = " << i << endl;cout << "j = " << j << endl;}
};class D : public C
{
private:int data;
public:int GetValue(){return data;}D(int value):data(value),C(GetValue()){cout << "data = " << data << endl;}
};int main()
{D objD(10);return 0;
}

上面的代码中,在D类的构造函数使用了初始化列表,在初始化列表中调用了成员函数来给父类的构造函数传参,乍一看好像没什么问题,但是从最后的运行结果来看,i和j未被成功赋值,这是为什么呢?

这是因为在初始化列表中,父类的构造函数被优先执行,导致data还没来得及赋值。所以建议将父类的构造函数放在初始化列表的最前面,以避免上述问题。

相关内容

热门资讯

保存时出现了1个错误,导致这篇... 当保存文章时出现错误时,可以通过以下步骤解决问题:查看错误信息:查看错误提示信息可以帮助我们了解具体...
汇川伺服电机位置控制模式参数配... 1. 基本控制参数设置 1)设置位置控制模式   2)绝对值位置线性模...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
表格中数据未显示 当表格中的数据未显示时,可能是由于以下几个原因导致的:HTML代码问题:检查表格的HTML代码是否正...
本地主机上的图像未显示 问题描述:在本地主机上显示图像时,图像未能正常显示。解决方法:以下是一些可能的解决方法,具体取决于问...
不一致的条件格式 要解决不一致的条件格式问题,可以按照以下步骤进行:确定条件格式的规则:首先,需要明确条件格式的规则是...
表格列调整大小出现问题 问题描述:表格列调整大小出现问题,无法正常调整列宽。解决方法:检查表格的布局方式是否正确。确保表格使...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...