类和对象【三】析构函数和拷贝构造函数

析构函数

析构函数的定义

析构函数(destructor) 与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数。
析构函数往往用来做“清理善后” 的工作(例如:在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。
来自百度百科


析构函数的作用

主要作用

完成对 对象 申请来的资源清理工作

析构函数可以用于释放对象在使用过程中分配的资源,如动态分配的内存打开的文件句柄网络连接
这样可以避免资源泄漏,确保程序的稳定性和资源的有效管理


次要作用

可以根据析构函数在对象生命周期结束时编译器自动调用这个特点,在析构函数中完成一些特殊操作

【例如:在类里创建一个static修饰的成员变量,让它在调用构造函数时++,调用拷贝构造时++,调用析构函数时- -,这样就可以实时记录还有多少个“存活”】


析构函数的特点

  1. 析构函数名是在类名前加上字符 。

  2. 析构函数没有参数也返回值类型。

  3. 对象生命周期结束时编译器自动调用析构函数

  4. 因为析构函数没有参数,所以析构函数不能重载,所以一个类只有一个析构函数

  5. 如果没有显式实现析构函数,编译器会自动生成一个默认的析构函数
    这个默认的析构函数的特点是:
    1 对内置类型成员变量不做处理无论内置类型是否申请了资源,都不处理
    2 对自定义类型 就自动调用所属的类的析构函数

    在这里插入图片描述


拷贝构造函数

拷贝构造函数的定义

拷贝构造函数是C++中一种构造函数的重载,它的作用是:用已有对象创建一个新对象,并将已存在的同类对象的数据成员拷贝到新对象中。
拷贝构造函数的形参是一个对该已有对象的引用,并且通常会被声明为const,以防止通过引用修改原对象


拷贝构造函数的作用

主要作用

用已有对象创建一个新对象,并将已存在的同类对象的数据成员拷贝到新对象中。

次要作用

可以根据拷贝构造函数在创建对象时编译器自动调用这个特点,在析构函数中完成一些特殊操作

【例如:在类里创建一个static修饰的成员变量,让它在调用构造函数时++,调用拷贝构造时++,调用析构函数时- -,这样就可以实时记录还有多少个“存活”】


拷贝构造函数的特点

  1. 拷贝构造函数是构造函数的重载,所以它也是构造函数,它只能用于创建新对象不能用于两个已经创建对象之间的拷贝

  2. 拷贝构造函数只有一个参数,参数必须是该类的对象的引用
    如果第不是该类的对象的引用或者有多个参数,那它就不是拷贝构造,而是普通构造函数

  3. 拷贝构造的第一个参数必须是该类对象的引用,不能是该类对象本身
    因为传值调用时,形参是实参的临时拷贝,所以规定了传对象本身的时候要调用拷贝构造函数
    这样的话,如果一个类的拷贝构造的参数是该类对象本身,那么调用拷贝构造,就要向传参,而传对象本身的时候又要调用拷贝构造,调用拷贝构造又要先传参数………
    就会无穷递归

    在这里插入图片描述

  4. 如果没有显式的写出拷贝构造函数,编译器会自动生成一个默认的拷贝构造函数
    这个默认的拷贝构造函数的特点是:
    1,对内置类型【int char等】成员直接浅拷贝
    2,对自定义类型【class,struct等】成员就调用该自定义类型自己的拷贝构造


浅拷贝和深拷贝

浅拷贝

把一个对象中的成员中存储的值直接拷贝给另一个对象的对应成员就是浅拷贝

下图就是一个浅拷贝的拷贝构造函数
在这里插入图片描述
浅拷贝的缺点:
如果对象的成员申请了资源,那么成员中存储的往往是那片资源的地址

此时如果使用浅拷贝,就直接把这个成员中存储的地址给了另一个对象的成员,那么这两个对象的成员就会指向同一片资源,这样不仅使用不方便,析构释放资源的时候还会多次释放,导致程序崩溃


在这里插入图片描述
上图相当于
在这里插入图片描述

解决办法也很简单,就让两个对象指向的资源不是同一片就可以了,也就是深拷贝


深拷贝

当对象有成员指向资源的时候,如果要把这个对象拷贝给其他对象就要用深拷贝

深拷贝很简单,分2步

  1. 先“拷贝”指向资源的数据结构
    即如果成语指向的资源是一条节点都在堆区的链表,这个时候接受拷贝的对象的成员就也要在堆区申请空间,创建节点,制造出于那条链表相同的节点个数和节点大小

  2. 再拷贝数据
    即把指向资源中存储的有效数据拷贝个另一个成员

例 上面例举的浅拷贝时的问题就可以这样解决
在这里插入图片描述


拷贝构造函数的调用场景

  1. 使用已经创建好的对象去创建新的对象
    在这里插入图片描述

  2. 函数的参数类型是类的对象
    因为形参是实参的临时拷贝,所以当参数是类的对象的时候,就要创建一个对象作为形参,当调用该函数的时候,编译器自动调用拷贝构造

    在这里插入图片描述

3.函数的返回值是类的对象
因为在函数调用结束后函数的栈区内存会被回收,如果返回的是在函数内创建的对象,函数调用结束后,这个对象的生命周期也会结束,空间会被释放
为了防止这样的情况,就需要创建一个临时对象来存储可能被销毁的对象的数据,这个临时对象就要编译器自动调用拷贝构造函数创建

最近更新

  1. TCP协议是安全的吗?

    2024-04-28 06:54:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-28 06:54:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-28 06:54:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-28 06:54:03       20 阅读

热门阅读

  1. android layout 的文件夹可以创建子文件夹吗

    2024-04-28 06:54:03       14 阅读
  2. Android 生成二维码

    2024-04-28 06:54:03       11 阅读
  3. 基于单片机的配电网故障自适应监控系统设计

    2024-04-28 06:54:03       13 阅读
  4. Redis面试题超详细(2024最新)

    2024-04-28 06:54:03       20 阅读
  5. Ubuntu22.04安装Nvidia 550驱动和CUDA toolkit 12.4.1

    2024-04-28 06:54:03       14 阅读
  6. 练习题(2024/4/27)

    2024-04-28 06:54:03       13 阅读
  7. iptables动作

    2024-04-28 06:54:03       24 阅读
  8. 【Kotlin】select简介

    2024-04-28 06:54:03       15 阅读
  9. 打地鼠游戏(python期中)

    2024-04-28 06:54:03       17 阅读
  10. 常用设计模式简介

    2024-04-28 06:54:03       9 阅读