构造函数体内赋值
在创建对象的时候,编译器通过构造函数体内赋值给对象中各个成员变量进行赋值,注意:这不是初始化!
因为赋值可以多次,但是初始化只能一次。
class Date
{
public:
Date(int year = 2023, int month = 9, int day = 21) //构造函数
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
上面的代码就是构造函数体内赋值。
初始化列表
在明白构造函数体内赋值之后,现在可以来谈谈初始化列表了。
初始化列表是什么?
class Date
{
public:
Date(int year, int month, int day)
:_year(year)
, _month(month)
, _day(day)
{
}
private:
int _year;
int _month;
int _day;
};
使用初始化列表的原因
已经有了构造函数,为什么还要用初始化列表?
1.对于普通成员变量,使用函数体内赋值和初始化列表都是一样的,例如:
class Date
{
public:
Date(int year, int month, int day)
:_year(year)
, _month(month)
, _day(day)
{
}
private:
int _year;
int _month;
int _day;
};
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
2.对于:引用成员变量、const成员变量、没有默认构造函数的自定义类型成员,必须使用初始化列表。
创建对象:对象整体定义
初始化列表:对象成员定义
在创建对象的时候,对象整体定义了,但是对象成员还没有定义,那么,对象的成员在哪里定义呢?
private中是对对象成员的声明,并不是定义。
所以对对象成员定义,就是在初始化列表中。
为什么是引用成员变量、const成员变量、自定义类型成员呢?
因为:
1.引用成员变量和cosnt成员变量必须在定义的时候初始化,必须在定义的时候赋值,所以放在初始化列表中进行定义并且赋值。
2. C++类的构造函数的调用顺序是先父类构造、再成员类对象调用构造、最后才是调用自身的构造函数。因此如果成员对象并没有默认构造函数(即不需要参数就能调用的构造函数)就必须给其传参数去调用带参的构造函数。初始化列表的执行要早于构造函数体,因此要将自定义类型成员放在初始化列表中初始化。
初始化列表的特性
1.尽量使用初始化列表。
因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表进行初始化。
class Time
{
public:
Time(int hour = 1)
:_hour(hour)
{
cout << "Time(int hour = 1)" << endl;
}
private:
int _hour;
};
class Date
{
public:
Date(int day)
{}
private:
int _day;
Time _time;
};
int main()
{
Date d1(2);
return 0;
}
2.成员变量在类中声明的次序就是初始化列表中的初始化顺序,与其在初始化列表中的顺序无关。
class A
{
public:
A(int a)
:_a1(a)
,_a2(_a1)
{}
void Print() {
cout<<_a1<<" "<<_a2<<endl;
}
private:
int _a2;
int _a1;
};
int main() {
A aa(1);
aa.Print();
}
在上面的代码中,先声明的是_a2,后声明的是_a1,在A aa(1)中,
应该先走,_a2(_a1),但是此时,_a1是随机值,所以_a2也应该被初始化为随机值。
第二步再初始化_a1(a), a = 1,所以_a1 = 1。
所以输出是: 1 随机值
所以:在使用初始化列表的时候,声明的顺序要和初始化列表的定义的顺序一样,不然如果涉及到指针、堆,可能就会酿成大错!