在 C++ 中,构造函数后面的冒号 :
引出的部分就是 初始化列表。它用于在构造函数体执行之前初始化类的成员变量。使用初始化列表有以下几个主要好处:
1. 提高效率,避免默认构造再赋值
如果不使用初始化列表,编译器会首先使用默认构造函数来构造成员变量,然后在构造函数体内进行赋值操作。这种做法可能导致不必要的操作,特别是在成员变量类型比较复杂时,比如自定义类型或者引用类型等。初始化列表直接在成员变量构造时就进行初始化,避免了重复构造和赋值的开销。
例如:
class MyClass {
private:
std::string name;
int age;
public:
MyClass(const std::string& name, int age) {
this->name = name; // 先默认构造,再赋值
this->age = age;
}
};
如果改用初始化列表:
class MyClass {
private:
std::string name;
int age;
public:
MyClass(const std::string& name, int age) : name(name), age(age) // 直接初始化
{}
};
在这种情况下,name
和 age
会直接通过初始化列表进行初始化,而不是先默认构造再赋值。
2. 初始化常量成员
C++ 中的常量成员变量(const
)必须在构造函数的初始化列表中初始化,而不能在构造函数体内进行赋值。如果没有在初始化列表中初始化常量成员,编译器会报错。
例如:
class MyClass {
private:
const int id;
public:
MyClass(int id) : id(id) {} // 必须在初始化列表中初始化常量成员
};
如果将常量成员初始化放到构造函数体内,会导致编译错误:
class MyClass {
private:
const int id;
public:
MyClass(int id) {
this->id = id; // 错误,const 成员不能被赋值
}
};
3. 初始化引用成员
引用类型成员变量必须通过初始化列表进行初始化,因为引用在 C++ 中必须在构造时绑定到一个对象,而不能通过赋值来重新绑定。因此,引用成员必须在初始化列表中初始化。
例如:
class MyClass {
private:
int& ref;
public:
MyClass(int& ref) : ref(ref) {} // 必须在初始化列表中初始化引用成员
};
如果尝试在构造函数体内给引用成员赋值,会导致编译错误:
class MyClass {
private:
int& ref;
public:
MyClass(int& ref) {
this->ref = ref; // 错误,引用成员不能重新绑定
}
};
4. 对基类的初始化
当一个类有基类时,基类的构造函数也需要在初始化列表中显式调用,否则基类的默认构造函数会在构造函数体执行前被调用。如果基类没有默认构造函数,编译器会要求显式调用基类的构造函数。
例如:
class Base {
public:
Base(int x) {
// 基类的构造函数
}
};
class Derived : public Base {
public:
Derived(int x, int y) : Base(x) {
// 子类构造函数
}
};
如果没有显式在初始化列表中调用 Base(x)
,编译器将尝试调用基类的默认构造函数(如果有的话),如果没有默认构造函数,会导致编译错误。
5. 初始化复杂类型(如 STL 容器)
STL 容器类、智能指针等较为复杂的类型最好在初始化列表中进行初始化,因为这些类型的成员往往会涉及到内存分配或其他初始化操作,使用初始化列表比在构造函数体内初始化更加高效。
例如:
#include <vector>
class MyClass {
private:
std::vector<int> data;
public:
MyClass(const std::vector<int>& init_data) : data(init_data) {} // 高效初始化
};
如果不使用初始化列表,可能需要先构造一个空的 vector
,然后再将传入的值赋值给它。
特别注意:初始化顺序
成员变量的初始化顺序是按照它们在类定义中声明的顺序,而不是在初始化列表中的顺序。因此,使用初始化列表时,可以清楚地控制成员变量的初始化顺序,避免意外的初始化顺序错误。
class MyClass {
private:
int a;
int b;
public:
MyClass(int x, int y) : b(y), a(x) {} // a 会先初始化,b 后初始化
};
即使 b
在初始化列表中排在 a
之前,a
依然会在 b
之前被初始化,因为成员变量的初始化顺序由它们在类中声明的顺序决定。
总结
使用 初始化列表 在 C++ 中有很多好处,尤其是在性能和语义上。它能够提高效率,减少不必要的默认构造与赋值操作;同时,它是初始化常量、引用成员以及基类成员的唯一方式;并且它还能保证成员变量按照声明的顺序进行初始化。通过合理使用初始化列表,能够编写出更高效、更安全、更可维护的代码。