C++析构函数内部工作机制详解

 更新时间:2023年02月27日 11:18:48   作者:北冥有鱼丶丶  
析构函数(Destructor)也是一种特殊的成员函数,没有返回值,不需要程序员显式调用(程序员也没法显式调用),而是在销毁对象时自动执行。构造函数的名字和类名相同,而析构函数的名字是在类名前面加一个~符号

我们主要从三个方面来学习析构函数的工作原理:

  • 析构函数的内部工作机制
  • 默认析构函数的内部工作机制
  • 析构函数的调用

1、析构函数的内部工作机制

众所周知,在对象的生命周期结束时会自动调用析构函数用于清理对象所申请的资源,那么它是如何清理的呢?

析构函数会调用delete函数释放对象中new出来的空间,即析构函数通过delete函数来清理对象所申请的资源,当然如果对象没有申请资源,那么就无需调用delete函数

由于是new出来的对象是在堆上分配空间的,即使离开了作用域,其依然存在,我们必须在析构函数中主动delete来释放new出来的在堆上的空间,否则对象消亡后,离开了作用域后,指向该空间的数据成员(指针)就会消失,我们失去了对这片空间的控制权,别人也无法使用这片空间,这就会造成内存泄漏。

如果对象是new出来的,那么它就是一个堆对象,不会被 操作系统自动回收,需要我们手动调用delete函数释放该堆对象,在delete函数中会先调用析构函数释放该对象所申请的资源,那么析构函数如何释放对象所申请的资源呢,就是前面所说的,析构函数又会调用delete函数来释放对象所申请的资源,当然如果对象没有申请资源,那么就无需调用delete函数。

#include <iostream>
using namespace std;
class Person {
public:
    Person() {
        cout <<  "调用了Person的构造函数" << endl;
    }
    ~Person() {
        cout << "调用了Person的析构函数" << endl;
    }
private:
    int name;
};
int main() {
    Person* person = new  Person();
    delete person;
    return 0;
}

2、默认析构函数的内部工作机制

我们清楚了析构函数的内部工作机制后,继续思考一个问题,默认析构函数它的内部是怎么工作的呢?

我们先来看看下面这段代码

class Time
{
public:
	~Time()
	{
		cout << "~Time()" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

程序运行结束后输出:~Time(),在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?

这就涉及到默认析构函数的内部工作机制了。

默认析构函数,对于基本类型成员不做处理,对于自定义类型成员会去调用它的析构函数。

总结: 默认构造函数对基本类型不做处理,对自定义类型会调用它的默认构造函数,默认拷贝构造函数对基本类型是按照字节方式直接拷贝的,对自定义类型是调用其拷贝构造函数完成拷贝的,默认析构函数对基本类型不做处理,对自定义类型会调用它的析构函数。默认赋值运算符对基本类型成员变量以值的方式逐字节拷贝。而对自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

默认构造函数只有一个,析构函数只有一个,拷贝构造函数只有一个,赋值运算符重载只有一个

如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,什么事都不会干,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏

3、析构函数的调用

析构函数在对象消亡时被调用,以清理对象所申请的资源,那具体它在何时被调用呢?

析构函数主要在以下4种情况下会被调用:

对象生命周期结束,此时会自动调用析构函数。

#include<iostream>
using namespace std;
class Person {
public:
    Person() {
        cout << "调用了Person的构造函数" << endl;
    }
    ~Person() {
        cout << "调用了Person的析构函数" << endl;
    }
private:
    int name;
};
int main() {
    Person person;
    return 0;
}

运行结果如下图所示:

该运行结果说明,在对象的生命周期结束后,会自动调用对象的析构函数。

成员关系:对象car是对象person的成员,person的析构函数被调用时,对象car的析构函数也被调用。

#include <iostream>
using namespace std;
class Car {
public:
    Car() {
        cout << "调用了Car的构造函数" << endl;
    }
    ~Car() {
        cout << "调用了Car的析构函数" << endl;
    }
private:
    int name;
};
class Person {
public:
    Person() {
        cout << "调用了Person的构造函数" << endl;
    }
    ~Person() {
        cout << "调用了Person的析构函数" << endl;
    }
private:
    int name;
    Car car;
};
int main() {
    Person person;
    return 0;
}

先调用的是包含类的析构函数,然后调用的是成员对象的析构函数

继承关系:当Person是Student的父类,调用Student的析构函数,会调用Person的析构函数。

#include <iostream>
using namespace std;
class Person {
public:
    Person() {
        cout << "调用了Person的构造函数" << endl;
    }
    ~Person() {
        cout << "调用了Person的析构函数" << endl;
    }
private:
    int name;
};
class Student :public Person {
public:
    Student() {
        cout << "调用了Student的构造函数" << endl;
    }
    ~Student() {
        cout << "调用了Student的析构函数" << endl;
    }
private:
    int name;
    string no;
};
int main() {
    Student student;
    return 0;
}

先调用的是派生类的析构函数释放派生类的资源,然后调用的才是父类的析构函数释放父类成员所指向的资源

值得注意的是,如果派生类对象是new出来的对象,基类指针指向一个new生成的派生对象,通过delete销毁基类指针指向的派生类对象时,有以下两种情况:

1、 如果基类析构函数不是虚析构函数,则只会调用基类的析构函数,派生类的析构函数不被调用,此时派生类中申请的资源不被回收。

2、 如果基类析构函数为虚析构函数,则释放基类指针指向的对象时会调用基类及派生类析构函数,派生类对象中的所有资源被回收。

到此这篇关于C++析构函数内部工作机制详解的文章就介绍到这了,更多相关C++析构函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • cmake 学习笔记

    cmake 学习笔记

    这篇文章主要介绍了作者学习cmake 的相关资料与心得,有需要的小伙伴可以参考下
    2017-07-07
  • opencv检测直线方法之形态学方法

    opencv检测直线方法之形态学方法

    这篇文章主要为大家详细介绍了opencv检测直线方法之形态学方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • C++ com编程学习详解

    C++ com编程学习详解

    这篇文章主要介绍了C++ COM编程的学习过程,在C++中,可以使用抽象基类来实现COM接口,需要的朋友可以参考下,希望能够给你带来帮助
    2021-09-09
  • windows 下C++生成Dump调试文件与分析

    windows 下C++生成Dump调试文件与分析

    dump文件是C++程序发生异常时,保存当时程序运行状态的文件,是调试异常程序重要的方法,所以程序崩溃时,除了日志文件,dump文件便成了我们查找错误的最后一根救命的稻草,这篇文章主要介绍了windows 下C++生成Dump调试文件与分析,需要的朋友可以参考下
    2023-04-04
  • 详解C语言内核字符串拷贝与比较

    详解C语言内核字符串拷贝与比较

    本文将探索一下字符串的拷贝与比较,与应用层不同内核字符串拷贝与比较也需要使用内核专用的API函数,字符串的拷贝往往伴随有内核内存分配,我们将首先简单介绍内核如何分配堆空间,然后再以此为契机简介字符串的拷贝与比较
    2022-09-09
  • C++语言基础 命名空间

    C++语言基础 命名空间

    一个中大型软件往往由多名程序员共同开发,会使用大量的变量和函数,当有两个人都同时定义了一个名字相同的全局变量或函数的时候,若是把他们的代码整合在一块编译,此时编译器就会提示变量或函数重复定义,C++为了解决这个问题,便引用了命名空间(namespace)的概念
    2020-01-01
  • C语言中const和define的区别你了解嘛

    C语言中const和define的区别你了解嘛

    这篇文章主要为大家详细介绍了C语言中const和define的区别,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • 浅析iterator与指针的区别

    浅析iterator与指针的区别

    指针和iterator都支持减法运算,指针-指针得到的是两个指针之间的距离,迭代器-迭代器得到的是两个迭代器之间的距离
    2013-10-10
  • C++学习小结之语句

    C++学习小结之语句

    本文给大家汇总介绍了下C++中比较基础的知识--语句,常用的语句都有详细介绍和附上了相关示例,十分实用,有需要的小伙伴可以参考下
    2015-07-07
  • C++设置系统时间及系统时间网络更新的方法

    C++设置系统时间及系统时间网络更新的方法

    这篇文章主要介绍了C++设置系统时间及系统时间网络更新的方法,涉及网络程序设计与系统函数的使用,需要的朋友可以参考下
    2014-10-10

最新评论