C++初始化列表

初始化列表

构造函数不仅可以有名字,参数列表,函数体,还可以有初始化列表,初始化列表以一个冒号开头,接着是以逗号分隔的数据成员列表,如下:

class Date {
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

private:
	int _year;
	int _month;
	int _day;
};

使用初始化列表的构造函数是显示的进行初始化,而使用内部赋值的方式进行的初始化并不是显示的,并且使用内部赋值的方式进行的初始化并不能真正的称之为初始化,只能说是伪初始化,因为真正的初始化只能进行一次,而内部赋值可以进行多次。

使用初始化列表和内部赋值的构造函数对内置类型来说并没有较大区别,但对于自定义类型的成员变量来说,推荐使用初始化列表初始化。

必须使用初始化列表的情况

1. 自定义类型成员(且该类没有默认的构造函数)

当类的成员对象是其他类的时候,如果作为成员的类没有默认构造函数,那么它必须要进行显示初始化,因为编译器会隐式的调用成员类型的默认构造函数,而因为已经定义了构造函数,所以编译器不会自动生成默认构造函数,则编译器尝试使用默认构造函数将会失败。

#include <iostream>
using namespace std;

class Time {
public:
	Time(int hour)
		:_hour(hour) //错误	C2512	“Time” : 没有合适的默认构造函数可用
	{}
private:
	int _hour;
};


class Date {
public:
	Date(int year = 1900, int month = 1, int day =1)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

private:
	int _year;
	int _month;
	int _day;

	Time _t;
};

int main() {
	Date d;
}

Date类数据成员中有Time类对象_t,创建Date类对象时,要先创建Time类;而Time类中已经有了一个参数的构造函数,所以编译器不会再进行默认无参的构造函数,因此_t无法创建。

想要正确的进行初始化,则要通过显示的方式进行,因此Date类的构造函数应该写成:

class Date {
public:
	Date(int n = 10,int year = 1900, int month = 1, int day = 1)
		:_t(n)
		,_year(year)
		, _month(month)
		, _day(day)
	{}

private:
	int _year;
	int _month;
	int _day;

	Time _t;
};

还有一个问题:当Date(int year = 1900, int n = 10, int month = 1, int day = 1)的顺序是这样时,调试的时候为什么一开始hour是1900,之后才是10?

答:在这段代码中,由于 Date 类中有一个 Time 类型的成员变量 _t,而 Time 类没有默认构造函数,因此编译器会尝试使用 Time 类的构造函数进行初始化。在 Date 类的构造函数中,有这样一行代码::_t(n),这行代码的作用是初始化 Date 类的成员变量 _t,但是因为 Time 类没有默认构造函数,编译器无法使用默认构造函数来初始化 _t。所以,编译器会尝试使用 Time(int hour) 构造函数来初始化 _t。在 Date 类的构造函数中,_t 的初始化是在_year(year) 之后的,因此在构造 Time 对象时,会先使用 year 的值来初始化 hour,然后才会使用 n 的值。所以 _thour 最初是 year 的值,也就是 1900,之后才被 n 的值 10 所覆盖。如果希望 Time 对象的 hour 最初是 10,可以调整代码顺序,将 _t 的初始化放在 _year(year) 之前,或者修改 Time 类的构造函数,使其接受一个默认的 hour 值。

2. const成员变量

当成员变量中有const类型的时候,会发现编译的时候会出错

class Date {
public:
	Date(int year = 1900, int month = 1, int day = 1, int n = 20) {
		_year = year;
		_month = month;
		_day = day;

		_n = n; //错误(活动)	E0137	表达式必须是可修改的左值

	}

private:
	int _year;
	int _month;
	int _day;
	const int _n;
};

当一个类型被const修饰的时候,它就必须要求你在声明的时候就给它进行初始化,例如:const int n = 10; 你并不能将它写成这样 const int n; n = 10;这也就说明了在上述Date类中,_n = n;是一个拷贝赋值,而并不是初始化,因此必须使用初始化列表

3. 引用成员变量

引用成员变量与const类似,也是在声明时就必须初始化,而且一旦被初始化就不能再指向其他对象,因此引用成员变量也必须使用初始化列表

初始化顺序问题

以下代码为什么_year是1,_month是1900

class Date {
public:
	Date(int year = 1900, int month = 1)
		: _year(year)
		, _month(month)
	{}

private:
	int _month;
	int _year;

};

以上表明了成员变量在类中声明次序就是在初始化列表的初始化顺序,与其在初始化列表中的先后顺序无关。

总结

  1. 构造函数体中的语句只能将其称之为赋初值,而不能称作初始化。
  2. 初始化只能进行一次。
  3. 成员变量在类中声明次序就是在初始化列表的初始化顺序,与其在初始化列表中的先后顺序无关。
  4. 类中包含引用,const,自定义类型的成员变量时,必须在初始化列表进行初始化。
posted @ 2024-04-20 00:38  羡鱼OvO  阅读(38)  评论(0)    收藏  举报