前言
"打牢基础,万事不愁" .C++的基础语法的学习."学以致用,边学边用",编程是实践性很强的技术,在运用中理解,总结.
引入
用一个简单的例子,分析析构函数的工作过程.以<C++ Prime Plus> 6th Edition(以下称"本书")内容做参考.
回顾类的默认函数
类的默认函数及用途如下:
构造函数:给属性赋值
复制构造函数:用已有对象初始化新对象,对象之间传递属性值
赋值符:类似于复制构造函数,传递值.
析构函数:删除对象属性
既然是默认函数,表示可以重载.如果没有属性,构造函数没有重载必要;复制构造函数和赋值符重载前面已解读过,应用场景是深复制中处理静态数据,以及复制指针原有的数据.这里主要是说析构函数的重载.
回顾new和delete
new的含义:在堆空间上开辟内存空间,返回指向该内存空间首地址的指针
delete的含义:释放new所指向的内存空间
new的格式:
指针类型 指针名=new 数据类型[元素个数]; //生成n个元素的数组,返回指针
指针类型 指针名=new 数据类型; //指向单个元素,返回指针
int *par=new int[5]; //par指向一个整型数组的指针
int *p=new int; //p指向一个int类型变量
可以在后面用{}加上该种数据类型的值
指针类型 指针名=new 数据类型[元素个数]{值列表(用逗号分开)};
指针类型 指针名=new 数据类型{单个值};
int *par=new int[5]{1,2,3,4,5}; //生成指向整型数组的指针并赋值
int *p=new int{3}; //生成指向单个整型值3的指针
=============
int b=par[1]; //par[1]值为2,间接访问数组
int c=*p; //*p的值为3,间接访问数据
delete的格式:和new相对应,当new生成的指针指向数组,用[],否则不用
delete[] par; //释放par指向的内存空间
delete p; //释放p指向的内存空间
析构函数的重载
下面用一个简单例子来说明:
#include<iostream>
using namespace std;
class Demo { //Demo类
int* pint;
public:
Demo(int* d):pint(d){}
~Demo() { delete pint; } //析构函数,注释看效果
};
class OutDemo { //OutDemo类
Demo* demo;
public:
OutDemo (Demo * d) : demo(d) {}
~OutDemo() { delete demo; } //析构函数,注释看效果
};
测试代码:
int main(void) {
int* pval = new int{ 3 }; //new生成整型指针
Demo* pd = new Demo(pval); //new生成Demo对象,返回指针
OutDemo* pod = new OutDemo(pd); //new生成OutDemo对象,返回指针
cout << "调用析构函数前的值为:" << *pval << endl;
cout << "调用析构函数前的地址为:" << pval << endl;
delete pod; //释放内存
cout << "调用析构函数后的值为:" << *pval << endl;
cout << "调用析构函数后的地址为:" << pval << endl;
}
说明:分别注释两个重载的析构函数,可以看出不同.下面分析
析构函数的调用过程
1>使用默认析构函数.
此时上面两个类中的析构函数应处于注释状态.当调用delete pod;时, 调用默认析构函数,
~OutDemo() {}; //默认析构函数
按照默认构造函数的定义,对象中属性被删除,即数据类型Demo*,变量demo的表示的值-----pd被删除,而pd指向的数据pval未删除.所以打印出来的结果*pval仍然等于3.
2>使用重载析构函数
此时上面两个类定义的析构函数应处于非注释状态.当调用delete pod;时,调用重载析构函数
~OutDemo() { delete demo; }
重载析构函数实际上有两个部分,一是调用默认析构函数,把对象中的属性pd删除(与上面一条相同)二调用delete demo;------他的含义是调用Demo的析构函数,此时对应语句是:
~Demo() { delete pint; }
此时再重复上一种状况,一删除对象属性pval,二调用delete pint,释放整型指针pint指向的空间,删除其数据,所以打印*pval的结果已发生改变.
有一个细节:当传递进去的指针不是new生成时,会报错,如把int* pval = new int{ 3 };改成
int a=3;
int *pval=&a;
运行后产生异常. 说明new和delete需配合使用
析构函数的小结
默认析构函数做了什么? 删除属性
重载析构函数做了什么?一调用默认析构函数;二根据内容操作.
重点:当调用"delete 对象指针名"时,执行析构函数 .如果是默认析构函数则只删除属性,如果重载了析构函数先删除属性,再执行其他内容.
优点:只要是传入的指针((new生成),在析构函数上加上删除语句(如delete p;),释放内存空间.而对象和他包含的对象之间是各自独立的.如delete pod;释放了最初传入的整型指针pval
应用场景:对内存空间要求比较高时,不然传对象引用就可以了.在笔者另一篇帖子里C++基础语法:复制构造函数,赋值构造函数及浅复制,深复制_复制构造和赋值-CSDN博客有介绍
更新
前面讲的都是针对指针属性,如果复杂一点,属性是双重指针该如何解决呢?思路一样的,析构函数中delete后面那个是指针.双重指针通常是指针数组,所以应该遍历他,再用delete.
class Demo { //Demo类
int** ppint;
public:
Demo(int** d):pint(d){}
~Demo() {
// for(int i=0;i<sizeof(ppint)/sizeof(int*);i++){ //这种方法求不出指针数组长度
for(int i=0;i<length;i++){ //这里length可以硬编码,因为数组长度固定
delete ppint[i]; //ppint的长度也是固定的
}
}
};