《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一分二快充线智能分配方案
随着移动设备的普及和快充技术的迅猛发展,Type-C接口已成为众多手机、平板和笔记本电脑的标配。然而,在日常使用中,我们经常会遇到需要同时为多个设备充电的情况。这时,Type-C一分二快充线就显得尤为重要。为了更好地满足用户的充…...
利用python脚本,根据词条爬取百度图片(爬虫)
把广角,换成你的关键词就行 # -*- 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 地址正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。 例如:“0.1.2.201” 和 “192.168.1.1” 是有效 IP 地址,…...
k8s的创建资源的流程图
背景 在k8s中创建资源需要经过几个流程的协作,包括认证模块,授权模块和资源管理模块的共同处理的结果 k8s的创建资源的流程图 第一步认证模块: k8s需要确保操作的客户端是合法的用户,并且不是仿冒的,也就是判断这个u…...
Android RecyclerView 滑动后选中的条目居中显示
话不多说先看效果: 实录效果视频如下 滚动居中 RecyclerView 在原有的RecyclerView 基础上操作,其他步骤不变,只是替换一下 manager 步骤 导入依赖 maven { url https://www.jitpack.io }//无限滚动implementation com.github.ZhaoChanghu:GalleryLayou…...
RPA-财务对账邮件应用自动化(客户对账机器人)
《财务对账邮件应用自动化》,将会使用邮箱的SMTP服务,小北把资源包绑定在这篇博客了 Uibot (RPA设计软件)———机器人的小项目友友们可以参考小北的课前材料五博客~ (本博客中会有部分课程ppt截屏,如有侵权请及请及时与小北我取得联系~) …...
Delphi模式编程
文章目录 Delphi模式编程涉及以下几个关键方面:**设计模式的应用****Delphi特性的利用****实际开发中的实践** Delphi模式编程的实例 Delphi模式编程是指在使用Delphi这一集成开发环境(IDE)和Object Pascal语言进行软件开发时,采用…...
flutter 自定义弹窗封装弹窗----在弹窗内实现部分窗体生命周期
小部件组件 可以在里面加装其他事件如HTTP接口访问 import package:flutter/material.dart;///执行弹窗动画封装 class ExecutionDialog extends StatefulWidget {// final String? title;// final String? message;// final Function? onExecute;//// const ExecutionDial…...
go语言 私用仓库包下载
设置私有仓库,这样访问的时候,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 提供了一系列静态方法用于科学计算,常用方法如下: abs 绝对值 acos,asin,atan,cos,sin,tan 三角函数 sqrt 平方根 pow(double a,double b) a的b次幂 max(double a,double b) 取大…...
Git 入门教程
Git 入门教程 一、Git 是什么? Git 是一个开源的分布式版本控制系统,用于追踪代码的改动。它可以帮助开发者协同工作,管理项目中的代码版本。 二、安装 Git 在开始使用 Git 之前,你需要在你的计算机上安装 Git。你可以从 Git …...
Linux网络配置(超详细)
Linux网络配置大全 Linux网络配置一.网络地址配置网络地址查看–ifconfig使用网络配置命令设置网络接口参数-ifconfig禁用(临时)或者重新激活网卡设置虚拟网络接口 修改网络配置文件网络接口配置文件 IP命令详解OPTIONS选项OBJECT对象 ip link 二、获取和修改主机名hostname查看…...
[自研开源] 数据集成之分批传输 v0.7
开源地址:gitee | github 详细介绍:MyData 基于 Web API 的数据集成平台 部署文档:用 Docker 部署 MyData 使用手册:MyData 使用手册 试用体验:https://demo.mydata.work 交流Q群:430089673 介绍 本篇基于…...
用 AI 编程-释放ChatGPT的力量
最近读了本书,是 Sean A Williams 写的,感觉上还是相当不错的。一本薄薄的英文书,还真是写的相当好。如果你想看,还找不到,可以考虑私信我吧。 ChatGPT for Coders Unlock the Power of AI with ChatGPT: A Comprehens…...
【快速解决】解决谷歌自动更新的问题,禁止谷歌自动更新,如何防止chrome自动升级 chrome浏览器禁止自动升级设置方法
目录 问题描述 解决方法 1、搜索栏搜索控制面板 2、搜索:服务 编辑 3、点击Windows工具 4、点击服务 5、禁止谷歌更新 问题描述 由于我现在需要装一个谷歌的驱动系统,但是目前的谷歌驱动系统的版本都太旧了,谷歌自身的版本又太新了…...
【Leetcode每日一题】模拟 - 替换所有的问号(难度⭐)(42)
1. 题目解析 题目链接:1576. 替换所有的问号 这个问题的理解其实相当简单,只需看一下示例,基本就能明白其含义了。 2.算法原理 遍历字符串:从左到右逐个处理字符。 处理问号字符:对于每个问号字符,我们需…...
再见 mysql_upgrade
在数据库管理的世界里,随着技术的不断进步和业务的不断发展,数据库的版本升级成为了一个不可避免的过程。 MySQL 作为业界领先的开源关系型数据库管理系统,其版本迭代与功能优化同样不容忽视。 而在这个过程中,升级工具就显得尤为…...
.NET Core教程:入门与实践实例
.NET Core教程:入门与实践实例 在信息技术飞速发展的今天,掌握一门高效的编程技术成为了每个开发者不可或缺的技能。在众多编程框架中,.NET Core以其跨平台、高性能和易扩展的特性,受到了广大开发者的青睐。本文将通过实例&#…...
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…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
[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 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
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开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
