当前位置: 首页 > news >正文

《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++》

相关文章:

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

文章目录 1、term7:Declare destructors virtual in polymorphic base classes2、总结3、相关面试题3.1 析构函数在什么情况下声明为虚函数 4、参考 1、term7:Declare destructors virtual in polymorphic base classes 带有多态性质的基类应该声明一个virtual析构函数&#x…...

Type-C一分二快充线智能分配方案

随着移动设备的普及和快充技术的迅猛发展&#xff0c;Type-C接口已成为众多手机、平板和笔记本电脑的标配。然而&#xff0c;在日常使用中&#xff0c;我们经常会遇到需要同时为多个设备充电的情况。这时&#xff0c;Type-C一分二快充线就显得尤为重要。为了更好地满足用户的充…...

利用python脚本,根据词条爬取百度图片(爬虫)

把广角&#xff0c;换成你的关键词就行 # -*- coding: utf-8 -*- """ Created on Wed Mar 29 10:17:50 2023 author: MatpyMaster """ import requests import os import redef get_images_from_baidu(keyword, page_num, save_dir):header {Us…...

java复原IP 地址(力扣Leetcode93)

复原IP 地址 力扣原题链接 问题描述 有效 IP 地址正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 ‘.’ 分隔。 例如&#xff1a;“0.1.2.201” 和 “192.168.1.1” 是有效 IP 地址&#xff0c…...

k8s的创建资源的流程图

背景 在k8s中创建资源需要经过几个流程的协作&#xff0c;包括认证模块&#xff0c;授权模块和资源管理模块的共同处理的结果 k8s的创建资源的流程图 第一步认证模块&#xff1a; k8s需要确保操作的客户端是合法的用户&#xff0c;并且不是仿冒的&#xff0c;也就是判断这个u…...

Android RecyclerView 滑动后选中的条目居中显示

话不多说先看效果: 实录效果视频如下 滚动居中 RecyclerView 在原有的RecyclerView 基础上操作&#xff0c;其他步骤不变&#xff0c;只是替换一下 manager 步骤 导入依赖 maven { url https://www.jitpack.io }//无限滚动implementation com.github.ZhaoChanghu:GalleryLayou…...

RPA-财务对账邮件应用自动化(客户对账机器人)

《财务对账邮件应用自动化》&#xff0c;将会使用邮箱的SMTP服务&#xff0c;小北把资源包绑定在这篇博客了 Uibot (RPA设计软件)———机器人的小项目友友们可以参考小北的课前材料五博客~ (本博客中会有部分课程ppt截屏,如有侵权请及请及时与小北我取得联系~&#xff09; …...

Delphi模式编程

文章目录 Delphi模式编程涉及以下几个关键方面&#xff1a;**设计模式的应用****Delphi特性的利用****实际开发中的实践** Delphi模式编程的实例 Delphi模式编程是指在使用Delphi这一集成开发环境&#xff08;IDE&#xff09;和Object Pascal语言进行软件开发时&#xff0c;采用…...

flutter 自定义弹窗封装弹窗----在弹窗内实现部分窗体生命周期

小部件组件 可以在里面加装其他事件如HTTP接口访问 import package:flutter/material.dart;///执行弹窗动画封装 class ExecutionDialog extends StatefulWidget {// final String? title;// final String? message;// final Function? onExecute;//// const ExecutionDial…...

go语言 私用仓库包下载

设置私有仓库&#xff0c;这样访问的时候&#xff0c;url前缀就不加proxy和sumdb go env -w GOPRIVATE"code.xxx.cn" go env -w GONOPROXY"code.xxx.cn" go env -w GONOSUMDB"code.xxx.cn" 设置取消安全认证 go env -w GOINSECURE"code…...

Math类

java.lang.Math 提供了一系列静态方法用于科学计算&#xff0c;常用方法如下&#xff1a; abs 绝对值 acos&#xff0c;asin&#xff0c;atan&#xff0c;cos&#xff0c;sin&#xff0c;tan 三角函数 sqrt 平方根 pow(double a,double b) a的b次幂 max(double a,double b) 取大…...

Git 入门教程

Git 入门教程 一、Git 是什么&#xff1f; Git 是一个开源的分布式版本控制系统&#xff0c;用于追踪代码的改动。它可以帮助开发者协同工作&#xff0c;管理项目中的代码版本。 二、安装 Git 在开始使用 Git 之前&#xff0c;你需要在你的计算机上安装 Git。你可以从 Git …...

Linux网络配置(超详细)

Linux网络配置大全 Linux网络配置一.网络地址配置网络地址查看–ifconfig使用网络配置命令设置网络接口参数-ifconfig禁用(临时)或者重新激活网卡设置虚拟网络接口 修改网络配置文件网络接口配置文件 IP命令详解OPTIONS选项OBJECT对象 ip link 二、获取和修改主机名hostname查看…...

[自研开源] 数据集成之分批传输 v0.7

开源地址&#xff1a;gitee | github 详细介绍&#xff1a;MyData 基于 Web API 的数据集成平台 部署文档&#xff1a;用 Docker 部署 MyData 使用手册&#xff1a;MyData 使用手册 试用体验&#xff1a;https://demo.mydata.work 交流Q群&#xff1a;430089673 介绍 本篇基于…...

用 AI 编程-释放ChatGPT的力量

最近读了本书&#xff0c;是 Sean A Williams 写的&#xff0c;感觉上还是相当不错的。一本薄薄的英文书&#xff0c;还真是写的相当好。如果你想看&#xff0c;还找不到&#xff0c;可以考虑私信我吧。 ChatGPT for Coders Unlock the Power of AI with ChatGPT: A Comprehens…...

【快速解决】解决谷歌自动更新的问题,禁止谷歌自动更新,如何防止chrome自动升级 chrome浏览器禁止自动升级设置方法

目录 问题描述 解决方法 1、搜索栏搜索控制面板 2、搜索&#xff1a;服务 ​编辑 3、点击Windows工具 4、点击服务 ​5、禁止谷歌更新 问题描述 由于我现在需要装一个谷歌的驱动系统&#xff0c;但是目前的谷歌驱动系统的版本都太旧了&#xff0c;谷歌自身的版本又太新了…...

【Leetcode每日一题】模拟 - 替换所有的问号(难度⭐)(42)

1. 题目解析 题目链接&#xff1a;1576. 替换所有的问号 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 遍历字符串&#xff1a;从左到右逐个处理字符。 处理问号字符&#xff1a;对于每个问号字符&#xff0c;我们需…...

再见 mysql_upgrade

在数据库管理的世界里&#xff0c;随着技术的不断进步和业务的不断发展&#xff0c;数据库的版本升级成为了一个不可避免的过程。 MySQL 作为业界领先的开源关系型数据库管理系统&#xff0c;其版本迭代与功能优化同样不容忽视。 而在这个过程中&#xff0c;升级工具就显得尤为…...

.NET Core教程:入门与实践实例

.NET Core教程&#xff1a;入门与实践实例 在信息技术飞速发展的今天&#xff0c;掌握一门高效的编程技术成为了每个开发者不可或缺的技能。在众多编程框架中&#xff0c;.NET Core以其跨平台、高性能和易扩展的特性&#xff0c;受到了广大开发者的青睐。本文将通过实例&#…...

docker环境配置过程中的常见问题

1、pull镜像问题 docker pull jenkins/jenkins:lts Using default tag: latest Trying to pull repository docker.io/library/centos ... Get https://registry-1.docker.io/v2/library/centos/manifests/latest: Get https://auth.docker.io/token?scoperepository%3Alibr…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

scikit-learn机器学习

# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...