1.析构函数
1.1析构函数概念
与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成 的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。它的名字与类名相同,前面加上一个波浪号(~),没有返回类型,也没有参数。当对象被销毁时,析构函数会自动被调用。
class MyClass {
public:// 构造函数MyClass() {// 初始化对象}// 析构函数~MyClass() {// 清理对象}
};
需要注意的是析构函数,一个类就只能有一个析构函数,不能想构造函数那样进行重载。
1.2析构函数的使用
析构函数也是一个默认成员函数,在我们未显示写时,会自动生成析构函数。
上述代码中,我们创建了两个类,在B中创建了A类型的成员变量,我们只在A中显示写析构函数,B中没有写,当我们再运行一步会发生什么呢?
我们发现B中默认生成的析构函数去自动调用了A中的析构函数,但是未对B中内置类型起作用。
所以对于自定义类型会去调用相应的析构函数。
其实当我们没有在A中写析构函数,程序依然能正常运行,因为上述代码中所申请的空间都会在程序结束后销毁。所以不是所有的类都需要析构函数,我们现在只需要默认知道像动态申请空间和打开文件需要即可。
自动生成的析构函数会按照默认行为来销毁对象。默认的析构函数不执行任何操作,它只是简单地销毁对象并释放对象占用的内存。这种情况适用于大多数类,特别是当类没有动态分配的资源或需要特殊的清理操作时。
然而,在某些情况下,需要手动定义析构函数来执行特定的清理操作。例如,如果类使用了动态分配的内存,我们需要在析构函数中释放这些内存。另外,如果类打开了一些文件或建立了网络连接,我们需要在析构函数中关闭文件或断开网络连接。
总而言之,自动生成的析构函数能够满足大多数情况下的需要,但在特定情况下可能需要手动定义析构函数,并编写相应的清理代码。
2.拷贝构造函数
1.1概念
只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型 对象创建新对象时由编译器自动调用。简单来说就是用对象去初始化对象。
1.2特性
1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发 无穷递归调用。
3.拷贝构造函数也是默认成员函数。
4.若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成 拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
为何会发生无穷的递归调用呢?
其实在C++中传类时会自动调用拷贝构造函数,若拷贝构造函数的形参是类,而不是类的引用,那么就会出现无穷递归。
如上图,我们在使用拷贝构造时,传的d1而引发拷贝构造,而此时我们传的d1给另一个拷贝构造,但是另一个拷贝构造,又开始调用另一个拷贝构造,这样下去就会形成无穷递归。
1.3使用
class A {
public:A(int year = 2024, int month = 2, int day = 1) {_year = year;_month = month;_day = day;}A(const A& s) {_year = s._year;_month = s._month;_day = s._day;}void print() {cout << _year << "_" << _month << "_" << _day << endl;}
private:int _year;int _month;int _day;
};int main() {A b;A b1(b);A b2 = b;//拷贝构造的两种调用b1.print();b2.print();return 0;
}
我们来看一下输出结果:
我们拷贝构造成功了,那么我们将显示写的拷贝构造删掉,默认生成的拷贝构造还可以完成任务呢?
我们发现自动生成的拷贝构造够用,但是需要注意的是这里是浅拷贝,是按照字节来拷贝的,如果我们有申请动态空间,然后用默认生成的拷贝构造来生成对象,这样只会对值进行拷贝,但是不会开辟动态空间,这样就会出问题,所以当我们申请动态空间的类需要拷贝构造时需要我们自行去现实些深拷贝。
类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构 造函数是一定要写的,否则就是浅拷贝。