定义
在 C++ 中,初始化列表是一种用于在对象创建时初始化其成员变量的语法机制。它主要用在类的构造函数中,位于构造函数的参数列表之后,函数体之前,以一个冒号:开始,后面跟着一系列以逗号分隔的成员变量初始化语句。
例如:
cpp
class MyClass {
private:
int num;
double value;
public:
MyClass(int n, double v) : num(n), value(v) {
}
};
在上述MyClass类的构造函数MyClass(int n, double v)中,num(n), value(v)就是初始化列表。它用于初始化num和value这两个成员变量。
作用
效率提升
对于某些数据类型,如 const 成员变量和引用成员变量,它们必须在初始化的时候被赋值,不能先定义再赋值。使用初始化列表可以确保这些成员变量在对象创建的第一时间就被正确初始化。
例如:
cpp
class ConstRefClass {
private:
const int constNum;
int& refNum;
public:
ConstRefClass(int n, int& ref) : constNum(n), refNum(ref) {
}
};
在这个ConstRefClass类中,constNum是一个 const 成员变量,refNum是一个引用成员变量。通过初始化列表,可以在构造函数调用时就为它们赋值。如果不使用初始化列表,而是在构造函数体中赋值,对于 const 成员变量会导致编译错误,因为 const 变量一旦定义就不能被修改;对于引用成员变量,也不符合引用必须在定义时初始化的规则。
避免多余的默认构造和赋值操作
当类中包含其他类对象作为成员变量时,如果这些成员变量所属的类有自己的构造函数,使用初始化列表可以直接调用合适的构造函数来初始化成员变量,避免先调用默认构造函数创建对象,再通过赋值操作来给成员变量赋值。
例如,假设有一个Point类和一个Rectangle类,Rectangle类包含两个Point类对象作为其左上角和右下角的坐标。
在Rectangle类的构造函数中,通过初始化列表直接调用Point类的构造函数来初始化topLeft和bottomRight,而不是先默认构造Point对象,再通过赋值操作来设置坐标,这样可以提高效率,特别是当Point类的构造函数中有一些复杂的初始化操作时。
初始化列表和在构造函数体内赋值有什么区别?
语法形式
初始化列表:
位于构造函数的参数列表之后,函数体之前,以冒号:开头,后面跟着成员变量的初始化列表,形式为 "成员变量 (初始值)",多个成员变量之间用逗号分隔。例如:
cpp
class MyClass {
private:
int num;
double value;
public:
MyClass(int n, double v) : num(n), value(v) {
}
};
构造函数体内赋值:
在构造函数的函数体内部,通过赋值语句来给成员变量赋值。例如:
cpp
class MyClass {
private:
int num;
double value;
public:
MyClass(int n, double v) {
num = n;
value = v;
}
};
初始化时机
初始化列表:
成员变量的初始化是在对象的存储空间被分配之后,构造函数体执行之前完成的。对于 const 成员变量和引用成员变量,初始化列表是初始化它们的唯一合法方式,因为这些类型的变量必须在定义时就初始化。例如:
cpp
class ConstRefExample {
private:
const int constValue;
int& refValue;
public:
ConstRefExample(int n, int& ref) : constValue(n), refValue(ref) {
}
};
构造函数体内赋值:
成员变量的赋值是在构造函数体执行时进行的,此时成员变量已经被初始化(如果没有在初始化列表中初始化,会先调用默认构造函数进行初始化),然后再进行赋值操作。这意味着对于一些不能被重新赋值的类型(如 const 成员变量),在构造函数体内赋值是不合法的。
效率差异
初始化列表:
在某些情况下效率更高。特别是当类中有其他类对象作为成员变量时,如果使用初始化列表,会直接调用成员变量所属类的构造函数进行初始化。这样可以避免先调用默认构造函数创建对象,然后再通过赋值操作来设置值,减少了一次不必要的构造和赋值过程。例如:
cpp
class InnerClass {
public:
InnerClass() {
// 假设这里有一些复杂的初始化操作
}
InnerClass(int value) {
// 另一种构造函数,假设也有复杂操作
}
};
class OuterClass {
private:
InnerClass inner;
public:
OuterClass() : inner(10) {
}
};
在OuterClass的构造函数中,通过初始化列表调用InnerClass的有参数构造函数InnerClass(int value)直接初始化inner。如果不使用初始化列表,而是在构造函数体内赋值,就会先调用InnerClass的默认构造函数,然后再执行赋值操作,这可能会涉及到先构造一个临时对象,再进行赋值,然后销毁临时对象等额外的操作。
构造函数体内赋值:
相对来说效率可能较低,因为可能会涉及到先默认构造成员变量,然后再进行赋值的过程。但对于简单的数据类型(如基本数据类型),这种效率差异通常可以忽略不计。例如,对于int类型的成员变量,在初始化列表初始化和在构造函数体内赋值的性能差异几乎没有。