初始化列表
在对象的构造过程中直接对成员变量进行初始化,而不是在构造函数的主体中赋值。
【为类的成员变量初始化提供的机制。通过初始化列表,我们可以在构造函数的函数体执行前,直接为类的成员变量赋初值】
初始化列表通常在构造函数参数列表的后面,通过冒号 : 引入
语法:构造函数():属性1(值1),属性2(值2)... {}
class 类名 {
public:类名(参数列表) : 成员1(初始值1), 成员2(初始值2), ... { // 构造函数体(可以执行其他操作)}
};
- 初始化列表:直接在成员变量定义时进行初始化。
- 构造函数体:先调用默认构造函数初始化成员变量,再通过赋值操作改变成员变量的值
初始化列表的初始化顺序
初始化顺序遵循成员变量在类中声明的顺序,而不是初始化列表中的顺序。
class Example {
private:int a;int b;
public:Example() : b(1), a(b) {} // 错误,a 的初始化不能依赖 b
};class Example {
private:int a;int b;
public:Example() : a(1), b(a) {} // 正确,b 可以依赖 a
};
- 初始化列表只能用于构造函数,不能用于其他成员函数。
- 成员变量的初始化顺序与类声明中的顺序一致,和列表中顺序无关
类的组合:类对象作为类成员
C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员
class A {}; // 定义一个空类 A
class B {
A a; // 在类 B 中定义 A 类型的成员变量 a
};
B类中有对象A作为成员,A为对象成员
构造顺序:
成员对象的构造顺序先于类本身的构造
在创建 B 对象时,B 的成员变量(也就是 A 类型的对象 a)会首先被构造,然后才会执行 B 的构造函数。
构造顺序:先构造成员 A,再构造 B。
析构顺序:先析构 B,再析构成员 A
类名::类名(对象成员所需的形参,本类成员形参): 对象1(参数), 对象2(参数), ...
{// 构造函数体
}
构造组合类对象的初始化次序:
- 按照类体中声明的顺序对成员进行初始化,而不是初始化列表的顺序;
- 初始化列表只是提供初始化方式,次序仍由声明顺序决定。
- 类的成员对象(即另一个类的对象作为当前类的成员)的构造函数调用顺序是按照它们在类中声明的先后顺序,而不是在初始化列表中的顺序
class Example {
private:
int a; // 声明在前
int b; // 声明在后
public:
Example() : b(20), a(10) { // 初始化列表中 b 在前,a 在后
// 函数体
}
};
初始化顺序是 a -> b
类成员对象的构造顺序
类的成员对象(对象成员+基本类型成员)构造顺序为:
- 按照成员变量在类中声明的顺序构造。
- 初始化列表的顺序仅决定成员的初始化方式,不影响成员的构造顺序。
- 未出现在初始化列表中的对象成员会调用默认构造函数。
#include <iostream>
using namespace std;class SubObject {
public:SubObject(int id) { cout << "SubObject " << id << " constructed" << endl; }
};class Parent {
private:SubObject obj1; // 声明在前SubObject obj2; // 声明在后int value;
public:Parent() : obj2(2), obj1(1), value(10) {cout << "Parent constructed with value = " << value << endl;}
};int main() {Parent p;return 0;
}
先声明的成员先构造
多重继承的构造顺序
对于多重继承
- 从左到右按照派生类的基类声明顺序进行构造。
- 然后构造派生类。
继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
虚继承的构造顺序
- 虚基类最先构造(即使多个派生类中包含该虚基类,也只构造一次)。
- 非虚基类按照声明顺序构造。
- 最后是派生类。
多继承且有内嵌对象时的构造函数
多继承+内嵌对象时构造顺序为:
- 虚基类先构造(只构造一次,按照继承层次从顶层虚基类开始)。
- 非虚基类按声明顺序构造(左到右,从基类的声明顺序决定)。
- 类成员对象按其声明顺序构造。
- 派生类的构造函数体最后执行。
#include <iostream>
using namespace std;// 基类Base1,构造函数有一个参数
class Base1 {
public:Base1(int i) { cout << "Constructing Base1 " << i << endl; }
};// 基类Base2,构造函数有一个参数
class Base2 {
public:Base2(int j) { cout << "Constructing Base2 " << j << endl; }
};// 基类Base3,构造函数没有参数
class Base3 {
public:Base3() { cout << "Constructing Base3 *" << endl; }
};// 派生类Derived,继承自Base1、Base2和Base3
class Derived : public Base2, public Base1, public Base3 {
public:// 派生类的构造函数,使用初始化列表初始化基类Derived(int a, int b, int c, int d) : Base1(a), // 初始化Base1member2(d), // 初始化成员对象member2member1(c), // 初始化成员对象member1Base2(b) // 初始化Base2{ }private:Base1 member1; // 成员对象member1,类型为Base1Base2 member2; // 成员对象member2,类型为Base2Base3 member3; // 成员对象member3,类型为Base3
};int main() {Derived obj(1, 2, 3, 4); // 创建Derived类的对象obj,并传递参数return 0;
}
-
基类构造函数的调用顺序:
-
在多重继承中,基类的构造函数按照声明的顺序被调用,而不是在派生类的初始化列表中的顺序。
-
声明顺序:在
Derived
类中,基类是按照Base2
、Base1
、Base3
的顺序声明的,因此构造函数的调用顺序是:Base2
->Base1
->Base3
。
-
-
派生类构造函数:
-
派生类
Derived
的构造函数接受四个参数,并通过初始化列表将这些参数传递给基类构造函数。 -
Base1
和Base2
通过初始化列表进行初始化,member2
和member1
也是通过初始化列表进行初始化。 -
注意,
Base3
没有构造函数参数,它使用默认构造函数进行初始化。
-
-
成员对象的初始化顺序:
-
成员对象的初始化顺序是按它们在类中声明的顺序,而不是在初始化列表中的顺序。所以,
member1
会在member2
之前初始化。
-