新闻详情

新闻详情

首页 / 资讯中心 / 详情

c++构造函数初始化列表

发布时间:2026/10/1 10:34:46来源:尧图网络
c++构造函数初始化列表
初始化列表使用示例话不多说直接上一段最简单的代码#include iostream class Point{ private: int x; int y; public: Point(int i 0, int j 0): x(i), y(j) {} int getX() const { return x; } int getY() const { return y; } }; int main() { Point p(3,4); std::cout Point x: p.getX() std::endl; std::cout Point y: p.getY() std::endl; return 0; }看一下私有变量x和yPoint(int i 0, int j 0): x(i), y(j) {}给成员变量x和y传参这就是构造函数初始化列表运行结果为什么需要引入这个写法如果改成在函数体内赋值Point(int i 0, int j 0) { x i; // 这是赋值不是初始化 y j; }看起来结果一样但底层过程不同。引入初始化列表主要有以下几个原因原因一. 性能避免先默认构造再赋值对于int这种内置类型两种写法差别不大。但对于类类型的成员差别巨大下面看一个示例代码InitializerTest.cpp只是简单的初始化了两个对象#include iostream #include WithInitializerList.h #include WithoutInitializerList.h // 用日志观察成员对象的构造过程。 int main() { std::cout with initializer list:\n; WithInitializerList a; std::cout \nwithout initializer list:\n; WithoutInitializerList b; return 0; }WithInitializerList.h#ifndef WITH_INITIALIZER_LIST_H #define WITH_INITIALIZER_LIST_H #include Number.h class WithInitializerList { public: WithInitializerList() : number(10), id(1) {} ~WithInitializerList() { std::cout ~WithInitializerList()\n; } private: Number number; const int id; }; #endifWithoutInitializerList.h#ifndef WITHOUT_INITIALIZER_LIST_H #define WITHOUT_INITIALIZER_LIST_H #include Number.h class WithoutInitializerList { public: WithoutInitializerList() : id(1) { number Number(10); } ~WithoutInitializerList() { std::cout ~WithoutInitializerList()\n; } private: Number number; const int id; }; #endifNumber.h的代码会稍微多一点#ifndef NUMBER_H #define NUMBER_H #include iostream class Number { public: Number() : value(0), id(nextId) { nextId; std::cout Number() id id this this \n; } Number(int value) : value(value), id(nextId) { nextId; std::cout Number(int) id id this this value value \n; } ~Number() { std::cout ~Number() id id this this \n\n; } private: int value; int id; inline static int nextId 1; }; #endif运行结果如下可以看到相比用构造函数初始化列表普通的构造方法居然构造了两次对象。而且代码是这么写的WithoutInitializerList() { number Number(10); }明明看着是显式调用了Number带参的构造方法但是却调用了一次Number无参的构造方法这也是为什么会构造了两次对象的原因那为什么会调用了一次无参的构造方法要知道我们如果初始化一个类正常在一个main方法这么写number Number(10)。在同时存在有参和无参的构造方法时是不会调用一次Number的无参构造方法的但是我们的场景是在WithoutInitializerList的构造方法里面去初始化Number编译器要求执行WithoutInitializerList构造函数体时成员 number 已经必须是一个完整对象了所以编译器隐含执行Number number; 先构造成员调用 Number()。然后才执行你的构造函数体number Number(10); // 创建临时 Number(10)再赋值这里的 是赋值运算符不是构造语法。它的意思是把一个已经存在的 Number 对象的内容赋给另一个已经存在的 Number 对象而 Number(10) 本身确实调用了带参构造函数但它创建的是一个临时对象Number temporary(10);number.operator(temporary);它不是在“构造 number”。要在 number 第一次诞生时就调用 Number(int)必须写在外层类的初始化列表中从运行结果来看相比于初始化列表。普通改造在一个类存在类类型的成员的时候多了临时对象的构造和销毁等步骤。出于性能考虑。当然是选择构造函数初始化列表比较好原因二有些成员只能在初始化列表中初始化无法在函数体内赋值const 成员“expression must be a modifiable lvalue” 意思是“表达式必须是一个可修改的左值。”可以简单理解左值为可取地址的变量这里int被const修饰了意味着x不能修改了当然你可以直接这么写const int x 10;但是如果没有初始化列表。你const变量就没办法更改它的值了。初始化列表给你提供了这么一种方式让你的变量既可以是const,也不需要你声明就赋值可以等到初始化类对象的时候再去指定const变量的值引用成员B::B(int r) provides no initializer for:B.h(10, 14): reference member B::ref意思是构造函数B::B(int r)没有为引用成员B::ref提供初始化看起来有点奇怪现在看着是你不给我初始化呀其实在cpp中引用必须初始化。在这里想这么用本来使用引用的姿势就是错的你可以直接在声明引用的时候就去初始化好所以对于引用成员其实也和const 成员一样初始化列表给我们提供了一种延迟和动态修改本来不可修改的变量的方式没有默认构造函数的类成员改造一下上面的B.hno default constructor exists for class A意思是说A没有默认构造函数为什么需要A有默认构造函数呢因为假设你在外部调用B(A aaaa)这个构造方法C必须先想办法构造成员A怎么构造 A需要调用 A() ,但是 A 没有 A()所以只能报编译错误了你可能会问为什么不能先进入函数体因为 C 的构造顺序是固定的1.基类构造 2. 成员变量构造 3.执行构造函数体一定是这个顺序。所以B(A aaaa) { a aaaa; }不能理解成1.创建 B 2. 进入 B 构造函数 3. a aaaa 4. 再创建 A而是 1. 创建 B 2. 创建 A a 3. 进入 B 构造函数体 4. 执行 a aaaa问题就出在第②步。既然你说需要A有默认构造函数那我就加上吧服啦又报了另一个错说function A::operator(const A ) (declared implicitly) cannot be referenced -- it is a deleted function意思是编译器生成的A::operator被删除了导致无法赋值了why原来class A里面的const int x 0;编译器担心你在B(A aaaa)这里传进去的A由于A可以使用初始化列表修改x的值那你再把aaaa赋值给a的时候相当于通过赋值改变了一个const的值解决办法很简单将const int x 0;的const去掉就好了基类没有默认构造函数class Derived : public Base { public: Derived(int v) : Base(v) {} // 必须显式调用基类构造 };原因三语义正确性初始化列表体现的是初始化语义而函数体内赋值体现的是赋值语义。对于某些类型这两者有本质区别如const、引用、无默认构造的类型。原因四成员初始化顺序的确定性class A { int a, b; public: A() : b(1), a(2) {} // 实际按 a 再 b 的顺序初始化可能有警告 };原因五 避免临时对象和潜在错误对于需要在构造时立即确定初值的场景如const成员、引用绑定初始化列表是唯一正确的方式避免了使用未初始化值的风险。总结原因说明效率避免默认构造 赋值的额外开销语法强制const、引用、无默认构造的成员必须用语义清晰区分初始化与赋值顺序确定明确成员初始化顺序正确性保证对象构造时成员即处于有效状态因此C 中建议优先使用初始化列表即使对内置类型也是如此以保持一致性并避免遗漏必须初始化的成员。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

PHP7.2字符串长度异常怎么解决 2026/10/1 11:17:54

PHP7.2字符串长度异常怎么解决

前言"长度不对"是 PHP 里最容易被误判成玄学的一类问题。症状往往是这样的:一个中文标题,肉眼数是 5 个字,strlen() 告诉你是 15;mb_strlen() 说是 5,但把它按 20 字符截断后页面出现了乱码问号;…

阅读更多 →
从位宽到OCI加载:PL/SQL Developer 12在64位系统下的部署与排障指南 2026/10/1 11:17:48

从位宽到OCI加载:PL/SQL Developer 12在64位系统下的部署与排障指南

做了十多年Oracle开发和数据库运维,直到现在我还是觉得, bit 这个词是所有技术指标里最容易被低估的一个。很多同事把“64位”简单理解成“性能更好”,可当你要在一台64位Windows机器上把PL/SQL Developer 12 (64 bit)部署得稳稳当当的时候…

阅读更多 →
Python疫情数据分析实战:从脏数据清洗到交互可视化 2026/10/1 11:17:47

Python疫情数据分析实战:从脏数据清洗到交互可视化

简介:本资源是一份高质量的Python课程设计成果,面向计算机、电子信息工程、数学等专业本科生,聚焦COVID-19疫情数据的全流程分析与可视化实践,适用于课程设计、期末大作业及毕业设计选题参考。压缩包共66个文件,含17个…

阅读更多 →
Redis夺命十二问:从性能原理到缓存治理的实战指南 2026/10/1 11:17:41

Redis夺命十二问:从性能原理到缓存治理的实战指南

Redis这三个字母,后端面试里出现的频率快赶上自我介绍里的“熟练掌握”了。但说实话,面试问Redis和实战用Redis完全是两码事:面试官问单线程为什么快,线上却因为一个大key把CPU打满;文档说AOF比RDB稳,真出故…

阅读更多 →
Windows 11下安装PyCharm全攻略:版本选择到解释器配置 2026/10/1 11:17:41

Windows 11下安装PyCharm全攻略:版本选择到解释器配置

很多人第一次接触Python编程,查资料时看到的第一个建议往往是“安装PyCharm”,但真到自己动手,光是“选哪个版本”这一步就能卡住不少人。官网界面上Community和Professional两个按钮并列排在那里,点哪个?下载完安装时…

阅读更多 →
OpenCV RotatedRect完全指南:角度、坐标与实战避坑 2026/10/1 11:17:40

OpenCV RotatedRect完全指南:角度、坐标与实战避坑

1. 为什么RotatedRect总让人犯迷糊 先聊聊我自己的经历。早几年做车牌识别项目的时候,用 cv2.minAreaRect() 拿到一个旋转矩形的结果,打印出来一看 ((x, y), (w, h), angle) ,我当时下意识以为angle就是数学课本里那个逆时针角度&#xf…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉