构造函数、拷贝构造函数、析构函数
构造函数
一、概念
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时,由编译器自动调用,在对象的生命周期内只调用一次且是在创建对象时被调用的,它保证了每个成员都有一个合适的初始值。
//代码
//构造函数
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}//构造函数
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d(2018, 4, 10); //对象d是在调用完构造函数后,才被创建好的
return 0;
}
二、特性
- 函数名与类名相同。
- 没有返回值。
- 新对象被创建时,由编译器自动调用,且在对象的生命周期内仅调用一次。
- 构造函数可以重载,实参决定了调用哪个构造函数。
- 无参构造函数和带有缺省值的构造函数都认为是缺省的构造函数,并且缺省的构造函数只能有一个,否则会造成调用不明确。
- 有初始化列表(一般不用)
//带有初始化列表的构造函数
class Date
{
public:
//带有初始化列表的构造函数
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d(2018, 4, 10);
return 0;
}
- 如果没有显式定义时,编译器会合成一个默认的构造函数。
- 构造函数不能用const修饰。因为在构造函数中可能会对成员对象赋值,若被const修饰之后,就不能进行赋值操作。
- 构造函数不能为虚函数。
三、对象初始化
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每一个成员变量后面跟一个放在括号中的初始值或表达式。
注:
(1)每个成员在初始化列表中只能出现一次,因为成员被初始化时,只能被初始化一次。
(2)初始化列表仅用于初始化类的数据成员,并不指定这些数据成员的初始化顺序,数据成员在类中声明顺序就是参数列表中的初始化顺序。
(3)尽量避免使用成员初始化成员,成员的初始化顺序最好和成员的定义顺序保持一致。
四、构造函数的作用
- 构造对象
- 初始化对象
注:
- 类中一定要放在初始化列表位置进行初始化的成员:
引用成员变量、const修饰的成员变量、类类型成员(该类有非缺省的构造函数)
拷贝构造函数
一、概念
只有单个行参,而且该形参是对本类型对象的引用(常用const修饰),这样的构造函数成为拷贝构造函数。创建对象时使用已存在的同类对象来进行初始化,由编译器自动调用。
//拷贝构造函数
class Date
{
public:
//构造函数
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
//带有初始化列表的拷贝构造函数
Date(const Date& d) //加const是为了避免对实参造成不必要的改动
: _year(d._year)
, _month(d._month)
, _day(d._day)
{}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2018, 4, 14);
Date d2(d1);
return 0;
}
二、特征
1. 构造函数的性质拷贝构造函数都满足。
2. 参数必须使用类类型对象引用传递,如果进行传值的话会使程序陷入死循环。
3. 如果没有显式定义,系统会自动合成一个默认的拷贝构造函数。默认的拷贝构造函数会依次拷贝类的数据成员完成初始化。但有些类的拷贝构造函数必须显示的定义出来,否则会造成程序崩溃。
三、使用场景
- 对象实例化对象。Date d2(d1);
- 作为函数的参数。Date Test(const Date d);
析构函数
一、概念
与构造函数功能相反,在对象被销毁时,由编译器自动调用,完成类的一些资源清理和汕尾工作。
//析构函数
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
~Date()
{}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2018, 4, 19);
return 0;
}
二、性质
- 析构函数在类名前加~
- 析构函数无参数无返回值,所以析构函数不能函数重载。
- 一个类有且只有一个析构函数。若没有显式定义时,系统会根据情况自动生成缺省的构造函数(当类B中包含类A的类的成员对象,且类A中有析构函数而类B无时,编译器会自动合成析构函数)。
class Time
{
public:
Time(int hour, int minute, int second)
: _hour(hour)
, _minute(minute)
, _second(second)
{}
~Time()
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
, t()
{}
private:
int _year;
int _month;
int _day;
Time t;
};
int main()
{
Time t(11, 4, 20);
Date d(2018,4, 20, t);
return 0;
}
- 对象生命周期结束时,C++编译系统自动调用析构函数。
- 注意析构函数内部并不是删除对象,而是做一些清理工作。