构造函数
一、作用
主要用来完成对象的初始化工作。
二、定义及使用
构造函数的定义:
类名(参数表)
{
函数体
}
举例说明:
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 d1=Date(2019,10,5); //显示的调用构造函数
Date d1(2019,10,5); //隐式的调用构造函数
Date* p=new Date(2019,10,22); // new和构造函数一起使用
}
解析:
Date d1=Date(2019,10,5);
就是分别将数据成员_year、_month、_day
分别初始化为2019、10、5。显示调用和隐式调用都是一样的作用。
对于语句Date* p=new Date(2019,10,22);
它创建了一个无名对象,并将其初始化为参数提供的值,并将该对象的地址赋给指针p
。在这种情况下,新对象没有名称,但是可以根据指针来进行管理。
注意:
我们无法使用对象来调用构造函数,因为在构造函数构造出对象之前,对象是不存在的。因此构造函数是由编译器自动调用,而不能通过对象来调用。
三、特点
1、函数名和类名相同
2、无返回值
3、在创建对象时,编译器自动调用
4、可以重载
四、默认构造函数
(1)在没有显示的定义构造函数时,编译器将自动生成一个默认构造函数。
如下所示:
Date ()
{}
如果这时候创建了一个对象d
,列如:
Date d
那么这个对象的数据成员都是随机值,但是程序不会出错。
(2)如果显示的定义了构造函数,列如只定义了如下构造函数:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
如果此时创建一个对象d
,列如:
Date d
那么这时就会编译报错。因为只有在没有显示定义构造函数时,编译器才会提供默认的构造函数。所以如果想要不出错,那么就要再写一个无参的构造函数。
总的代码就如下所示:
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date() //无参构造函数或者也叫做默认的构造函数
{}
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
};
int main()
{
Date d1; //调用的是默认的构造函数
Date d3(2019, 10, 5); //调用的是带三个参数构造函数
}
五、带默认值参数的构造函数
不仅是普通的函数可以缺省参数,构造函数也可以进行缺省。
列如:
Date(int year=2022, int month=3, int day=5)
{
_year = year;
_month = month;
_day = day;
}
它的使用办法和普通函数使用缺省是一样的。
六、成员初始化列表
我们可以将上面的构造函数利用成员初始化列表换成另外一种形式,如下所示:
Date(int year, int month, int day):_year(year),_month(month),_day(day)
{}
注意:
对于用const修饰的数据成员、或是引用类型的数据成员我们只能使用成员初始化列表来进行初始化。
七、成员初始化的顺序
首先看一下,下面这段代码:
#include<iostream>
using namespace std;
class D
{
public:
D(int i):x(y+1),y(i)
{
cout << "x:" << x << endl;
cout << "y:" << y << endl;
}
private:
int x;
int y;
};
int main()
{
D d(10);
}
运行结果:
解析:
按照成员初始化列表的本意,应该是首先用i
的值去初始化y
,然后再用y
的值去初始化x
。但是为什么结果却在意料之外呢?这是因为数据成员的初始化顺序是按照它在类中声明的顺序初始化的。所以它会首先初始化x
,然后再初始化y
。