C++成员初始化列表

server/2024/10/17 18:20:47/

我们在类的构造函数中使用成员初始化列表可以带来效率上的提升,那么成员初始化列表在编译后会发生什么就是这篇文章要探究的问题

文章目录

  • 引入
  • 成员初始化列表
    • 用成员初始化列表优化上面的代码
    • 成员初始化列表展开
    • 成员初始化列表的潜在危险
  • 参考资料


引入

考虑下面这个例子

#include <iostream>
using namespace std;class String {int len;
public:String() {len = 0;}String(int l) {len = l;}String(const String& str) {len = str.len;}
};class Word {String _name;int _cnt;
public:Word() {_name = 0;_cnt = 0;}
};
int main()
{Word word;
}

其在VS2022下的汇编码,可以看到在 Word 的默认构造函数中调用了 String 的两个构造函数,分别是默认构造函数和带参数的构造函数

这里在 [rbp+0C4h] 的位置即 _name=0 右边的 0 作为临时 String 类对象,调用带参数的构造函数,然后将 [rbp+0C4h] 里的值逐位拷贝至 [this] 位置(首位置就是成员 _name的位置),相当于是编译器合成了默认的赋值运算符

其编译器对上述Word默认构造函数中代码的扩张结果可能如下:

Word::Word()
{_name.String::String();	String temp = String( 0 );_name.String::operator=( temp );temp.String::~String();_cnt = 0;
}

成员初始化列表

用成员初始化列表优化上面的代码

上面的代码我们可以用成员初始化列表进行优化

Word::Word : _name( 0 )
{_cnt = 0;
}

它会被扩张成下面的样子

Word::Word()
{_name.String::String( 0 );_cnt = 0;
}

汇编代码可以看到只调用了一次带参的构造函数,且可以看到是在 _cnt=0 之前先进行了 _name 的初始化


成员初始化列表展开

当然我们也可以将两个变量全用成员初始化列表实现

Word::Word() : _cnt(0), _name(0)
{}

编译器会逐个操作初始化列表,以适当的次序在构造函数内安插初始化操作,并且位于任何显式的用户代码之前,例如上面的代码会扩充为:

Word::Word()
{_name.String::String( 0 );_cnt = 0;
}

可以看到扩充代码是以类中成员变量的声明次序决定,不是由初始化列表的排列次序决定,下面的汇编代码也验证了这一点。


成员初始化列表的潜在危险

如果类中成员变量的声明次序与初始化列表中的项目排列次序是混乱的或是有差异的,可能会导致意想不到的问题。

考虑下面这个例子:

class X {
public:int i;int j;
public:X(int val) : j(val), i(j) {}
};

我们在写这个构造函数的本意可能是想先用 val 初始化 j 再用 j 来初始化 i,我们用 1 来初始化,然后输出 x.ix.j

可以看到结果不是我们希望的那样,因为正如前面所述,成员初始化列表会以变量声明顺序进行初始化,而不是初始化列表中的排列顺序。

我们可以改为下面的代码,就没有问题,因为合成的代码会插入到用户显式的代码之前。

class X {
public:int i;int j;
public:X(int val) : j(val) {i = j;}
};

如果一个派生类成员函数被调用,其返回值当作基类构造函数的一个参数,将会如何:

class FooBar : public X {int _fval;
public:int fval() {return _fval;}FooBar( int val ) : _fval( val ), X ( fval() ) {} 
}

它的可能扩张结果如下,也不是一个好主意

FooBar::FooBar ()
{X::X( this, this->fval() );_fval = val;
}

以 1 作为初始化参数,然后顺序输出 _fvalij 结果如下


参考资料

《深度探索C++对象模型》—— Stanley B.Lippman著,侯捷译


http://www.ppmy.cn/server/28334.html

相关文章

SecGPT 全球首个网络安全开源大模型且可以在CPU上运行的网络安全大模型

SecGPT 网络安全大模型 探索使用网络安全知识训练大模型,能达到怎样的能力边界。 SecGPT的愿景是将人工智能技术引入网络安全领域,以提高网络防御的效率和效果。其使命是推动网络安全智能化,为社会提供更安全的数字生活环境。 SecGPT可以作为基座安全模型,用于探索各种网…

004 springCloudAlibaba Gateway

文章目录 gatewayServerGatewayServerApplication.javaServletInitializer.javaapplication.yamlpom.xml orderServerOrderController.javaProductClient.javaOrderServerApplication.javaServletInitializer.javaapplication.yamlpom.xml productServerProductController.java…

深入探索达梦数据库:关键技术学习路径与实战指南

导语 随着国产数据库的崛起&#xff0c;达梦数据库作为国内自主研发的关系型数据库管理系统&#xff0c;以其优异的性能、强大的功能和高度的兼容性赢得了广泛认可。对于数据库工程师、开发人员以及IT专业人士来说&#xff0c;深入学习和掌握达梦数据库技术不仅是提升专业能力的…

实现并发请求数量控制:提高网页性能的关键

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

MySQL-配置文件

1、配置文件格式 配置文件中启动选项被分为若干组&#xff0c;每组都有一个’组名’&#xff0c;用[ ] 包裹每组下都可定义若干个启动选项配置文件中指定的启动选项不允许添加--前缀配置文件中每行只能指定一个具体启动选项相关分组示例如下&#xff1a; [server] (具体启动选…

第11章 数据库技术(第一部分)

一、数据库技术术语 &#xff08;一&#xff09;术语 1、数据 数据描述事物的符号描述一个对象所用的标识&#xff0c;可以文字、图形、图像、语言等等 2、信息 现实世界对事物状态变化的反馈。可感知、可存储、可加工、可再生。数据是信息的表现形式和载体&#xff0c;信…

Pytorch迁移学习训练病变分类模型

划分数据集 1.创建训练集文件夹和测试集文件夹 # 创建 train 文件夹 os.mkdir(os.path.join(dataset_path, train))# 创建 test 文件夹 os.mkdir(os.path.join(dataset_path, val))# 在 train 和 test 文件夹中创建各类别子文件夹 for Retinopathy in classes:os.mkdir(os.pa…

为什么叫“机器学习”Machine Learning 而不是叫“计算机学习”?

有一门学科“机器学习”火了起来&#xff0c;它是计算机科学与数学结合的产物&#xff0c;它的目的是使计算机“聪明”起来&#xff0c;实现人工智能。可是&#xff0c;令人困惑的是它明明就是计算机学习&#xff0c;为什么不叫“计算机学习”而叫“机器学习”呢&#xff1f;这…