今天要学习两个特殊的函数,分别是构造函数和析构函数,它们究竟有什么用呢?
比如说,我们先写一个简单的日期的类
class Date {
public:
void Init() {
_year = 1;
_month = 1;
_day = 1;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main() {
Date d1;
d1.Init();
d1.Print();
return 0;
}
我们要定义一个对象后还要对这个对象进行初始化,只要定义对象就要初始化,这个步骤是不是有点太繁琐了,甚至我们有时候还会忘记初始化,所以这时,就创造了一种函数叫做构造函数,它其实就是去完成了初始化这个工作,并且它是在对象创建后自动调用的,是不是就让我们省心了不少
它的一些规则是这样的:
1.函数名与类名相同,意思是在日期这个例子中,构造函数的函数名也叫Date
2.无返回值,这里的无返回值意思不是写void,而是根本就不需要写返回值
3.对象实例化(创建对象)时编译器自动调用对应的构造函数
4.构造函数可以重载
比如说我去写一个构造函数
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 d1(2023,11,22);
d1.Print();
return 0;
}
这时就可以替换掉Init函数,并且调用的话也是这么去调用,规则的第四条是什么意思呢?构成重载嘛,就是构成重载的函数可以同时存在,比如说
Date() {
_year = 1;
_month = 1;
_day = 1;
}
Date(int year,int month,int day) {
_year = year;
_month = month;
_day = day;
}
当我去传参调用的话就调用第二个,当我不传参调用就调用第一个,不会产生歧义
我们又知道有缺省参数这回事,如果我们给第二个构造函数缺省值的话
那我不传参调那个呢?结果是编译出错,因为调用有歧义
我们已经知道,如果不给构造函数的话,对于日期类(其实也就是内置类型)编译器不会自动去调用构造函数。那如果是自定义类型呢?那编译器就会去调用自定义类型的构造函数,如果自定义类型也没写构造函数,那编译器也无法去调用。
所以在C++11中,内置类型是可以给值的,比如说
就是在声明的时候后面给上值,这时如果没有构造函数的话,就会用给定的值去初始化,就像这样
这种情况我们是不是没有写构造函数,可是数据还是初始化了,这时我们就有了一个默认构造的概念,总的来说,我们不去传参数调用的构造函数,都可以叫做默认构造。除了这种情况,还有无参构造函数和全缺省构造函数也可以叫默认构造。这三种情况是不能同时存在的
那我们在看看下面这种情况
构造函数不给缺省值,并且在调用的时候也不给值,这样是会报错的,
因为我既然不给d1传值,但是它有构造函数,不传参数就无法调用这个构造函数
有了所谓的初始化函数,那我们是不是也需要一个销毁函数呢?这样就有了我们的析构函数,这里的析构函数其实不是完成对对象本身的销毁,而是完成对象中资源的清理工作(比如说,当一个日期类的对象生命周期结束时,它调用析构函数是没有多大意义的,因为它的年月日随着栈帧的销毁就一并销毁了;但是对于一个栈,它是在堆上申请过空间的,这时把堆上的空间要释放掉是非常有必要的),它的规则如下
1.析构函数名是在类名前加上~
2.无参数,无返回值类型
3.一个类只能有一个析构函数,若未显示定义,系统会自动生成默认的析构函数,并且析构函数不能重载,因为根本就没有参数嘛
4.对象生命周期结束时,会自动调用析构函数
大概就是这样子的
可以看到,一个对象的创建和生命周期结束确实会自动调用构造函数和析构函数
这里打印的话就是为了表示一下是否调用了该函数和什么时候调用析构函数