《Effective C++》《构造/析构/赋值运算——7、为多态基类声明virtual析构函数》

文章目录

  • 1、term7:Declare destructors virtual in polymorphic base classes
  • 2、总结
  • 3、相关面试题
    • 3.1 析构函数在什么情况下声明为虚函数
  • 4、参考

1、term7:Declare destructors virtual in polymorphic base classes

带有多态性质的基类应该声明一个virtual析构函数;如果class内带有任何virtual函数,它就该拥有一个virtual析构函数;
举个反面栗子:

#include <iostream>  
#include <memory>  
  
// 基类 TimeKeeper  
class TimeKeeper {  
public:  
    TimeKeeper() {  
        std::cout << "TimeKeeper constructor called." << std::endl;  
    }  
    ~TimeKeeper() {  
        std::cout << "TimeKeeper destructor called." << std::endl;  
    }  
    // 假设 TimeKeeper 类有其他成员或方法...  
    // ...  
};  
  
// 派生类 AtomicClock  
class AtomicClock : public TimeKeeper {  
public:  
    AtomicClock() {  
        std::cout << "AtomicClock constructor called." << std::endl;  
    }  
    ~AtomicClock() {  
        std::cout << "AtomicClock destructor called." << std::endl;  
    }  
    // 原子钟特有的成员或方法...  
    // ...  
};  
  
// 派生类 WaterClock  
class WaterClock : public TimeKeeper {  
public:  
    WaterClock() {  
        std::cout << "WaterClock constructor called." << std::endl;  
    }  
    ~WaterClock() {  
        std::cout << "WaterClock destructor called." << std::endl;  
    }  
    // 水钟特有的成员或方法...  
    // ...  
};  
  
// 派生类 WristWatch  
class WristWatch : public TimeKeeper {  
public:  
    WristWatch() {  
        std::cout << "WristWatch constructor called." << std::endl;  
    }  
    ~WristWatch() {  
        std::cout << "WristWatch destructor called." << std::endl;  
    }  
    // 腕表特有的成员或方法...  
    // ...  
};  
  
// 工厂函数,返回 TimeKeeper 派生类的指针  
TimeKeeper* getTimeKeeper() {  
    // 这里我们简单地返回 AtomicClock 的实例,  
    // 但在实际中,可以根据需要返回不同派生类的实例。  
    return new AtomicClock();  
}  
  
int main() {  
    // 从 TimeKeeper 继承体系获取一个动态分配的对象  
    TimeKeeper* ptk = getTimeKeeper();  
      
    // 使用 ptk 指向的对象...  
    // ...  
      
    // 释放内存,避免资源泄漏  
    delete ptk;  
      
    return 0;  
}

输出内容如下:

TimeKeeper constructor called.
AtomicClock constructor called.
TimeKeeper destructor called.

先来说一下这为什么是一个反面教材,第二个getTimeKeeper()返回的指针指向一个derived对象,而那个对象却被一个base class指针删除。因为析构的顺序,是先析构derived对象,然后再析构base对象。通过基类指针删除子类对象时,仅调用了基类的析构函数,而导致子类的Derived成分没有销毁,产生了一个“局部销毁”对象,这将导致资源泄漏、破坏数据结构、在调试器上浪费时间的严重后果。消除这个问题的做法很简单:给base class内声明一个virtual对象,这样删除对象,就会“如你所愿”。
举个栗子:

#include <iostream>  
#include <memory>  
  
// 基类 TimeKeeper  
class TimeKeeper {  
public:  
    TimeKeeper() {  
        std::cout << "TimeKeeper constructor called." << std::endl;  
    }  
    virtual ~TimeKeeper() {  
        std::cout << "TimeKeeper destructor called." << std::endl;  
    }  
    // 假设 TimeKeeper 类有其他成员或方法...  
    // ...  
};  
  
// 派生类 AtomicClock  
class AtomicClock : public TimeKeeper {  
public:  
    AtomicClock() {  
        std::cout << "AtomicClock constructor called." << std::endl;  
    }  
    ~AtomicClock() {  
        std::cout << "AtomicClock destructor called." << std::endl;  
    }  
    // 原子钟特有的成员或方法...  
    // ...  
};  
  
// 派生类 WaterClock  
class WaterClock : public TimeKeeper {  
public:  
    WaterClock() {  
        std::cout << "WaterClock constructor called." << std::endl;  
    }  
    ~WaterClock() {  
        std::cout << "WaterClock destructor called." << std::endl;  
    }  
    // 水钟特有的成员或方法...  
    // ...  
};  
  
// 派生类 WristWatch  
class WristWatch : public TimeKeeper {  
public:  
    WristWatch() {  
        std::cout << "WristWatch constructor called." << std::endl;  
    }  
    ~WristWatch() {  
        std::cout << "WristWatch destructor called." << std::endl;  
    }  
    // 腕表特有的成员或方法...  
    // ...  
};  
  
// 工厂函数,返回 TimeKeeper 派生类的指针  
TimeKeeper* getTimeKeeper() {  
    // 这里我们简单地返回 AtomicClock 的实例,  
    // 但在实际中,可以根据需要返回不同派生类的实例。  
    return new AtomicClock();  
}  
  
int main() {  
    // 从 TimeKeeper 继承体系获取一个动态分配的对象  
    TimeKeeper* ptk = getTimeKeeper();  
      
    // 使用 ptk 指向的对象...  
    // ...  
      
    // 释放内存,避免资源泄漏  
    delete ptk;  
      
    return 0;  
}

输出内容如下:

TimeKeeper constructor called.
AtomicClock constructor called.
AtomicClock destructor called.
TimeKeeper destructor called.

virtual函数的目的就是允许derived class的实现得以客制化;任何class只要带有virtual函数几乎确定应该也有一个virtual析构函数。

2、总结

书山有路勤为径,学海无涯苦作舟。

3、相关面试题

3.1 析构函数在什么情况下声明为虚函数

在面向对象的编程中,析构函数应该被声明为虚函数(virtual)当且仅当类被用作基类,并且你打算通过基类指针来删除派生类对象时(只有这么一个情况下)。这是为了防止发生所谓的“切片”问题(slicing problem)和确保正确的对象析构顺序。
当有一个基类指针指向一个派生类对象,并通过这个基类指针来调用 delete 操作符时,如果基类的析构函数不是虚函数,那么只会调用基类的析构函数,而不会调用派生类的析构函数。这会导致派生类部分的对象没有被正确地销毁,可能造成资源泄漏和其他问题。
通过将基类的析构函数声明为虚函数,可以确保当通过基类指针删除对象时,先调用派生类的析构函数,然后调用基类的析构函数,从而正确地释放所有资源。

4、参考

4.1 《More Effective C++》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/499437.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

stable diffusion如何下载模型?

文件夹里面有14个模型&#xff0c;把这些模型复制到SD文件夹里 具体位置:SD文件>models>ControlNet

【C/C++】从零开始认识C++历程-启航篇

文章目录 &#x1f4dd;前言&#x1f320; 什么是C&#xff1f;&#x1f309;C的发展史 &#x1f320;C的重要性&#x1f309;语言的使用广泛度 &#x1f320;在工作领域&#x1f309; 岗位需求 &#x1f320;相关笔试题&#x1f309; 公司怎样面试C &#x1f6a9;总结 &#x…

蓝桥杯 - 小明的背包1(01背包)

解题思路&#xff1a; 本题属于01背包问题&#xff0c;使用动态规划 dp[ j ]表示容量为 j 的背包的最大价值 注意&#xff1a; 需要时刻提醒自己dp[ j ]代表的含义&#xff0c;不然容易晕头转向 注意越界问题&#xff0c;且 j 需要倒序遍历 如果正序遍历 dp[1] dp[1 - vo…

java的多态和final关键字

多态&#xff1a; 多态分为对象多态&#xff0c;行为多态 多态的前提&#xff1a; 有继承/实现关系&#xff1b;存在父类引用子类对象&#xff1b;存在方法重写&#xff1b; 注意&#xff1a;多态是对象&#xff0c;行为的多态&#xff0c;java的成员变量不谈多态 这是我写…

将Knife4j所展示请求参数和响应参数转化为TS类型声明

目标&#xff1a;在浏览器控制台输入js代码&#xff0c;将读取页面所展示的请求参数和响应参数&#xff0c;将他们转化为TS的类型声明&#xff0c;并在控制台中输出出来。 将Knife4j所展示请求参数和响应参数转化为TS类型声明 1 找到所需要的元素节点2 转化元素节点3 封装成函…

本地部署的stable diffusion 如何更新controlnet?

stable diffusion 未启动状态 点击“版本管理” 点击“扩展” 找到controlnet&#xff0c;点击右边的“更新”按钮 完成&#xff01;

【软考---系统架构设计师】特殊的操作系统介绍

目录 一、嵌入式系统&#xff08;EOS&#xff09; &#xff08;1&#xff09;嵌入式系统的特点 &#xff08;2&#xff09;硬件抽象层 &#xff08;3&#xff09;嵌入式系统的开发设计 二、实时操作系统&#xff08;RTOS&#xff09; &#xff08;1&#xff09;实时性能…

总结TCP各类知识点

前言 本篇博客博主将详细地介绍TCP有关知识点&#xff0c;坐好板凳发车啦~ 一.TCP特点 1.有连接 TCP传输的过程中类似于打电话的各个过程 2.可靠传输 通过TCP自身的多种机制来保证可靠传输 3.面向字节流 内容是以字节的方式来进行发送与接收 4.缓冲区 TCP有接收缓冲区…

网络安全接入认证-802.1X接入说明

介绍 802.1X是一个网络访问控制协议&#xff0c;它可以通过认证和授权来控制网络访问。它的基本原理是在网络交换机和认证服务器之间建立一个安全的通道&#xff0c;并要求客户端提供身份验证凭据。如果客户端提供的凭据是有效的&#xff0c;交换机将开启端口并允许访问。否则&…

服务器被挖矿了怎么办,实战清退

当我们发现服务器资源大量被占用的时候&#xff0c;疑似中招了怎么办 第一时间重启服务是不行的&#xff0c;这些挖矿木马一定是会伴随着你的重启而自动重启&#xff0c;一定时间内重新霸占你的服务器资源 第一步检查高占用进程 top -c ps -ef 要注意这里%CPU&#xff0c;如果…

1.8.1 摄像机

一、摄像机 OpenGL本身没有摄像机的概念&#xff0c;但是我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机&#xff0c;产生一种我们在移动的感觉&#xff0c;而不是场景在移动。 本节将会讨论如何在OpenGL中配置一个摄像机&#xff0c;让你能够在3D场景中…

Web APIs

文章目录 Web APIs1. DOM1.1 介绍DOM 树DOM 节点document 1.2 获取DOM对象1.3 操作元素内容1.4 操作元素属性常用属性修改控制样式属性操作表单元素属性自定义属性 1.5 间歇函数1.6 事件事件监听事件类型事件处理程序 1.7 事件类型鼠标事件键盘事件焦点事件文本框输入事件 1.8 …

【JavaScript】数组 ③ ( JavaScript 数组长度 | 修改数组长度 | 数组案例 )

文章目录 一、JavaScript 数组长度1、数组长度2、修改数组长度 二、数组案例1、求数组元素平均值2、求数组元素最大值 一、JavaScript 数组长度 1、数组长度 在 JavaScript 中 , 数组长度 可以通过 数组变量的 length 属性 获取 , 该属性 返回 数组中的元素数量 , 也就是 数组长…

【Java】ArrayList数组的扩容机制 jdk1.8

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 ArrayList和普通数组不同&#xff0c;ArrayList支持动态扩容&#xff0c;那么ArrayList到底是如何扩容的呢&#xff1f;你又是否知道ArrayList数组的初始长度是多少呢&#xff1f; 在开始介绍之前&#xff0c;我们要先介…

【IDEA+通义灵码插件】实现属于你的大模型编程助手

1、前言 大模型到底该以一种什么方式落地&#xff0c;从而嵌入我们的工作当中&#xff0c;助力我们工作效率的提升&#xff0c;其实最好的方式也许就是虚拟助手的方式&#xff0c;就像钢铁侠的"贾维斯"一样&#xff0c;随叫随到能回答问题&#xff0c;能自动的解决一…

python函数参数中独立星号*的作用

python函数中间有一个&#xff08;&#xff09;分隔&#xff0c;星号后面为*命名关键字参数&#xff0c;星号本身不是参数**。命名关键字参数&#xff0c;在函数调用时必须带参数名字进行调用。如下例子&#xff1a;

【Golang入门教程】Go语言变量的初始化

文章目录 强烈推荐引言举例多个变量同时赋值总结强烈推荐专栏集锦写在最后 强烈推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站:人工智能 推荐一个个人工作&#xff0c;日常中比较常…

深入剖析哈希表:以Java中的HashMap为例

哈希表是一种非常高效的数据结构&#xff0c;它允许我们以接近常数的时间复杂度进行插入、删除和查找操作。在Java中&#xff0c;HashMap类是实现哈希表的一个非常流行的工具。本文将深入探讨哈希表的工作原理&#xff0c;并通过Java代码来展示HashMap的使用和内部机制。 一、…

Linux 动静态库的制作,使用和加载

Linux 动静态库的制作,使用和加载 一.前置说明1.mylib.h2.mylib.c3.mymath.h mymath.c4.如何制作库 二.动静态库的制作1.静态库的制作1.制作2.使用一下静态库,验证是否成功打包 2.动态库的制作1.编译.c源文件文件生成.o目标文件2.打包生成动态库3.编写makefile文件,自动化制作动…

基于Springboot+vue的鲜花销售商城网站

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;鲜花销售商城当然也不能排除在外。鲜花销售商城是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#x…
最新文章