我们使用内置类型创建对象的时候,因为内置类型是一个固定的类型(比如int),所以编译器会为我们分配空间(4字节),使得我们的代码正常运行。
而用类实例化出来的对象,因为是自定义类型,所以系统提前并不知道我们所定义出来的类型有多大,占多少字节。
在我们用类实例化对象的时候,通常都会在类的默认成员函数中调用一个叫做构造函数的东西。
通过构造函数,我们可以对成员变量进行初始化,编译器也可以计算出我们类的大小,为类开辟相应的空间。
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时,由编译器自动调用,在对象的生命周期内只调用一次,保证每个数据成员都有一个合适的初始值。
class Date{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
cout << this << endl;
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
构造函数具有以下特性:
- 函数名与类名相同
- 没有返回值,不是void,是没有返回值
- 新对象被创建时,由编译器自动调用,且在对象的生命周期内仅调用一次
- 构造函数可以重载,实参决定了调用哪个构造函数
- 无参构造函数和有参带缺省值的构造函数都认为是缺省的构造函数,并且缺省的构造函数只能有一个
- 有初始化列表(可以不用)
- 如果没有显示定义,在必要的时候,编译器会合成一个默认的构造函数
- 构造函数不能用const修饰
- 构造函数不能为虚函数
谈完构造函数,另外一个函数叫做拷贝构造函数,也需要拿出来说一说。
内置类型中,我们可以用一个相同类型的变量为另外另外一个相同类型的变量初始化,而在类类型中,这一个功能是由拷贝构造函数来完成的。
既然是拷贝构造,所以拷贝构造函数也具有构造函数的特性。
拷贝构造函数只有单个形参,而且该形参是对本类类型对象的引用,这样的构造函数称为拷贝构造函数。拷贝构造函数是特殊的构造函数,创建对象时使用已存在的同类对象来初始化,由编译器自动调用。
class Date{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
cout << "构造函数" << this << endl;
_year = year;
_month = month;
_day = day;
}
Date(Date& d)
{
cout << "拷贝构造函数" << this << endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
构造函数的特征:
- 构造函数的重载,构造函数的性质拷贝构造函数均满足
- 参数必须使用类类型对象引用
- 如果没有显示定义,系统会自动合成一个默认的拷贝构造函数。默认的拷贝构造函数会依次拷贝类的数据成员完成初始化
那么既然有创建,那么自然也有销毁,在销毁的时候,系统会默认调用的一个函数,叫做析构函数。
析构函数与构造函数功能相反,在对象被销毁时,由编译器自动调用,完成类的一些资源清理和收尾工作。
析构函数的特性:
- 析构函数在类名加上字符~
- 析构函数无参数无返回值
- 一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数。
- 对象生命周期结束时,C++编译系统自动调用析构函数
- 注意析构函数体内并不是删除对象,而是做一些清理工作
class Array{
public:
Array(int sz = 10)
{
cout << "构造函数" << this << endl;
ptr = (int*)malloc(sizeof(int)*sz);
if (!ptr)
{
cerr << "ptr" << endl;
exit(1);
}
size = sz;
}
Array(Array& arr)
{
cout << "拷贝构造" << this << endl;
ptr = (int*)malloc(sizeof(int)*arr.size);
if (!ptr)
{
cerr << "ptr" << endl;
exit(1);
}
memcpy(ptr, arr.ptr, sizeof(int)*arr.size);
}
~Array()
{
cout << "析构函数" << this << endl;
if (!ptr)
{
free(ptr);
ptr = NULL;
}
}
private:
int *ptr;
int size;
};