初始化列表使用示例话不多说直接上一段最简单的代码#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 中建议优先使用初始化列表即使对内置类型也是如此以保持一致性并避免遗漏必须初始化的成员。