有以下代码
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Init(2022, 7, 5);
Date d2;
d2.Init(2022, 7, 6);
return 0;
}
对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法,然后自己设定信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?------构造函数可以解决这个问题。
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。
需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
using namespace std;
class Date
{
public:
//无参构造函数
Date()
{
_year = 2001;
_month = 4;
_day = 28;
}
//带参构造函数
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 d2(2022,9,27);//调用带参
// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,
// 否则就成了函数声明
// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
Date d3();//变成声明
return 0;
}
上述代码d1会初始化为2001.4.28,d2会初始化为2022.9.27,所以看情况调用哪个构造函数。
上述代码太繁琐,使用全缺省或者半缺省值可以把两个函数合并到一起,使用全缺省或者半缺省是推荐的。
语法上无参构造函数和全缺省构造函数可以同时存在,但是如果存在无参调用就会造成二义性,因为这两个函数都可以在无实参的情况下被调用。
using namespace std;
class Date
{
public:
//无参构造函数
Date()
{
_year = 2001;
_month = 4;
_day = 28;
}
//全缺省构造函数
Date(int year = 1, int month = 2, int day = 3)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2000,4,11);//调用全缺省
Date d1;//引起二义性
return 0;
}
如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
默认构造函数包括自己写的无参的和全缺省的,还有系统默认生成的共三种。
这里要注意的是,默认构造函数不是指编译器默认生成的,而是不需要传参就能够调用的构造函数就叫默认构造函数。所以说2.1章节的用户自定义构造函数里的无参构造函数和全缺省构造函数其实也是默认构造函数,但是章节1中的带参而不带缺省值的构造函数不是默认构造函数。并且默认构造函数只能有一个,不是用户自己写就是编译器写。
要区分系统写的默认构造函数和用户写的默认构造函数的区别就要先搞清楚:
其中内置类型包括:int/char/double/指针/数组等
自定义类型包括:class/struct等定义的类型
我们不写,编译器会默认生成构造函数,而对于内置类型却不做初始化处理。
对于自定义类型,会回去调用它的默认构造函数(是它自己类里面的默认成员函数) 如果没有默认构造函数就会调用编辑器默认生成的无参构造函数。
class Date
{
public:
/*
// 如果用户显式定义了构造函数,编译器将不再生成
//不是默认构造函数,既不是全缺省也不是无形参
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
*/
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
// 将Date类中构造函数屏蔽后,代码可以通过编译,
//因为编译器生成了一个无参的默认构造函数。
// 将Date类中构造函数放开,代码编译失败,
//因为一旦显式定义任何构造函数,编译器将不再生成。
// 无参构造函数,放开后报错:
//error C2512: “Date”: 没有合适的默认构造函数可用。
Date d1;
return 0;
}
那如果我声明一个自定义类型,但是又不给它默认构造函数呢?它会被编译器写的默认构造函数初始化吗?-----------答案是不会,这其实和上述问题同一个意思,只是这个自定义类型声明在别人的成员里,而刚开始的自定义类型声明在main函数。
class Time//声明一个新类
{
public:
//这是Time对象自己的默认构造函数
Time()
{
cout << "Time()" << endl;
_hour = 0;
_minute = 0;
_second = 0;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year;
int _month;
int _day;
// 在Date里声明Time类型的自定义类型
Time _t;
};
上述代码当我们将Time对象的默认构造函数注释掉时,就相当于没有默认构造函数那么系统就会去生成一个,但是此时和内置类型一样,都不会对其成员进行初始化。而当Time函数还在时,就会把它当成自定义类型自己的默认构造函数并且调用它。
自己没写编译器就生成,写了编译器就不生成,然后对于内置类型不做初始化,对于自定义类型就会去调用自定义类型对象自己的默认构造函数,如果另一个类里也没有写的话就都不初始化。
注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。
class Date
{
private:
// 基本类型(内置类型)
int _year = 1970;
int _month = 1;
int _day = 1;
};
这也算是C++官方做出的解决方案之一了。
既然系统生成的默认构造函数对内置类型不处理,那这个默认构造函数有什么用?
其实这算是C++的不足之处,但是这个函数还是需要用户结合需求情况,一般都是要自己实现构造函数,现在看来构造函数的优点不在于能否初始化,而在于会自动调用,只要你实现了构造函数,那么就没有问题。
C++系统默认生成的构造函数设计得不好,没有对内置类型和自定义类型统一处理,不处理内置类型成员变量,只处理自定义类型成员变量。