[C++]类的参数初始化表

一、为什么要有参数初始化表?

        在前面我们知道,默认的构造函数会对自定义类型的成员调用其默认的构造函来进行初始化,而对基本类型则不做处理(但有些编译器会进行处理),但是,如果自定义类型的类中因为用户显式定义了一个需要传入参数的构造函数而导致无法调用默认的构造函数时,这个时候会发生什么事情呢?来看下面代码。

//没有默认构造函数的Date类
class Date
{
public:
	//显式定义的需要传入参数才能执行的构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

//时间类,其成员有自定义类型日期类
class Time
{
public:
	//默认的构造函数
	Time(int hour = 0, int minute = 0, int second = 0)
	{
		_hour = hour;
		_minute = minute;
		_second = second;
	}

private:
	Date _date;//自定义成员日期类

	//基本类型
	int _hour;
	int _minute;
	int _second;
};

int main()
{
	//创建一个Time类
	Time t1;
	return 0;
}

编译报错:

        因为在创建Time类t1变量时,因为Date类没有默认的构造函数,Time类无法通过其默认的构造函数来调用自定义成员Date类的构造函数,于是就发生了编译错误。解决的第一个方法就是修改Date类,让其具有默认的构造函数,那么有没有不修改Date类的方法呢?那就要使用今天要介绍的参数初始化表了。

二、参数初始化表的概念

        什么是初始化列表呢?上面我们对Date类Time类内的基本成员的初始化是通过构造函数的函数体内通过赋值语句来实现的。C++提供了一种初始化数据成员的方法——参数初始化表来实现对数据成员的初始化。这种方法不在函数体内对数据成员初始化,而是在函数首部实现。来看如下使用参数初始化表对上面声明的Date类Time类的数据成员进行初始化的代码:

//没有默认构造函数的Date类
class Date
{
public:
	//显式定义的需要传入参数才能执行的构造函数
	//使用初始化列表对Date类的数据成员进行初始化
	Date(int year, int month, int day)
    :_year(year),
    _month(month),
    _day(day) 
	{;}//空函数体

	//打印日期和时间
	void Display()
	{
		cout << _year << " 年 " << _month << " 月 " << _day << " 日 ";
	}

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

//时间类,其成员有自定义类型日期类
class Time
{
public:
	//默认的构造函数
	Time(int hour = 0, int minute = 0, int second = 0)
    :_hour(hour),
    _minute(minute),
    _second(second),
    _date(2024,4,19)//定义_date时传入参数来调用构造函数
	{;}//空函数体

	void Display()//打印出日期和时间
	{
		_date.Display();
		printf("%02d 时 %02d 分 %02d 秒\n", _hour, _minute, _second);
	}

private:
	Date _date;//自定义成员日期类

	//基本类型
	int _hour;
	int _minute;
	int _second;
};

int main()
{
	//创建一个Time类
	Time t1(21,19,3);
	t1.Display();
	return 0;
}

运行结果:

其关键语句为

表示用形参year,month,day分别对_year,_month,_day进行初始化 。我们可以看到,参数初始化化表的形式为在原来函数的首部的末尾加一个冒号,然后列出各成员的初始化表,括号里的参数代表用该参数对该成员进行初始化。括号里可以是表达式,进行函数调用,如对整形指针ptr的初始化可以写成ptr((int*)malloc(...))

带有参数初始化表的构造函数的一般形式如下:

类名::构造函数名([参数表])[:成员初始化表]

{

        [构造函数体]

}

其中方括号里的内容可有可无。如果要初始化的成员为数组,则不能在参数初始化表对其初始化,而应该在我们的构造函数体用用户自定义的方法对其初始化。

初始化列表本质可以理解为每个对象中成员定义的地方。

        我们通过用参数初始化表定义自定义类型_date类时传入指定的参数就可以调用用户显式定义的构造函数来进行初始化了。

三、参数初始化表的使用及细节

(一)需要使用参数初始化表的情况

        在一般情况下所有的类成员,可以在函数体内用赋值的方法进行初始化,也可以用参数初始化表的方法进行初始化,但是以下几种成员变量必须使用参数初始化表来进行初始化,因为其类型的变量需要在声明定义时初始化

1、引用

        因为引用是变量的别名,声明引用时必须指明是哪个变量的引用,所以需要使用参数初始化表给出其引用的实参。

//使用参数初始化表的情况
//引用
class Test1
{
public:
	//使用参数初始化表对引用进行初始化
	Test1(int& n) :ref1(n){ ; }//相当于 int& ref1 = n;
	void Set(int n)//用n修改引用的值
	{
		ref1 = n;
	}
private:
	int& ref1;//int类型的 引用
};

int main()
{
	int temp1 = 0;
	cout << "temp1 = " << temp1 << endl;//输入temp1的值
	Test1 t1(temp1);//实参temp1作为ref1的引用对象
	t1.Set(10);//将引用ref1赋值为10
	cout << "temp1 = " << temp1;//再次输出,temp1的值被修改为了10
	return 0;
}

运行结果:

2、const修饰的变量

        const修饰的变量在声明定义时初始化后不能够再被修改,需要在声明时就进行定义初始化,所以需要使用参数初始化表来初始化其值。

//用参数初始化表来初始化const修饰的对象
class Test2
{
public:
    //用n初始化_num变量 
	Test2(int n) :_num(n) { ; }//相当于 const int _num = n;
	const int _num;//const修饰的变量
};

int main()
{
	Test2 t1(10);
	cout << "t1._num = " << t1._num << endl;
	return 0;
}

运行结果:

3、没有默认构造函数的自定义类型

        这个情况在上面已经做过讨论,当自定义类型没有默认的构造函数可以调用时,则需要我们在初始化列表为这个自定义类型定义时传入参数来调用构造函数进行初始化

(二)默认的参数初始化表

        C++程序员普遍喜欢使用参数初始化表的方法对所有数据成员进行初始化,所以我们应该尽量用参数初始化表对成员进行初始化,但如果当我们不写参数初始化表时会怎么样呢?

        我们不写参数初始化表,也会有一个默认的参数初始化表,对于自定义类型,初始化表会调用其对应的默认构造函数对其自定义类型的成员进行初始化,然后再看基本类型成员是否有缺省值,如果有缺省值,则用缺省值在参数初始化表对其进行初始化,剩余没有缺省值的基本类型的变量是否做处理要看编译器。构造函数的执行顺序是先执行参数初始化表,后执行函数体的操作

class A
{
public:
	A():_a(0) { ; }//默认构造函数

	int _a;
};

class Test3
{
public:
	Test3() { ; }//当不写参数初始化表时,编译器会自动执行一次参数初始化表
	//这里就相当于 Test3():_num1(5),_a1() { ; }


	//基本类型
	int _num1 = 5;//给成员变量赋缺省值5
	int _num2;//没有缺省值,是否处理具体看编译器

	//自定义类型
	A _a1;
};

int main()
{
	Test3 t1;
	cout << "t1._num1 = " << t1._num1 << endl;
	cout << "t1._num2 = " << t1._num2 << endl;
	cout << "t1._a1._a = " << t1._a1._a << endl;
	return 0;
}

运行结果: 

(三)参数初始化表的初始化顺序

        我们来看如下代码:

class Test4
{
public:
    //这里是否为先用n初始化_b,然后再用_b初始化_a 
    //所以_a和_b都被初始化为了n呢?
	Test4(int n):_b(n),_a(_b) {; } 

	//打印_a 和 _b 的值
	void Display()
	{
		cout << "_a = " << _a << endl;
		cout << "_b = " << _b << endl;
	}
private:
	//基本类型
	int _a;
	int _b;
};

int main()
{
	Test4 t1(5);//传入5调用其构造函数
	t1.Display();//显示_a 和 _b 的值
	return 0;
}

        参数初始化列表里是先写的用n_b变量进行初始化,再用_b_a进行初始化,那么输出结果是不是_a = 5,_b = 5呢?

运行结果: 

        我们运行发现,其结果并不是我们预期的结果,这是为什么呢?

        这是因为:成员变量在声明时的次序就是其在参数初始化表中初始化的顺序,与变量在参数初始化表中的次序无关。

        

         

  • 14
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值