当前位置: 首页 > 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…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

Webpack性能优化:构建速度与体积优化策略

一、构建速度优化 1、​​升级Webpack和Node.js​​ ​​优化效果​​&#xff1a;Webpack 4比Webpack 3构建时间降低60%-98%。​​原因​​&#xff1a; V8引擎优化&#xff08;for of替代forEach、Map/Set替代Object&#xff09;。默认使用更快的md4哈希算法。AST直接从Loa…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...