首页 > 编程笔记

C++构造函数的初始化列表

在 C++ 中,构造函数负责初始化新创建的对象。然而,除了构造函数体中的赋值操作,C++ 还提供了一种更高效、更精确的方式来初始化成员变量,叫做初始化列表。

初始化列表紧跟在构造函数参数列表的后面,以一个冒号开头,然后是成员变量及其初始值,初始值用圆括号包裹着。例如,MyClass 类的构造函数以初始化列表的方式为各个成员变量赋初值:
class MyClass {
public:
    MyClass(int a, double b) : m_a(a), m_b(b) {}  // 初始化列表
private:
    int m_a;
    double m_b;
};
在 C++ 中,构造函数的初始化列表是非常有用的,这点让初学者非常疑惑,因为下面的语句也能达到同样的目的,且更加直观:
MyClass(int a, double b){
    m_a = a;
    m_b = b;  
}
上面两种方法是有区别的,初始化列表的方式对每个变量只有一次初始化,而后一种方法则先用默认值初始化,然后再赋相应的值,也就是说在进入构造函数之前已经完成了初始化。因此,使用初始化列表的效率更高。

搞清楚以上两种初始化方式的区别之后,如果类中有 const 或引用类型的成员变量,必须使用初始化列表来初始化它们,因为这些类型的成员变量在创建后就不能更改。

例如:
class MyClass {
public:
    MyClass(int a, const std::string& s) : m_a(a), m_s(s) {}
private:
    int m_a;
    const std::string m_s;  // 必须使用初始化列表
};

初始化列表的完整实例

下面的例子中定义了一个 Point 类,用来表示在三维坐标系统下的一个点,类的构造函数可以接收 3 个参数,或者忽略 z 坐标,或者不提供初始化参数。
#include <iostream>
using namespace std;

class Point
{
    friend ostream& operator << (ostream & out, const Point & p); //输出函数
public:
    // 3 个参数的构造函数
    Point(int x, int y, int z) :m_x(x), m_y(y), m_z(z) {
        cout << "3 个参数的构造函数被调用" << *this << endl; //输出数据
    }
    // 2个参数的构造函数
    Point(int x, int y) :m_x(x), m_y(y), m_z(0) {
        cout << "2个参数的构造函数被调用" << *this << endl;    //输出数据
    }
    //无参数的构造函数
    Point() :m_x(0), m_y(0), m_z(0) {
        cout << "无参数的构造函数被调用" << *this << endl;
    }
    // 复制构造函数
    Point(const Point& p) :m_x(p.m_x), m_y(p.m_y), m_z(p.m_z) {
        cout << "复制构造函数被调用" << *this << endl; //输出数据
    }
private:
    int m_x, m_y, m_z;
};

//重载输出操作符
ostream& operator<<(ostream& out, const Point& p)
{
    out << "x = " << p.m_x << "y = " << p.m_y << "z = " << p.m_z;
    return out;
}

Point fun(Point p)
{
    return p;
}

int main(){
    Point pl;
    Point p2(10, 10);
    Point p3(10, 10, 10);
    Point p4 = p2;
    fun(p3);
}
执行结果为:

无参数的构造函数被调用x = 0 y = 0 z = 0
2个参数的构造函数被调用x = 10 y = 10 z = 0
3 个参数的构造函数被调用x = 10 y = 10 z = 10
复制构造函数被调用x = 10 y = 10 z = 0
复制构造函数被调用x = 10 y = 10 z = 10
复制构造函数被调用x = 10 y = 10 z = 10

示例程序中,根据提供参数的不同,编译器会选择不同的构造函数。调用 fun() 函数的过程中,会调用两次复制构造函数,一次是在参数传递过程中,一次是在函数返回时。

总结

构造函数的初始化列表是 C++ 中一种强大而灵活的特性,它不仅提高了代码的执行效率,还为成员变量的精确初始化提供了更多控制。

使用初始化列表时,有一些需要注意的地方:

推荐阅读