c++ -- 析构函数

假期 2020.02.04

慕课学习资源



定义

  • 名字与类名相同,在前面加“~”即可,与构造函数一样,没有返回值,但是有参数,并且一个类中最多一个析构函数。
  • 析构函数在对象消亡之后自动被调用,常用来释放空间
  • 编译器通常会自动生成缺省的析构函数,与确实缺省构造函数类似,实际上什么都不做
  • 如果定义了,则编译器不生成缺省析构函数

运用示例

其中创建一个对象数组,一共三个对象,三个对象在程序结束后自动调用析构函数
在这里插入图片描述


常出现情况

  1. delete 运算导致析构函数调用。
    在这里插入图片描述
  2. 若new一个对象数组,那么用delete释放时应该写 []。否则只delete一个对象(调用一次析构函数)
    在这里插入图片描述
  3. 析构函数在对象作为函数返回值返回后被调用,函数调用返回时生成临时对象返回
    在这里插入图片描述

分析:在return a之后,子函数中的对象消亡,调用第一次析构函数;在主函数结束之后,主函数中的实参消亡,进行调用第二次析构函数。
对比下例:
在这里插入图片描述
分析: 在return a之后,子函数中的对象消亡,调用第一次析构函数;函数调用的返回值(临时对象)用过后,该临时对象析构函数被调用,进行调用第二次析构函数;第三次定义的全局对象消亡,调用析构函数。


析构函数调用顺序

在网上扒的一段代码

#include <iostream>
#include <string>
using namespace std;

class Demo {
public:
    Demo(string s);
    ~Demo();
private:
    string m_s;
};
Demo::Demo(string s) : m_s(s) { }
Demo::~Demo() { cout << m_s << endl; }

void func() {
    //局部对象
    Demo obj1("1");
}

//全局对象
Demo obj2("2");

int main() {
    //局部对象
    Demo obj3("3");
    //new创建的对象
    Demo* pobj4 = new Demo("4");
    func();
    cout << "main" << endl;

    return 0;
}

执行效果
在这里插入图片描述
分析:发现全局对象是最后调用析构函数的;最先是局部对象;
理论原因:

  • 在函数内部创建的对象是局部对象,位于栈区,函数执行结束时会调用这些对象的析构函数。
  • new 创建的对象位于堆区,通过 delete 删除时才会调用析构函数;如果没有 delete,析构函数就不会被执行。
  • 在所有函数之外创建的对象是全局对象,位于内存分区中的全局数据区,程序在结束执行时会调用这些对象的析构函数,并且是最后调用的,
    如下图
    在这里插入图片描述
    我们会发现全局变量确实按照栈的顺序进行调用析构函数的,后入先出。其实全局对象与局部对象都是按照如此的方式进行调用析构函数顺序,只要是以栈的形式分配空间。但这个也与程序的执行有关,从上到下的顺序。
    附:
    栈: 在 windows 下,栈是向低地址扩展的数据结构,是一块连续的内存区域,栈顶的地址和栈的最大容量是系统预先规定好的,能从栈获得的空间较小。
    :堆是向高地址扩展的数据结构,是不连续的内存区域,这是由于系统是由链表在存储空闲内存地址,自然堆就是不连续的内存区域,且链表的遍历也是从低地址向高地址遍历的,堆得大小受限于计算机系统的有效虚拟内存空间,由此空间,堆获得的空间比较灵活,也比较大。

举一个慕课上面的例子

#include<iostream>
using namespace std;
class Demo {
	int id;
public: 
	Demo(int i){
		id = i; 
		cout << "id=" << id << " constructed" << endl;
	}
	~Demo() {
		cout << "id=" << id << " destructed" << endl;
	} 
};
Demo d1(1); 
void Func() {
	static Demo d2(2); Demo d3(3); cout << "func" << endl;
} 
int main() {
	Demo d4(4); 
	d4 = 6; 
	cout << "main" << endl; 
	Demo d5(5);
	Func(); 
	cout << "main ends" << endl; return 0;
}

执行结果

  1. 对应全局对象Demo d1(1);
id=1 constructed
  1. 对应局部变量Demo d4(4);
id=4 constructed
  1. 临时对象d4 = 6;
id=6 constructed
id=6 destructed
main
  1. 对应局部变量Demo d5(5);
id=5 constructed
  1. 两个局部对象
    static Demo d2(2); Demo d3(3);
id=2 constructed
id=3 constructed
func
  1. 子函数返回,调用析构函数.静态局部对象见后续分析
id=3 destructed
main ends
  1. 主函数中的两个局部对象,调用析构函数
id=5 destructed
id=6 destructed
  1. 剩下全局对象和局部对象。而全局变量和静态局部变量时从静态存储区中划分的空间,即栈,二者的区别在于作用域的不同,全局变量作用域大于静态局部变量(只用于声明它的函数中),而之所以是先释放 2 在释放 1的原因是, 程序中首先调用的是 1的构造函数,然后调用的是 2 的构造函数,析构函数的调用与构造函数的调用顺序刚反。
id=2 destructed
id=1 destructed

复制构造函数与析构函数的例子

在这里插入图片描述

发布了166 篇原创文章 · 获赞 45 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_44116998/article/details/104154453