C++初始化列表

一、核心概念:什么是构造函数初始化列表?

构造函数初始化列表是在构造函数体执行之前 ,直接对类的成员变量进行初始化的语法结构。语法格式

cpp 复制代码
类名(参数列表) : 成员变量1(初始值1), 成员变量2(初始值2), ... {
    // 构造函数体(可选)
}
二、为什么要用初始化列表?
  1. 必须使用的场景

    • 初始化const 成员变量(常量成员只能初始化,不能赋值);
    • 初始化引用成员(引用必须在定义时绑定对象,不能后续赋值);
    • 初始化基类成员(继承场景中,子类构造函数需通过初始化列表给父类构造传参);
    • 初始化没有默认构造函数的成员对象(成员变量是另一个类的对象,且该类无默认构造)。
  2. 性能优势

    • 不用初始化列表时,成员变量会先被默认构造,再在构造函数体中赋值(相当于 "先创建再修改");
    • 用初始化列表时,成员变量直接初始化("一步到位"),避免额外的默认构造 / 析构开销。
三、基础用法示例
cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

class Person {
private:
    // 不同类型的成员变量
    const int id;       // 常量成员
    string& name;       // 引用成员
    int age;            // 普通成员
    double height;      // 普通成员

public:
    // 构造函数初始化列表(核心)
    Person(int id_val, string& name_val, int age_val = 18, double height_val = 170.0)
        : id(id_val),   // 初始化常量id
          name(name_val), // 初始化引用name
          age(age_val),   // 初始化普通成员age
          height(height_val) // 初始化普通成员height
    {
        // 构造函数体(可选,可做额外逻辑,比如打印)
        cout << "Person对象已初始化:id=" << id << ", name=" << name << endl;
    }

    // 打印成员
    void showInfo() {
        cout << "id: " << id << ", name: " << name << ", age: " << age << ", height: " << height << endl;
    }
};

int main() {
    string name = "张三";
    Person p(1001, name); // 调用构造函数,使用默认参数age=18、height=170.0
    p.showInfo();

    // 修改引用绑定的变量,验证引用特性
    name = "李四";
    p.showInfo();
    return 0;
}

输出结果

cpp 复制代码
Person对象已初始化:id=1001, name=张三
id: 1001, name: 李四, age: 18, height: 170

关键解释

  • id(id_val):常量成员id只能通过初始化列表初始化,无法在构造函数体中赋值;
  • name(name_val):引用成员name必须绑定到一个已存在的字符串对象,初始化列表是唯一方式;
  • 普通成员age/height用初始化列表初始化,比在构造函数体中赋值更高效。
四、注意事项
  1. 初始化顺序 :成员变量的初始化顺序只取决于它们在类中的声明顺序,与初始化列表中的顺序无关!示例(易错点):

    cpp 复制代码
    class Test {
    private:
        int a;
        int b;
    public:
        // 初始化列表顺序:b先,a后;但实际初始化顺序是a→b(按声明顺序)
        Test(int val) : b(val), a(b) {
            cout << "a=" << a << ", b=" << b << endl; // a是随机值,b=val
        }
    };
  2. 默认参数与初始化列表 :构造函数的参数可以带默认值,但默认值只能写在参数列表中,不能写在初始化列表里。

  3. 空初始化列表:如果构造函数没有显式写初始化列表,编译器会自动调用成员变量的默认构造函数(如果有)。


总结

  1. 核心作用 :初始化列表是构造函数中初始化成员变量的高效方式,也是初始化const成员、引用成员的唯一方式;
  2. 关键规则:成员变量的初始化顺序由类内声明顺序决定,与初始化列表顺序无关;
  3. 性能优势:相比构造函数体赋值,初始化列表避免了成员变量的 "默认构造 + 赋值" 两步操作,直接一步初始化。

场景二:

一、先理解:引用的本质(基础铺垫)

引用(&)不是一个独立的变量,而是已存在变量的 "别名",它的核心规则是:

  1. 必须在定义时绑定对象 (不能先定义、后绑定,像指针那样int* p; p = &a;是不允许的);
  2. 一旦绑定,终身不能更改绑定对象(引用的指向是 "只读" 的);
  3. 引用不能为空 (必须绑定到一个有效的对象,不能像空指针那样int* p = nullptr;)。

举个简单的非类场景例子,先感受引用的绑定规则:

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int a = 10;
    int& ref = a; // ✅ 正确:定义引用时直接绑定a
    // int& ref;   // ❌ 错误:引用定义时未绑定对象
    // ref = a;    // 这不是"绑定",而是给ref(即a)赋值

    ref = 20;     // 本质是给a赋值,不是更改引用绑定
    cout << a << endl; // 输出20,验证ref是a的别名
    return 0;
}

二、类中引用成员的特殊约束:必须用初始化列表

类的成员变量(包括引用成员)的 "定义时机" 是:创建类对象时,先执行初始化列表,再执行构造函数体

  • 初始化列表阶段:是引用成员 "定义并绑定对象" 的唯一时机;
  • 构造函数体阶段:引用成员已经完成定义(绑定),此时再试图 "赋值",只是修改引用绑定的对象的值,而非重新绑定。
1. 错误示例:试图在构造函数体中 "绑定" 引用成员(编译报错)
cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

class Person {
private:
    string& name; // 引用成员

public:
    // 错误写法:试图在构造函数体中给引用成员"赋值"(实际不是绑定)
    Person(string n) {
        name = n; // 编译报错!引用成员name未初始化(未绑定对象)
    }
};

int main() {
    string str = "张三";
    Person p(str);
    return 0;
}

编译错误信息(GCC 为例)

cpp 复制代码
error: uninitialized reference member 'Person::name' in constructor
     Person(string n) {
                     ^
note: 'std::string& Person::name' should be initialized
     string& name;
            ^~~~

错误原因 :构造函数体执行前,引用成员name必须完成 "定义 + 绑定对象",但你没写初始化列表,导致name是 "未初始化的引用"(违反引用必须绑定对象的规则),编译器直接报错。

2. 正确示例:用初始化列表绑定引用成员
cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

class Person {
private:
    string& name; // 引用成员(必须绑定到外部string对象)

public:
    // 正确:初始化列表中绑定引用成员到外部对象
    Person(string& n) : name(n) { 
        // 构造函数体执行时,name已经绑定到n(即外部的str)
        cout << "引用成员name已绑定:" << name << endl;
    }

    void changeName(string newName) {
        name = newName; // ✅ 这是修改绑定对象的值,不是更改绑定
    }

    void showName() {
        cout << "当前name:" << name << endl;
    }
};

int main() {
    // 步骤1:定义外部string对象(引用必须绑定到已存在的对象)
    string str = "张三";
    
    // 步骤2:创建Person对象,初始化列表将name绑定到str
    Person p(str); 
    p.showName(); // 输出:当前name:张三

    // 步骤3:修改引用绑定的对象的值(不是更改绑定)
    p.changeName("李四");
    p.showName(); // 输出:当前name:李四
    cout << "外部str的值:" << str << endl; // 输出:李四(验证是同一个对象)

    // 步骤4:验证引用不能更改绑定
    string str2 = "王五";
    // p.name = str2; // 错误!这是给str赋值为"王五",不是绑定到str2
    p.changeName(str2);
    p.showName(); // 输出:当前name:王五
    cout << "外部str的值:" << str << endl; // 输出:王五(仍绑定str)
    return 0;
}

输出结果

cpp 复制代码
引用成员name已绑定:张三
当前name:张三
当前name:李四
外部str的值:李四
当前name:王五
外部str的值:王五

三、关键细节:引用成员的常见坑点

1. 不能绑定到临时对象(否则会悬空)
cpp 复制代码
// 危险写法:绑定到临时对象(函数参数n是拷贝,函数结束后销毁)
Person(string n) : name(n) { } 

// 调用时:
Person p("张三"); // "张三"是临时string,构造函数结束后n销毁,name悬空(野引用)

解决 :引用成员必须绑定到生命周期更长的对象 (如 main 函数中的 str、全局变量等),或改用const string&接收临时对象(但仍需注意生命周期)。

2. 引用成员的 "赋值"≠"重新绑定"

很多人误以为name = str2是让引用绑定到新对象,其实:

  • namestr的别名,name = str2等价于str = str2(修改 str 的值);
  • C++ 没有语法能让引用 "换绑" 对象,一旦初始化列表绑定,终身不变。
3. 引用成员 vs 指针成员

如果需要 "动态切换绑定对象",可以用指针成员代替引用成员:

cpp 复制代码
class Person {
private:
    string* name; // 指针成员(可先定义后赋值)
public:
    Person(string* n) {
        name = n; // ✅ 指针允许先定义后赋值
    }
    void changeBind(string* n) {
        name = n; // ✅ 指针可切换绑定对象
    }
};

总结

  1. 核心规则:引用成员必须在初始化列表中绑定对象,构造函数体中无法完成 "绑定"(仅能修改绑定对象的值);
  2. 本质原因:引用的特性是 "定义时必须绑定、绑定后不可更改",而类成员的 "定义时机" 是初始化列表阶段;
  3. 避坑要点:引用成员不能绑定临时对象(会悬空),"赋值" 不是 "重新绑定",需切换绑定对象可改用指针。

场景三:

一、先理解:继承中对象的初始化顺序

当创建子类对象时,C++ 的初始化流程是固定的,和代码书写顺序无关:

  1. 执行父类构造函数(初始化父类成员);
  2. 执行子类的初始化列表(初始化子类成员);
  3. 执行子类的构造函数体(可选的额外逻辑)。

关键点:父类构造函数的执行时机早于子类的任何成员初始化,因此子类无法在自己的构造函数体中 "回头" 初始化父类成员 ------ 必须在子类的初始化列表中,显式调用父类构造函数并传递参数。


二、为什么必须用初始化列表给父类传参?

假设父类没有默认构造函数(即父类只有带参数的构造函数),那么:

  • 子类必须显式调用父类的带参构造函数(否则编译器不知道如何初始化父类成员);
  • 调用父类构造函数的唯一语法,就是写在子类构造函数的初始化列表中;
  • 如果子类不写,编译器会尝试调用父类的默认构造函数,若父类没有默认构造,直接编译报错。
基础场景:单继承 + 父类带参构造

先看错误示例(试图在子类构造函数体中初始化父类成员):

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 父类:Person
class Person {
protected:
    string name;
    int age;
public:
    // 父类只有带参构造,无默认构造
    Person(string n, int a) : name(n), age(a) {
        cout << "父类Person构造:" << name << "," << age << endl;
    }

    void showPerson() {
        cout << "姓名:" << name << ",年龄:" << age << endl;
    }
};

// 子类:Student(继承Person)
class Student : public Person {
private:
    int score; // 子类独有成员
public:
    // 错误写法:试图在构造函数体中给父类成员赋值
    Student(string n, int a, int s) {
        // 编译报错!父类Person无默认构造,且未在初始化列表中调用其带参构造
        name = n; // 即使父类成员是public/protected,这也是"赋值"而非"初始化"
        age = a;
        score = s;
    }
};

int main() {
    Student s("张三", 18, 90);
    return 0;
}

编译错误信息(GCC 为例)

cpp 复制代码
error: no matching function for call to 'Person::Person()'
     Student(string n, int a, int s) {
                                    ^
note: candidate: Person::Person(std::string, int)
     Person(string n, int a) : name(n), age(a) {
                               ^~~~~~
note:   candidate expects 2 arguments, 0 provided

错误核心:编译器尝试调用父类Person()(默认构造),但父类没有,且子类未显式调用带参构造。


三、正确示例:子类初始化列表调用父类构造

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 父类:Person
class Person {
protected:
    string name;
    int age;
public:
    // 父类带参构造(无默认构造)
    Person(string n, int a) : name(n), age(a) {
        cout << "父类Person构造:" << name << "," << age << endl;
    }

    void showPerson() {
        cout << "姓名:" << name << ",年龄:" << age << endl;
    }
};

// 子类:Student(继承Person)
class Student : public Person {
private:
    int score; // 子类独有成员
public:
    // 正确写法:子类初始化列表中调用父类构造函数,并初始化自身成员
    Student(string n, int a, int s) 
        : Person(n, a), // 第一步:调用父类带参构造,初始化父类成员name/age
          score(s)      // 第二步:初始化子类成员score
    {
        // 第三步:执行子类构造函数体(可选)
        cout << "子类Student构造:分数=" << score << endl;
    }

    void showStudent() {
        showPerson(); // 调用父类方法
        cout << "分数:" << score << endl;
    }
};

int main() {
    // 创建子类对象,触发初始化流程
    Student s("张三", 18, 95);
    s.showStudent();
    return 0;
}

输出结果(验证初始化顺序):

cpp 复制代码
父类Person构造:张三,18
子类Student构造:分数=95
姓名:张三,年龄:18
分数:95

关键解释

  1. Person(n, a):在子类初始化列表中,显式调用父类的带参构造函数,传递na初始化父类的nameage
  2. 初始化顺序:先执行Person(n, a)(父类构造),再执行score(s)(子类成员初始化),最后执行子类构造函数体;
  3. 即使父类成员是protected(子类可访问),也不建议在子类构造函数体中赋值 ------ 因为这是 "先默认构造父类成员,再赋值",效率低(尤其父类成员是大对象时)。

四、进阶场景:多继承的初始化列表

如果子类继承多个父类,初始化列表中按父类的继承顺序调用各父类的构造函数(注意:初始化列表的书写顺序不影响执行顺序,执行顺序由继承顺序决定)。

示例:多继承
cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 父类1:Person
class Person {
protected:
    string name;
public:
    Person(string n) : name(n) {
        cout << "Person构造:" << name << endl;
    }
};

// 父类2:Score
class Score {
protected:
    int math;
public:
    Score(int m) : math(m) {
        cout << "Score构造:数学=" << m << endl;
    }
};

// 子类:Student(多继承Person和Score)
// 继承顺序:Person → Score
class Student : public Person, public Score {
private:
    int grade; // 子类独有成员
public:
    // 初始化列表:书写顺序是Score→Person,但执行顺序由继承顺序决定(Person先)
    Student(string n, int m, int g) 
        : Score(m),  // 书写顺序1:Score构造
          Person(n), // 书写顺序2:Person构造
          grade(g)   // 子类成员初始化
    {
        cout << "Student构造:年级=" << g << endl;
    }

    void show() {
        cout << "姓名:" << name << ",数学:" << math << ",年级:" << grade << endl;
    }
};

int main() {
    Student s("李四", 90, 3);
    s.show();
    return 0;
}

输出结果(验证执行顺序):

cpp 复制代码
Person构造:李四
Score构造:数学=90
Student构造:年级=3
姓名:李四,数学:90,年级:3

关键注意

  • 多继承时,父类构造函数的执行顺序 = 子类继承列表中的顺序public Person, public Score → 先 Person,后 Score);
  • 初始化列表中父类构造的书写顺序不影响执行顺序,但建议和继承顺序一致,避免代码阅读混乱。

五、特殊情况:父类有默认构造函数

如果父类有默认构造函数(无参构造),子类可以不写初始化列表 ------ 编译器会自动调用父类的默认构造函数:

cpp 复制代码
// 父类:有默认构造
class Person {
protected:
    string name = "未知"; // C++11类内初始化
public:
    // 默认构造(无参)
    Person() {
        cout << "Person默认构造" << endl;
    }
};

// 子类:无需在初始化列表中调用父类构造
class Student : public Person {
private:
    int score;
public:
    Student(int s) : score(s) { // 只初始化子类成员
        cout << "Student构造:分数=" << s << endl;
    }
};

总结

  1. 核心规则:子类必须通过初始化列表调用父类构造函数(尤其是父类无默认构造时),这是初始化父类成员的唯一方式;
  2. 初始化顺序
    • 单继承:先执行父类构造 → 再初始化子类成员 → 最后执行子类构造函数体;
    • 多继承:父类构造的执行顺序 = 子类继承列表的顺序(与初始化列表书写顺序无关);
  3. 效率与规范 :即使父类有默认构造,也建议显式在初始化列表中调用(如Person()),让代码逻辑更清晰;
  4. 避坑要点:多继承时,初始化列表的父类构造书写顺序不影响执行顺序,建议与继承顺序一致。

情况四:

一、先理解:成员对象的初始化规则

当一个类(简称 "外部类")包含另一个类的对象(简称 "成员对象")时,外部类对象的创建流程是:

  1. 执行成员对象的构造函数(初始化成员对象);
  2. 执行外部类的初始化列表(初始化外部类的其他成员);
  3. 执行外部类的构造函数体(可选逻辑)。

关键点:

  • 如果成员对象的类有默认构造函数(无参构造),编译器会自动调用默认构造初始化成员对象,外部类无需处理;
  • 如果成员对象的类没有默认构造函数(只有带参构造),编译器无法 "猜" 出初始化参数,必须由外部类在初始化列表中显式调用成员对象的带参构造函数。

二、基础场景:单个成员对象(无默认构造)

1. 错误示例:未在初始化列表中初始化成员对象
cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 成员对象的类:Date(只有带参构造,无默认构造)
class Date {
private:
    int year, month, day;
public:
    // 带参构造(无默认构造)
    Date(int y, int m, int d) : year(y), month(m), day(d) {
        cout << "Date构造:" << y << "-" << m << "-" << d << endl;
    }

    void showDate() {
        cout << year << "-" << month << "-" << day << endl;
    }
};

// 外部类:Person(包含Date类型的成员对象birthday)
class Person {
private:
    string name;
    Date birthday; // 成员对象:Date类(无默认构造)
public:
    // 错误写法:未在初始化列表中调用Date的带参构造
    Person(string n, int y, int m, int d) {
        name = n;
        // 编译报错!Date无默认构造,编译器无法初始化birthday
        // 即使想在这里赋值:birthday = Date(y,m,d); 也不行------因为第一步已经失败
    }
};

int main() {
    Person p("张三", 2000, 1, 1);
    return 0;
}

编译错误信息(GCC 为例)

cpp 复制代码
error: no matching function for call to 'Date::Date()'
     Person(string n, int y, int m, int d) {
                                          ^
note: candidate: Date::Date(int, int, int)
     Date(int y, int m, int d) : year(y), month(m), day(d) {
                                 ^~~~
note:   candidate expects 3 arguments, 0 provided

错误核心:编译器尝试调用Date()(默认构造)初始化birthday,但Date类只有带参构造,且外部类未显式调用。

2. 正确示例:外部类初始化列表调用成员对象构造
cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 成员对象的类:Date(只有带参构造)
class Date {
private:
    int year, month, day;
public:
    Date(int y, int m, int d) : year(y), month(m), day(d) {
        cout << "Date构造:" << y << "-" << m << "-" << d << endl;
    }

    void showDate() {
        cout << year << "-" << month << "-" << day << endl;
    }
};

// 外部类:Person(包含Date类型的成员对象)
class Person {
private:
    string name;
    Date birthday; // 成员对象:Date(无默认构造)
public:
    // 正确写法:在初始化列表中调用Date的带参构造,初始化birthday
    Person(string n, int y, int m, int d)
        : name(n),               // 初始化外部类普通成员
          birthday(y, m, d)      // 初始化成员对象:调用Date(y,m,d)
    {
        // 构造函数体:此时birthday已经初始化完成
        cout << "Person构造:" << name << endl;
    }

    void showPerson() {
        cout << "姓名:" << name << ",生日:";
        birthday.showDate();
    }
};

int main() {
    // 创建Person对象,触发初始化流程
    Person p("张三", 2000, 1, 1);
    p.showPerson();
    return 0;
}

输出结果(验证初始化顺序):

cpp 复制代码
Date构造:2000-1-1
Person构造:张三
姓名:张三,生日:2000-1-1

关键解释

  1. birthday(y, m, d):在外部类Person的初始化列表中,显式调用Date的带参构造函数,传递y/m/d初始化成员对象birthday
  2. 初始化顺序:先执行birthday(y, m, d)(成员对象构造),再执行name(n)(外部类成员初始化),最后执行Person的构造函数体;
  3. 即使尝试在Person构造函数体中写birthday = Date(y,m,d),也无法解决问题 ------ 因为构造函数体执行前,birthday必须先完成初始化,而编译器第一步就会因找不到Date的默认构造而报错。

三、进阶场景:多个成员对象(均无默认构造)

如果外部类包含多个无默认构造的成员对象 ,初始化列表中需要依次调用每个成员对象的带参构造,且成员对象的构造顺序 = 其在外部类中的声明顺序(与初始化列表书写顺序无关)。

示例:多个成员对象
cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 成员对象1:Date(无默认构造)
class Date {
private:
    int year, month, day;
public:
    Date(int y, int m, int d) : year(y), month(m), day(d) {
        cout << "Date构造:" << y << "-" << m << "-" << d << endl;
    }
};

// 成员对象2:Score(无默认构造)
class Score {
private:
    int math, english;
public:
    Score(int m, int e) : math(m), english(e) {
        cout << "Score构造:数学=" << m << ",英语=" << e << endl;
    }
};

// 外部类:Student(包含Date和Score两个成员对象)
class Student {
private:
    // 成员对象声明顺序:先birthday(Date),后score(Score)
    Date birthday;
    Score score;
    string name;
public:
    // 初始化列表书写顺序:score → birthday → name(与声明顺序不同)
    Student(string n, int y, int m, int d, int ma, int en)
        : score(ma, en),    // 书写顺序1:Score构造
          birthday(y, m, d),// 书写顺序2:Date构造
          name(n)           // 外部类成员初始化
    {
        cout << "Student构造:" << name << endl;
    }
};

int main() {
    Student s("李四", 2001, 2, 2, 90, 85);
    return 0;
}

输出结果(验证成员对象构造顺序):

cpp 复制代码
Date构造:2001-2-2
Score构造:数学=90,英语=85
Student构造:李四

关键注意

  • 成员对象的构造顺序 = 其在外部类中的声明顺序birthday先声明 → 先构造,score后声明 → 后构造);
  • 初始化列表中成员对象的书写顺序不影响构造顺序,但建议和声明顺序一致,避免代码阅读混乱。

四、嵌套场景:成员对象本身也包含无默认构造的成员

如果成员对象的类(如Date)本身也包含无默认构造的成员对象(如Time),则需要层层通过初始化列表传递参数:

cpp 复制代码
#include <iostream>
using namespace std;

// 最内层:Time(无默认构造)
class Time {
private:
    int hour, minute;
public:
    Time(int h, int mi) : hour(h), minute(mi) {
        cout << "Time构造:" << h << ":" << mi << endl;
    }
};

// 中间层:Date(包含Time成员对象,无默认构造)
class Date {
private:
    int year, month, day;
    Time time; // 成员对象:Time(无默认构造)
public:
    // Date的初始化列表:调用Time的构造
    Date(int y, int m, int d, int h, int mi)
        : year(y), month(m), day(d), time(h, mi) {
        cout << "Date构造:" << y << "-" << m << "-" << d << endl;
    }
};

// 最外层:Person(包含Date成员对象,无默认构造)
class Person {
private:
    string name;
    Date birthday; // 成员对象:Date(无默认构造)
public:
    // Person的初始化列表:调用Date的构造,层层传递Time的参数
    Person(string n, int y, int m, int d, int h, int mi)
        : name(n), birthday(y, m, d, h, mi) {
        cout << "Person构造:" << n << endl;
    }
};

int main() {
    // 传递所有参数,层层初始化
    Person p("王五", 2002, 3, 3, 10, 30);
    return 0;
}

输出结果(验证嵌套初始化顺序):

cpp 复制代码
Time构造:10:30
Date构造:2002-3-3
Person构造:王五

五、特殊情况:成员对象有默认构造

如果成员对象的类有默认构造函数(无参构造),外部类可以不写初始化列表 ------ 编译器会自动调用成员对象的默认构造

cpp 复制代码
// 成员对象的类:有默认构造
class Date {
private:
    int year = 2000, month = 1, day = 1; // C++11类内初始化
public:
    // 默认构造(无参)
    Date() {
        cout << "Date默认构造:" << year << "-" << month << "-" << day << endl;
    }
};

// 外部类:无需在初始化列表中调用Date构造
class Person {
private:
    string name;
    Date birthday; // 成员对象:Date(有默认构造)
public:
    Person(string n) : name(n) { // 只初始化name
        cout << "Person构造:" << name << endl;
    }
};

总结

  1. 核心规则:当成员对象的类无默认构造函数时,外部类必须在初始化列表中显式调用该成员对象的带参构造函数,这是唯一能完成初始化的方式;
  2. 初始化顺序
    • 单个成员对象:先构造成员对象 → 再初始化外部类成员 → 最后执行外部类构造函数体;
    • 多个成员对象:构造顺序 = 其在外部类中的声明顺序(与初始化列表书写顺序无关);
  3. 嵌套场景:需要层层通过初始化列表传递参数,确保每一层的成员对象都能被正确初始化;
  4. 效率建议 :即使成员对象有默认构造,也建议显式在初始化列表中调用(如birthday()),让代码逻辑更清晰,同时避免 "默认构造 + 赋值" 的低效操作。
相关推荐
biter down1 小时前
C++设计一个不能被拷贝的特殊类
开发语言·c++
似水明俊德2 小时前
10-C#
开发语言·windows·c#
似水明俊德2 小时前
14-C#
开发语言·c#
勇敢牛牛_2 小时前
【aiway】基于 Rust 开发的 API + AI 网关
开发语言·后端·网关·ai·rust
khddvbe2 小时前
C++中的代理模式实战
开发语言·c++·算法
计算机安禾2 小时前
【C语言程序设计】第31篇:指针与函数
c语言·开发语言·数据结构·c++·算法·leetcode·visual studio
kaikaile19952 小时前
庞加莱截面计算MATLAB程序
开发语言·matlab
酬勤-人间道2 小时前
自研软件模型处理全流程|个人开发经验分享
c++·经验分享·计算机·计算机图形学·桩号·开挖·回填