c对象的构造过程并非发生在花括号{}内部而是严格分为两个阶段初始化阶段发生在进入构造函数函数体之前 在此阶段所有的非静态成员变量(包括基类子对象都必须被初始化 如果程序员提供了初始化列表则按照列表中的指定方式初始化 如果程序员未提供初始化列表编译器会尝试调用成员的默认构造函数进行隐式初始化 当程序控制权到达构造函数左侧花括号 **{** 时所有成员变量已经不再是“未定义”状态而是拥有了确定的值或状态计算/赋值阶段发生在构造函数函数体内部即 **{...}** 之间 此时执行的代码(如member value)本质上是赋值操作而非初始化 对于类类型成员这意味着先调用默认构造函数创建对象在调用赋值运算符修改其状态这通常比直接初始化(初始化列表效率更低为什么必须“先初始化后进入函数体”这一规则由c语言标准强制规定主要基于以下原因A.保证对象状态的合法性与安全性C强调对象在创建完成后应立即处于“有效状态”。如果允许成员变量在进入函数体前保持未初始化状态那么在函数体的第一行代码执行之前对象可能包含垃圾值对于内置类型或处于无效状态对于类类型。这会导致未定义行为特别是在发生异常或早期返回时。B.支持const和引用成员- const成员常量一旦定义就必须初始化且之后不能修改赋值。因此它们必须在初始化阶段完成赋值无法在函数体内通过赋值操作符进行修改- 引用成员引用必须在定义时绑定到一个对象且不能重新绑定。因此也必须在初始化阶段完成绑定。- 如果编译器不强制在进入函数体前完成初始化这两类成员将无法正确创建。C.处理没有默认构造函数的类类型如果一个成员对象所属的类没有默认构造函数(例如之定义了带参构造函数)编译器无法在隐式初始化阶段自动创建它。此时程序员必须在初始化列表中显示提供参数。如果编译器允许进入函数体后再处理那么在该成员未被初始化的情况下对象本身就处于不完整状态这是非法的。代码democlassMember{public:Member(){std::coutMember Default constructorstd::endl;}Member(inti){std::coutMember Param constructoristd::endl;}Memberoperator(intval){std::coutMember Assignment valstd::endl;return*this;}};classMyclass{Member m;public:Myclass(intval):m(val){std::coutMyclass constructor1 valstd::endl;}Myclass(){m42;std::coutMyclass constructor2std::endl;}};intmain(){Myclassmc(1);Myclass mc2;}输出Member Param constructor 1Myclass constructor1 1##此处只调用了一次构造函数高效且直接Member Default constructorMember Assignment 42Myclass constructor2##先调用默认构造函数初始化m,进入函数体后又调用赋值构造运算符。多了一次构造和赋值的开销。常用误区与注意事项误区初始化列表的顺序决定初始化顺序事实成员变量的初始化顺序严格遵循他们在类中声明的顺序而不是初始化列表中书写的顺序。classFoo{public:inta;intb;Foo(intval):b(val),a(b){}};intmain(){Foofo(1);std::coutfo.astd::endl;}在上述代码中a会使用未初始化的b值进行初始化导致未定义行为,例如上述代码虽然编译通过但fo的成员a打印出来的值为0并非预期的1误区内置类型如int不需要初始化事实对于内置类型如果在初始化列表中未指定且在类内没有就地初始化c11起支持他们的值将是未定义的垃圾值。虽然编译器确保了“初始化动作的发生”(即内存分配并标记为已构造)但对于内置类型这个“默认初始化”并不赋予其特定值如0除非他们是全局或静态对象。因此最佳实践是始终显示初始化所有成员。总结构造函数体{}内的代码是对已经存在的对象进行操作而不是创建对象的过程。初始化列表是成员变量诞生的地方构造函数体是成员变量出生后接受进一步逻辑处理的地方