1. 类成员初始化方式
在 C++ 中,类成员的初始化有两种主要方式:
- 赋值初始化:在构造函数体内部对成员进行赋值。
- 初始化列表:在构造函数定义时,在冒号的后面使用初始化列表进行初始化。
赋值初始化
赋值初始化是在构造函数体内对成员进行赋值。这意味着成员变量在构造函数体内被赋值之前,已经被默认构造了。
初始化列表
初始化列表是在构造函数冒号后对成员进行初始化,这是在成员被分配内存的同时进行的初始化操作。这样避免了成员变量先被默认构造然后再赋值的过程。
#include <iostream>
class MyClass {
public:
int x;
int y;
// 赋值初始化
MyClass(int a, int b) {
x = a; // 赋值初始化
y = b; // 赋值初始化
}
// 初始化列表
MyClass(int a, int b) : x(a), y(b) {} // 初始化列表
};
2. 构造函数的执行顺序
构造函数的执行顺序对于派生类来说是比较规则的。派生类对象的构造顺序遵循以下步骤:
- 虚拟基类的构造函数(如果有多个虚拟基类,则按继承关系中声明的顺序执行它们的构造函数)。
- 普通基类的构造函数(按继承顺序执行)。
- 类类型成员对象的构造函数(按声明顺序执行)。
- 派生类的构造函数(最后执行)。
通过以下例子说明:
#include <iostream>
class BaseA {
public:
BaseA() { std::cout << "BaseA Constructor\n"; }
};
class BaseB {
public:
BaseB() { std::cout << "BaseB Constructor\n"; }
};
class Member {
public:
Member() { std::cout << "Member Constructor\n"; }
};
class Derived : public BaseA, public BaseB {
public:
Member m;
Derived() { std::cout << "Derived Constructor\n"; }
};
int main() {
Derived d;
return 0;
}
以上程序的输出将是:
BaseA Constructor
BaseB Constructor
Member Constructor
Derived Constructor
3. 为什么使用成员初始化列表会更快
使用成员初始化列表比在构造函数体内部赋值会更高效的原因如下:
-
避免了不必要的默认构造和赋值:在构造函数体内部赋值,成员变量先默认构造,然后再赋值。而通过初始化列表,成员在分配内存时就直接初始化,可以避免默认构造和随后的赋值操作。
-
减少临时对象的构造:通过初始化列表,构造函数直接初始化成员,而不需要先产生临时对象然后再赋值给成员。
让我们再举一个成员是对象的例子:
#include <iostream>
#include <string>
class MyObject {
public:
std::string value;
// 使用赋值初始化
MyObject() {
value = "Hello, World!";
std::cout << "MyObject assignment constructor: " << value << std::endl;
}
// 使用初始化列表
MyObject(const std::string& s) : value(s) {
std::cout << "MyObject initializer list constructor: " << value << std::endl;
}
};
int main() {
MyObject obj1;
MyObject obj2("Hello, C++!");
return 0;
}
运行结果:
MyObject assignment constructor: Hello, World!
MyObject initializer list constructor: Hello, C++!
为了提高效率,尽量使用成员初始化列表来初始化类成员。
小结
以上总结了类成员初始化的两种方式、构造函数执行顺序及其原因,并通过代码举例说明。希望这些解释和例子能帮助你更好地理解和应用这些 C++ 特性。