再谈构造函数
构造函数体赋值
在实例化对象时,编译器会通过调用构造函数给对象中的各个成员变量一个合适的初始值:
class Date {
public:
//构造函数
Date(int year = 2023,int month = 6,int day = 5)
{
//_year可以多次赋值
_year = year;
_year = 2024;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初始值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。
初始化列表
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或表达式。
class Date {
public:
//构造函数
Date(int year = 2023,int month = 6,int day = 5)
//初始化列表
:_year(year)
,_month(month)
,_day(day)
{
}
private:
int _year;
int _month;
int _day;
};
对于对象来说,初始化列表是对象成员变量定义初始化的地方。
在定义类时,类中的成员变量只是声明并不是定义初始化。
当我们在成员变量中加上const
,程序就不能正常运行了,这是因为const
变量必须在定义的时候初始化,const
只有一次初始化的机会,所以必须给成员变量找一个定义的位置,不然像const
类型的成员不好处理。所以在C++11中规定构造函数初始化列表是成员变量定义和初始化的地方。
class Date {
public:
//构造函数
Date(int year = 2023, int month = 6, int day = 5)
//初始化列表
:_year(year)
, _month(month)
, _day(day)
,_t(20) //true,初始化列表是成员变量定义的地方
{
_t = 30;//error,不能在构造函数体内初始化
}
private:
//成员变量的声明
int _year;
int _month;
int _day;
const int _t = 20; //这里只是缺省值,并不是初始化
};
int main()
{
Date d;//这里的定义是对象整体的定义
//成员变量在构造函数的初始化列表中定义并且初始化
}
注意:
1.哪个对象调用构造函数,初始化列表就是该对象所有成员变量定义的位置,且每个成员变量在初始化列表中只能出现一次。
因为任何类型的变量初始化只能进行一次,所以同一个成员变量在初始化列表中不能多次出现。
2.不管是否显式在初始化列表定义初始化成员变量,编译器对每个成员变量都会在初始化列表中进行定义初始化;当在初始化列表显式定义初始化成员变量的时候,使用初始化列表中的值。
例如当Date类的构造函数初始化列表为空时,也会先走初始化列表再走函数体。
class Date {
public:
//构造函数
Date(int year = 2023, int month = 6, int day = 5)
//初始化列表为空时,也会先走这里在进入函数体
{
}
/*Date(int year = 2023, int month = 6, int day = 5)
//初始化列表
:_year(year)
, _month(month)
, _day(day)
{
}*/
private:
//成员变量的声明
int _year;
int _month;
int _day;
};
3.有三个变量必须在初始化列表初始化:const变量
,int& 变量名
(引用也必须在定义的地方初始化),没有默认构造的自定义类型成员。
默认构造函数:无参的构造函数、全缺省的构造函数以及编译器自动生成的构造函数。
下面的程序中B类的构造函数不是默认构造函数,A类中的自定义类型成员变量_bb
会自动调用B类的构造函数,所以在A类中的初始化列表中要定义_bb
。const
成员变量_c
和引用成员变量_ref
都要在初始化列表中定义。
class B {
public:
//B的构造函数,不是默认构造函数
B(int a)
:_a(a)
{
cout << "B(int a = 20)" << endl;
}
//B的打印函数
void Print()
{
cout << _a << endl;
}
//B的析构函数
~B()
{
cout << "~B()" << endl;
}
private:
int _a = 0;
};
class A
{
public:
A()
:_a(2)
,_b(2)
,_c(3)
//,_ref(_c)//权限的放大,不可以,_c是const类型的变量
,_ref(_a)
,_bb(30)
{}
void Print()
{
cout << _a << "/" << _b << "/" << _c << "/" << _ref << "/" << endl;
_bb.Print();
}
private:
int _a;//声明
int _b;
const int _c;
int& _ref;
B _bb;
};
int main()
{
A a;
a.Print();
return 0;
}
4.成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。