什么是 C++ 中的初始化列表?它的作用是什么?初始化列表和在构造函数体内赋值有什么区别?

时间:2024-11-23 13:57:22

在这个ConstRefClass类中,constNum是一个 const 成员变量,refNum是一个引用成员变量。通过初始化列表,可以在构造函数调用时就为它们赋值。如果不使用初始化列表,而是在构造函数体中赋值,对于 const 成员变量会导致编译错误,因为 const 变量一旦定义就不能被修改;对于引用成员变量,也不符合引用必须在定义时初始化的规则。

避免多余的默认构造和赋值操作

当类中包含其他类对象作为成员变量时,如果这些成员变量所属的类有自己的构造函数,使用初始化列表可以直接调用合适的构造函数来初始化成员变量,避免先调用默认构造函数创建对象,再通过赋值操作来给成员变量赋值。

例如,假设有一个Point类和一个Rectangle类,Rectangle类包含两个Point类对象作为其左上角和右下角的坐标。

在Rectangle类的构造函数中,通过初始化列表直接调用Point类的构造函数来初始化topLeft和bottomRight,而不是先默认构造Point对象,再通过赋值操作来设置坐标,这样可以提高效率,特别是当Point类的构造函数中有一些复杂的初始化操作时。

初始化列表和在构造函数体内赋值有什么区别?

语法形式

初始化列表:

位于构造函数的参数列表之后,函数体之前,以冒号:开头,后面跟着成员变量的初始化列表,形式为 “成员变量 (初始值)”,多个成员变量之间用逗号分隔。例如:

class MyClass {
private:
    int num;
    double value;
public:
    MyClass(int n, double v) : num(n), value(v) {
    }
};

构造函数体内赋值:

在构造函数的函数体内部,通过赋值语句来给成员变量赋值。例如:

class MyClass {
private:
    int num;
    double value;
public:
    MyClass(int n, double v) {
        num = n;
        value = v;
    }
};

初始化时机

初始化列表:

成员变量的初始化是在对象的存储空间被分配之后,构造函数体执行之前完成的。对于 const 成员变量和引用成员变量,初始化列表是初始化它们的唯一合法方式,因为这些类型的变量必须在定义时就初始化。例如:

class ConstRefExample {
private:
    const int constValue;
    int& refValue;
public:
    ConstRefExample(int n, int& ref) : constValue(n), refValue(ref) {
    }
};

构造函数体内赋值:

成员变量的赋值是在构造函数体执行时进行的,此时成员变量已经被初始化(如果没有在初始化列表中初始化,会先调用默认构造函数进行初始化),然后再进行赋值操作。这意味着对于一些不能被重新赋值的类型(如 const 成员变量),在构造函数体内赋值是不合法的。

效率差异

初始化列表:

在某些情况下效率更高。特别是当类中有其他类对象作为成员变量时,如果使用初始化列表,会直接调用成员变量所属类的构造函数进行初始化。这样可以避免先调用默认构造函数创建对象,然后再通过赋值操作来设置值,减少了一次不必要的构造和赋值过程。例如:

class InnerClass {
public:
    InnerClass() {
        // 假设这里有一些复杂的初始化操作
    }
    InnerClass(int value) {
        // 另一种构造函数,假设也有复杂操作
    }
};
class OuterClass {
private:
    InnerClass inner;
public:
    OuterClass() : inner(10) {
    }
};

在OuterClass的构造函数中,通过初始化列表调用InnerClass的有参数构造函数InnerClass(int value)直接初始化inner。如果不使用初始化列表,而是在构造函数体内赋值,就会先调用InnerClass的默认构造函数,然后再执行赋值操作,这可能会涉及到先构造一个临时对象,再进行赋值,然后销毁临时对象等额外的操作。

构造函数体内赋值:

相对来说效率可能较低,因为可能会涉及到先默认构造成员变量,然后再进行赋值的过程。但对于简单的数据类型(如基本数据类型),这种效率差异通常可以忽略不计。例如,对于int类型的成员变量,在初始化列表初始化和在构造函数体内赋值的性能差异几乎没有。