再谈构造函数
构造函数体赋值
在创建对象时,编译器会通过调用构造函数,给对象中的各个成员变量一个合适的初始值:
class Date
{
public:
// 构造函数
Date(int year = 0, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
需要注意的是:虽然通过调用上述的构造函数后,对象中的每个成员变量都有了一个初始值,但是构造函数中的语句只能将其称作为赋初值,而不能称作为初始化。因为初始化只能初始化一次,而构造函数体内可以进行多次赋值。
class Date
{
public:
// 构造函数
Date(int year = 0, int month = 1, int day = 1)
{
_year = year;// 第一次赋值
_year = 2022;// 第二次赋值
//...
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
初始化列表
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或表达式。
class Date
{
public:
// 构造函数
Date(int year = 0, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
注意事项:
一、每个成员变量在初始化列表中只能出现一次
因为初始化只能进行一次,所以同一个成员变量在初始化列表中不能多次出现。
二、类中包含以下成员,必须放在初始化列表进行初始化:
二、类中包含以下成员,必须放在初始化列表进行初始化:
1.引用成员变量
引用类型的变量在定义时就必须给其一个初始值,所以引用成员变量必须使用初始化列表对其进行初始化。
int a = 10;
int& b = a;// 创建时就初始化
2.const成员变量
被const修饰的变量也必须在定义时就给其一个初始值,也必须使用初始化列表进行初始化。
const int a = 10;//correct 创建时就初始化
const int b;//error 创建时未初始化
3.自定义类型成员(该类没有默认构造函数)
若一个类没有默认构造函数,那么我们在实例化该类对象时就需要传参对其进行初始化,所以实例化没有默认构造函数的类对象时必须使用初始化列表对其进行初始化。
在这里再声明一下,默认构造函数是指不用传参就可以调用的构造函数:
1.我们不写,编译器自动生成的构造函数。
2.无参的构造函数。
3.全缺省的构造函数。
class A //该类没有默认构造函数
{
public:
A(int val) //注:这个不叫默认构造函数(需要传参调用)
{
_val = val;
}
private:
int _val;
};
class B
{
public:
B()
:_a(2021) //必须使用初始化列表对其进行初始化
{}
private:
A _a; //自定义类型成员(该类没有默认构造函数)
};
总结一下:在定义时就必须进行初始化的变量类型,就必须放在初始化列表进行初始化。
三、尽量使用初始化列表初始化
因为初始化列表实际上就是当你实例化一个对象时,该对象的成员变量定义的地方,所以无论你是否使用初始化列表,都会走这么一个过程(成员变量需要定义出来)。
严格来说:
1.对于内置类型,使用初始化列表和在构造函数体内进行初始化实际上是没有差别的,其差别就类似于如下代码:
// 使用初始化列表
int a = 10
// 在构造函数体内初始化(不使用初始化列表)
int a;
a = 10;