C++ 关于虚函数的一些碎碎念

devtools/2025/1/1 12:08:39/

(一)有子类继承的基类的析构函数必须是virtual

如果基类的析构函数不是虚函数那也不会报错,但是在工程上是非常危险的,方法实现了多态,析构也要实现多态
否则容易出现这种类似问题

实现一个基类A

class A
{
public:A();~A();int a1;
private:void* buf;
};
A::A():buf(malloc(16))
{cout << "Creat A buf " << endl;
}
A::~A() 
{cout << "Drop A buf " << endl;free(buf); 
}

B1和B2继承A类


class B1 : public A
{
public:B1();~B1();
private:void* ch_buf;
};
B1::B1() :ch_buf(malloc(16))
{cout << "Creat B1" << endl;
}
B1::~B1()
{free(ch_buf);cout << "Drop B1 " << endl;
}class B2 : public A
{
public:B2();~B2();
private:void* ch_buf;
};
B2::B2() : ch_buf(malloc(16))
{cout << "Creat B2" << endl;
}
B2::~B2()
{free(ch_buf);cout << "Drop B2 " << endl;
}

场景模拟,由于构造哪个类由传参决定,所以返回的是基类

A* Creat1(int c)
{if (c == 1)return new B1;if (c == 2)return new B2;return nullptr;
}void Drop1(A* tm)
{delete tm;
}int main(int argc, char* argv[])
{auto test = Creat1(2);Drop1(test);return 0;
}

此时就会出现问题,Drop函数里delete只调用了基类的析构函数,但是没有调用子类的析构函数,造成内存泄露
在这里插入图片描述
所以为了防止这种情况产生,将基类的虚构函数设置成virtual就能调用到子类的虚构函数
在这里插入图片描述

(二)没有子类继承的类应当设置成final,当基类的虚函数被设置成final,则该虚函数不能在子类被重写,类被设置final则无法继承

基类的虚函数被设置成final,则该虚函数不能在子类被重写
在这里插入图片描述
类被设置final则无法继承
在这里插入图片描述

(三)虚函数表指针、虚函数表、虚函数之间的关系

在构造函数构造对象时创建虚函数表,并将虚函数表指针指向虚函数表,虚函数表就是一个指针数组,存放的是虚函数的地址,所以虚函数表指针是一个指针数组指针,所以当一个类里有虚构函数的时候,这个类就会多出一个指针的大小 【(32位系统)4字节,(64位系统)8字节】

当一个子类实例化时会先调用父类的构造函数,此时会创建一个父类的虚函数表,然后在调用子类的构造函数此时会把父类的虚函数表复制给子类,注意:子类和父类的虚函数表是不同的地址,但是里面的虚函数表里的指针指向同一个地址
如果子类重写了父类的虚函数,则会将重写的函数地址替换掉从基类继承的虚函数地址。

(四)override的作用

当子类重写了父类的虚函数,即使不加override也不会报错,但是加上override能提示coder,这个函数是重写自父类中的。

加上override之后,我们在重写的时候,编译器会知道我们是在重写虚函数,如果我们函数的名称写错了,编译器就会报错。

(五)通过虚继承来避免多继承造成的二义性

还是上面的类 A ; B1 : public A ; B2 : public A;
如果此时再来一个类继承自B1 和 B2

class C final:public B1, public B2
{
public:int c1;
};

那我们要怎么知道c.A::a1调用的是什么
通过地址打印可以看到
c.A::a1c.B1::a1是同一个地址
c.B2::a1是另一个地址
在这里插入图片描述
可是如果我们在继承的时候使用虚继承

class B1 : virtual public A
class B2 : virtual public A

此时就可以发现
c.A::a1c.B1::a1c.B2::a1都是同一个地址
在这里插入图片描述


http://www.ppmy.cn/devtools/146583.html

相关文章

Nginx 常用安全头

Web 应用中配置 HTTP 安全响应头是提升网站安全性的重要一步。合理配置 Nginx 的安全头&#xff0c;可以抵御常见的安全威胁&#xff08;如 XSS、点击劫持、MIME 类型嗅探等&#xff09;&#xff0c;增强用户隐私保护和传输安全性。 常见的 HTTP 安全头及其作用 1. Content-Se…

【Leetcode】3046. 分割数组

文章目录 题目思路代码复杂度分析时间复杂度空间复杂度 结果 题目 题目链接&#x1f517; 给你一个长度为 偶数 的整数数组 n u m s nums nums 。你需要将这个数组分割成 n u m s 1 nums1 nums1 和 n u m s 2 nums2 nums2 两部分&#xff0c;要求&#xff1a; n u m s 1. l…

人工智能与物联网:从智慧家居到智能城市的未来蓝图

引言&#xff1a;未来已来&#xff0c;智能化的世界 想象一下&#xff0c;一个早晨&#xff0c;智能闹钟根据你的睡眠状态自动调整叫醒时间&#xff0c;咖啡机早已备好热腾腾的咖啡&#xff0c;窗帘缓缓拉开&#xff0c;迎接清晨的阳光。这不是科幻小说中的场景&#xff0c;而是…

基于单片机的智能递口罩机器人设计

本设计是一款智能递口罩机器人&#xff0c;主控器采用STM32单片机&#xff0c;ESP32协同控制&#xff0c;在支持MicroPython的OpenMV机器视觉模块的控制下&#xff0c;实现人脸搜索与识别&#xff0c;进而控制小车的运动及机械臂递口罩动作。这款机器人拥有温湿度传感器&#x…

C# 超高速高性能写日志

原理 使用列队先缓存到内存&#xff0c;独立线程从列队中使用log4net写到磁盘上。 日志写入列队 public void EnqueueMessage(string message, FlashLogLevel level, Exception ex null) {if ((level FlashLogLevel.Debug && _log.IsDebugEnabled)|| (level Flas…

mysql数据库中,一棵3层的B+树,假如数据节点大小是1k,那这棵B+可以存多少条记录(2100万的由来)

在MySQL中&#xff0c;3层的B树可以存储的数据量取决于多个因素&#xff0c;包括页大小、每行数据的大小以及索引项的大小。以下是一个详细的计算过程&#xff1a; 一、假设条件 页大小&#xff1a;在InnoDB存储引擎中&#xff0c;B树的每个节点&#xff08;页&#xff09;大…

python实战项目55:多线程爬取笔趣阁小说

python实战项目55:多线程爬取笔趣阁小说 一、明确需求二、单本小说下载三、使用concurrent.futures线程池模块下载并实现文件合并四、使用threading模块实现多线程下载并合并文件一、明确需求 需求是使用多线程爬取笔趣阁网站小说的所有章节内容并保存,多线程分别使用了conc…

如何在 Ubuntu 22.04 上安装 phpMyAdmin

简介 PHPMyAdmin 是在 Ubuntu 22.04 上管理 MySQL 数据库的绝佳选择。它是一个流行的工具&#xff0c;拥有简单、高效且用户友好的基于 Web 的界面&#xff0c;让你能够轻松地管理 MySQL 数据库。因此&#xff0c;许多开发人员、数据库管理员和网站所有者都信任 PHPMyAdmin 来…