引言
在C++中,类的成员变量初始化是保证对象正确构建的关键。本文将深入探讨C++类成员变量初始化,重点关注初始化列表的作用、使用场景以及与构造函数体赋值的区别。
为什么需要初始化列表?
初始化列表是C++构造函数中的一种语法,用于在构造函数开始执行之前初始化成员变量。它通常出现在构造函数的参数列表之后,冒号之后。为什么需要初始化列表呢?
- 引用成员: 引用必须在声明时初始化,且不能被重新赋值。因此,引用类型的成员变量只能在初始化列表中初始化。
- const成员: const成员变量的值在编译期就已经确定,因此也必须在初始化列表中初始化。
- 自定义类型成员没有默认构造函数: 如果自定义类型的成员变量没有默认构造函数,则必须在初始化列表中提供初始化参数,以调用该成员类型的构造函数。
- 效率: 在初始化列表中初始化成员变量,通常比在构造函数体中赋值的效率更高,因为初始化列表中的初始化操作是在构造函数体执行之前进行的,避免了不必要的默认构造和赋值操作。
初始化列表的使用
class Data{
public:
Data(int year = 0, int month = 1, int day = 1)
: _year(year), _month(month), _day(day) {} // 初始化列表
private:
int _year;
int _month;
int _day;
};
在上面的用法中,我们使用初始化列表初始化_year,_month和_day。当然这个通过普通赋值也可以实现,接下来让我们看不可以通过普通赋值实现的。例如,常量成员、引用成员和没有默认构造函数的成员都必须使用初始化列表。
示例:常量成员变量
class Data {
public:
Data(int year = 0, int month = 1, int day = 1)
: _year(year), _month(month), _day(day), _id(100) {} // 初始化列表初始化 const 成员
private:
const int _id; // 常量成员必须通过初始化列表进行初始化
int _year;
int _month;
int _day;
};
在这个例子中,_id
是一个常量成员。由于常量成员在对象构造后不能被修改,必须通过初始化列表赋值。在构造函数体内进行赋值将会导致编译错误。
示例:引用成员变量
class Data {
public:
Data(int& ref, int year = 0, int month = 1, int day = 1)
: _ref(ref), _year(year), _month(month), _day(day) {} // 初始化列表初始化引用成员
private:
int& _ref; // 引用成员必须通过初始化列表进行初始化
int _year;
int _month;
int _day;
};
在这个例子中,_ref
是一个引用。引用必须在对象构造时进行初始化,不能像普通变量一样通过赋值改变。
示例:没有默认构造函数的成员变量
class AnotherClass {
public:
AnotherClass(int val) : _val(val) {}
private:
int _val;
};
class Data {
public:
Data(int year = 0, int month = 1, int day = 1)
: _year(year), _month(month), _day(day), _other(year) {} // 初始化列表初始化没有默认构造函数的成员
private:
int _year;
int _month;
int _day;
AnotherClass _other; // AnotherClass 没有默认构造函数,因此必须使用初始化列表
};
这里的 AnotherClass
没有默认构造函数,因此成员 _other
必须在初始化列表中提供参数进行初始化。如果试图在构造函数体内进行赋值,将会导致编译错误。
总结来说,初始化列表在以下情况下是必须的:
- 常量成员 (
const
) - 引用成员 (
&
) - 没有默认构造函数的类成员
初始化列表与构造函数体赋值的区别
- 执行顺序: 初始化列表中的初始化操作是在构造函数体执行之前进行的。
- 效率: 初始化列表通常比构造函数体赋值的效率更高。
- 引用和const成员: 引用和const成员只能在初始化列表中初始化。
初始化顺序
- 初始化顺序与初始化列表无关,与成员变量在类中的声明顺序有关。
- 成员变量的声明顺序决定了初始化的顺序。