c++ virtual 关键字 override 关键字

2023-11-16

1.什么是virtual

virtual是一个C++的修饰符,用于修饰函数,被修饰的函数称为虚函数。

2.为什么需要?

在C++中,我们都知道要实现多态有2种方法,一种是重写,一种是重载,重载是横向的,意思是只发生在同一个类中,根据函数的参数个数,类型不同从而实现重载,而重写则是纵向的,发生在继承中,子类函数覆盖父类函数,父类指针指向子类实体时,应该实现运行时多态。

3.通常用在什么情形?

1.作为基类的类的析构函数
如果一个类做为父类,然后它被别人继承,当用子类指针指向子类时不会出现任何的问题,但是如果用父类指针指向子类时,若没有加析构函数,那只会析构父类的析构函数,这时我们需要用virtual修饰父类的析构函数。

2.需要实现多态的函数
若一个函数需要实现多态,即运行时多态。

1、定义在函数中的对象,在函数调用结束时,在对象释放前调用析构函数
2、定义在函数中的static对象,在函数调用结束时,不调用对象析构函数,在mian函数结束时,会调用对象析构函数
3、定义全局对象或者static全局对象,程序执行流程离开其作用域时,调用对象析构函数
4、用new运算法生成的对象,调用delete运算法释放该对象时,先调用对象析构函数

重写 覆盖的几个函数必须函数名、参数、返回值都相同;

case 1

#include <iostream>
using namespace std;
 
class Base{
  public:
    ~Base() {cout<<"~B"<<endl;}
};
 
class Derived:public Base{
  public:
    ~Derived() {cout<<"~D"<<endl;}
};
 
int main (){
  Base *b = new Derived; //注意这里
  delete b;
}

子类指向父类,查看打印日志:

~B

可见删除子类指向父类的指针,代表delete只删除了父类,并没有删除子类的相关内存,会产生内存泄漏。所以在多态的使用过程中,delete(指向父类的子类对象)需要格外注意是否会没有释放子类或者父类。

case 2

#include <iostream>
using namespace std;
 
class Base{
  public:
    ~Base() {cout<<"~B"<<endl;}
};
 
class Derived:public Base{
  public:
    ~Derived() {cout<<"~D"<<endl;}
};
 
int main (){
  Derived *d = new Derived; //注意这里
  delete d;
}

子类指向子类指针,查看打印日志:

~D

~B

可以得知,delete子类的析构顺序:是先释放子类,在释放父类。

这是我们期望的结果,所以没啥问题。。。

case 3
对于case 1当中的情况,如何去规避呢? 需要将父类的析构函数定义为virtual

#include <iostream>
using namespace std;
 
class Base{
  public:
    virtual ~Base() {cout<<"~B"<<endl;}
};
 
class Derived:public Base{
  public:
    ~Derived() {cout<<"~D"<<endl;}
};
 
int main (){
  Base *d = new Derived; //注意这里
  delete d;
}

打印结果:

~D

~B

原理:delete d; //d虽然为Base*,但是它的析构函数是virtual的,所以它调用析构函数,其实是调用虚函数表中的函数。而Base* d指向的虚函数表在内存中其实是new Derived类的虚函数表。所以通过Base类的virtual函数 + Base* d指向派生类(Derived)对象,从而使得在调用delete d的时候实际上是调用的是delete Derived* d的意思,也就是第二种情况的效果。

4.延伸:虚函数/纯虚函数、override 关键字


虚函数分类:

  • 纯虚函数要求派生类必须重写
  • 普通虚函数有默认实现但可以被派生类重写

使用override和default关键字可以明确指示函数的重写和默认实现。

示例

virtual void Draw() const = 0; // 1) 纯虚函数
virtual void Error(const string& msg); // 2) 普通虚函数
virtual void Error(const string& msg) override = default;	// 错误,= default只能用在默认构造、拷贝构造等默认函数中
virtual void Error(const string& msg) override;

9.问题汇总

9.1 非虚函数和虚函数都可以重写,那区别是啥?

virtual void Error(const string& msg);
void Error(const string& msg);

对于上述两个函数声明:

  1. virtual void Error(const string& msg); 是一个虚函数的声明。虚函数是在基类中声明并带有 virtual 关键字的函数,它可以被派生类重写(覆盖)。虚函数可以通过基类指针或引用来实现动态绑定,即在运行时根据对象的实际类型来调用相应的函数。

  2. void Error(const string& msg); 是一个非虚函数的声明。非虚函数是在类中声明但没有 virtual 关键字修饰的函数。非虚函数在派生类中也可以被重写,但它们不具备动态绑定的特性。当通过基类指针或引用调用非虚函数时,将根据指针或引用的类型来确定调用的函数,而不考虑指向的对象的实际类型。

因此,虚函数和非虚函数的区别在于它们的调用方式和多态性的支持。虚函数可以通过基类指针或引用实现多态性,而非虚函数的调用始终与指针或引用的类型相符,不会发生动态绑定。

9.2 基类虚函数/纯虚函数、子类有没有 override 的区别


override 的作用:用于显式地标识子类中的函数重写父类的虚函数(包括纯虚函数)的一种方式。

使用 override 关键字有以下几个好处:

  1. 明确表明意图:使用 override 关键字可以明确表示子类中的函数是对父类虚函数的重写,可以增强代码的可读性和可维护性

  2. 静态检查错误:使用 override 关键字可以让编译器在子类中发现对父类虚函数的重写错误,例如函数签名不匹配等。

  3. 避免意外创建新函数:如果没有使用 override 关键字,子类可能会意外地创建一个新的函数,而不是重写父类的虚函数。

虽然使用 override 关键字可以提高代码的可读性和安全性,但在某些情况下,如果没有使用 override 关键字,编译器仍然可以进行正确的函数匹配。所以使用 override 关键字是一种良好的编程实践,但不是强制要求。

注意:如果基类不是虚函数,则无法使用override修饰。

9.2 override 和 override = default

在C++中,只有特殊成员函数(默认构造函数、拷贝构造函数、移动构造函数、析构函数和赋值运算符)才能使用 = default; 语法进行默认。普通的成员函数不能使用 = default; 进行默认。

class Shape {
   public:
      Shape(){}
      virtual ~Shape(){  cout << "Parent class ~Shape :" <<endl; }
};
class Rectangle: public Shape{
   public:
      Rectangle(){ }
      virtual ~Rectangle() override = default; 

10.综合实例

#include <iostream> 
using namespace std;
 
class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      virtual ~Shape(){  cout << "Parent class ~Shape :" <<endl; }
      int area()
      {
         cout << "Parent class area :" <<endl;
         return 0;
      }
      virtual void test()       // 普通虚函数
      {
         cout << "Parent class test :" <<endl;
      }
      virtual void test2() = 0; // 纯虚函数:子类必须实现
};
class Rectangle: public Shape{
   public:
      Rectangle( int a=0, int b=0):Shape(a, b) { }
      virtual ~Rectangle() override = default; 
      int area ()
      { 
         cout << "Rectangle class area :" <<endl;
         return (width * height); 
      }
      void test() override 
      {
         cout << "Rectangle class test :" <<endl;
      }
      void test2() override
      {
         cout << "Rectangle class test2 :" <<endl;
      }
};
class Triangle: public Shape{
   public:
      Triangle( int a=0, int b=0):Shape(a, b) { }
      virtual ~Triangle(){  cout << "Parent class ~Triangle :" <<endl; }
      int area ()
      { 
         cout << "Triangle class area :" <<endl;
         return (width * height / 2); 
      }
      void test()   //可以不写override,但是写了一目了然
      {
         cout << "Triangle class test :" <<endl;
      }
      void test2()  //可以不写override,但是写了一目了然
      {
         cout << "Triangle class test2 :" <<endl;
      }
};
// 程序的主函数
int main( )
{
   Shape *shape;
   Rectangle rec(10,7);
   Triangle  tri(10,5);
 
    //普通函数:基类、子类:按类型
   shape = &rec;
   shape->area();//基类
 
   shape = &tri;
   shape->area();//基类
   
   //虚函数
   shape = &rec;
   shape->test();//子类
 
   shape = &tri;
   shape->test();//子类
   
   shape->Shape::test();//指定父类
   
   //纯虚函数约定的是子类必须实现、override约定的是重写基类
   
   return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++ virtual 关键字 override 关键字 的相关文章

  • 如何获取正在访问 ASP.NET 应用程序的当前用户?

    为了获取系统中当前登录的用户 我使用以下代码 string opl System Security Principal WindowsIdentity GetCurrent Name ToString 我正在开发一个 ASP NET 应用程
  • “构建”构建我的项目,“构建解决方案”则不构建

    我刚刚开始使用VS2010 我有一个较大的解决方案 已从 VS2008 成功迁移 我已将一个名为 Test 的控制台应用程序项目添加到解决方案中 选择构建 gt 构建解决方案不编译新项目 选择构建 gt 构建测试确实构建了项目 在失败的情况
  • 以文化中立的方式将字符串拆分为单词

    我提出了下面的方法 旨在将可变长度的文本拆分为单词数组 以进行进一步的全文索引处理 删除停止词 然后进行词干分析 结果似乎不错 但我想听听关于这种实现对于不同语言的文本的可靠性的意见 您会建议使用正则表达式来代替吗 请注意 我选择不使用 S
  • 按成员序列化

    我已经实现了template
  • 不支持将数据直接绑定到存储查询(DbSet、DbQuery、DbSqlQuery)

    正在编码视觉工作室2012并使用实体模型作为我的数据层 但是 当页面尝试加载时 上面提到的标题 我使用 Linq 语句的下拉控件往往会引发未处理的异常 下面是我的代码 using AdventureWorksEntities dw new
  • Asp.NET WebApi 中类似文件名称的路由

    是否可以在 ASP NET Web API 路由配置中添加一条路由 以允许处理看起来有点像文件名的 URL 我尝试添加以下条目WebApiConfig Register 但这不起作用 使用 URIapi foo 0de7ebfa 3a55
  • 关于 C++ 转换:参数 1 从“[some_class]”到“[some_class]&”没有已知的转换

    我正在研究 C 并且遇到了一个错误 我不知道确切的原因 我已经找到了解决方案 但仍然想知道原因 class Base public void something Base b int main Base b b something Base
  • 不同枚举类型的范围和可转换性

    在什么条件下可以从一种枚举类型转换为另一种枚举类型 让我们考虑以下代码 include
  • 将 VSIX 功能添加到 C# 类库

    我有一个现有的单文件生成器 位于 C 类库中 如何将 VSIX 项目级功能添加到此项目 最终目标是编译我的类库项目并获得 VSIX 我实际上是在回答我自己的问题 这与Visual Studio 2017 中的单文件生成器更改 https s
  • 显示UnityWebRequest的进度

    我正在尝试使用下载 assetbundle统一网络请求 https docs unity3d com ScriptReference Networking UnityWebRequest GetAssetBundle html并显示进度 根
  • while 循环中的 scanf

    在这段代码中 scanf只工作一次 我究竟做错了什么 include
  • 如何在整个 ASP .NET MVC 应用程序中需要授权

    我创建的应用程序中 除了启用登录的操作之外的每个操作都应该超出未登录用户的限制 我应该添加 Authorize 每个班级标题前的注释 像这儿 namespace WebApplication2 Controllers Authorize p
  • 如何在 C 中调用采用匿名结构的函数?

    如何在 C 中调用采用匿名结构的函数 比如这个函数 void func struct int x p printf i n p x 当提供原型的函数声明在范围内时 调用该函数的参数必须具有与原型中声明的类型兼容的类型 其中 兼容 具有标准定
  • 垃圾收集器是否在单独的进程中运行?

    垃圾收集器是否在单独的进程中启动 例如 如果我们尝试测量某段代码所花费的进程时间 并且在此期间垃圾收集器开始收集 它会在新进程上启动还是在同一进程中启动 它的工作原理如下吗 Code Process 1 gt Garbage Collect
  • 如何查看网络连接状态是否发生变化?

    我正在编写一个应用程序 用于检查计算机是否连接到某个特定网络 并为我们的用户带来一些魔力 该应用程序将在后台运行并执行检查是否用户请求 托盘中的菜单 我还希望应用程序能够自动检查用户是否从有线更改为无线 或者断开连接并连接到新网络 并执行魔
  • 是否可以在 .NET Core 中将 gRPC 与 HTTP/1.1 结合使用?

    我有两个网络服务 gRPC 客户端和 gRPC 服务器 服务器是用 NET Core编写的 然而 客户端是托管在 IIS 8 5 上的 NET Framework 4 7 2 Web 应用程序 所以它只支持HTTP 1 1 https le
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定
  • C# - OutOfMemoryException 在 JSON 文件上保存列表

    我正在尝试保存压力图的流数据 基本上我有一个压力矩阵定义为 double pressureMatrix new double e Data GetLength 0 e Data GetLength 1 基本上 我得到了其中之一pressur
  • 如何在文本框中插入图像

    有没有办法在文本框中插入图像 我正在开发一个聊天应用程序 我想用图标图像更改值 等 但我找不到如何在文本框中插入图像 Thanks 如果您使用 RichTextBox 进行聊天 请查看Paste http msdn microsoft co
  • C++ 中类级 new 删除运算符的线程安全

    我在我的一门课程中重新实现了新 删除运算符 现在我正在使我的代码成为多线程 并想了解这些运算符是否也需要线程安全 我在某处读到 Visual Studio 中默认的 new delete 运算符是线程安全的 但这对于我的类的自定义 new

随机推荐

  • python英文(无空格)文本分词模块wordninja使用

    在NLP中 数据清洗与分词往往是很多工作开始的第一步 大多数工作中只有中文语料数据需要进行分词 现有的分词工具也已经有了很多了 这里就不再多介绍了 英文语料由于其本身存在空格符所以无需跟中文语料同样处理 如果英文数据中没有了空格 那么应该怎
  • react在移动端的自适应布局

    1 移动端基本可以无阻碍的进行flex的弹性布局 这边对flex就不进行深究 2 可以进行依据窗口进行vw vh vmin vmax单位的布局 1 vw vh vmin vmax 是一种视窗单位 也是相对单位 它相对的不是父节点或者页面的根
  • Android USB Camera(1) : 调试记录

    1 前言 前段时间调试了一个uvc摄像头 这里做下记录 硬件平台为mt6735 软件平台为android 5 0 2 底层配置 UVC全称是usb video class 一种usb视频规范 所有遵循uvc协议的摄像头都不需要安装额外的驱动
  • integer conversion resulted in a change of sign

    Type 69 D integer conversion resulted in a change of sign MDK 出现 68 D integer conversion resulted in a change of sign 在K
  • 三行代码实时追踪你的手,只要有浏览器就够了

    栗子 发自 凹非寺 量子位 报道 公众号 QbitAI Are You OK O K 人脸不管做了多么一言难尽的表情 五官也不太会四处乱跑 手就不一样了 手势百媚千娇 镜头看到的画面就百媚千娇 所以 AI怎么识别手呢 一位叫做Victor
  • Java获取月份天数错误

    之前编写获取日期函数如下 获取某年某月有多少天 return 该月的天数 public static int getDaysAboutMonth int year int month Calendar c Calendar getInsta
  • 电力电子转战数字IC——我的IC面试(2022.10.14更新)

    目录 感谢信 HKWS10 14面试 25mins JXC10 13面试 30mins JDSK9 23面试 42mins 快速的自我介绍 介绍一下这个MCDF的项目 你这里写SV搭建的验证环境 和UVM搭建的有什么区别吗 你这里写了覆盖率
  • 正视周期,创业在衰退中砥砺前行

    比衰退更重要的是 早期投资机构正在面临结构性机遇 数科星球原创 作者丨苑晶 编辑丨大兔 周期 犹如一只隐形的手 在2023年影响着芸芸众生 从经济周期 行业周期再到货币周期 这个隐形的手牵动着消费者需求 产业链变迁 政策变动等多种因素 亦牵
  • 12.SpringBoot整合mybatis实现插入操作

    本文基于10 springboot整合mybatis环境 默认环境配置好的 下面进入正题 首先查看以下数据库表有什么内容 然后 在EmpMapper中定义插入方法 并在emp xml中加入insert语句 这里推荐使用navicat复制其中
  • 概率图论:了解概率分布、概率独立性和随机化

    作者 禅与计算机程序设计艺术 概率图模型 Probabilistic Graphical Model PGM 是现代统计学习中的一个重要工具 它通过描述变量间的依赖关系和概率分布来对复杂系统进行建模 概率图模型由两部分组成 一是概率模型 它
  • QPoint与QPointF的区别

    QPointF类使用浮点精度定义平面中的点 QPoint类使用整数精度定义平面中的点
  • C语言链表嵌套链表学生成绩管理系统

    一阶段考核标准 用C语言链表嵌套链表学生成绩管理系统 链表A 每个节点存放一个新的链表B1 B2 B3 B4 B5的头结点 场景 一个年级 相当链表A 该年级5个班 每个班5个人 相当于链表B1 B5 做一个学生成绩管理系统学生成绩有语文
  • java版工程管理系统Spring Cloud+Spring Boot+Mybatis实现工程管理系统源码

    工程项目管理软件 工程项目管理系统 对建设工程项目管理组织建设 项目策划决策 规划设计 施工建设到竣工交付 总结评估 运维运营 全过程 全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一 系统管理 1 数据字典 实现对数据字典标签
  • Java中transient关键字的详细总结

    目录 一 概要介绍 1 序列化 2 为什么要用transient关键字 3 transient的作用 二 transient使用总结 三 使用场景 一 概要介绍 本文要介绍的是Java中的transient关键字 transient是短暂的
  • the left operand of ** is a garbage value

    CapLocation location 0 if segmentedControl tag SegmentControlTag if segmentIndex 0 location CapLeft else if segmentIndex
  • 最全的前端性能优化手段回答

    前端性能优化手段 参考答案 前端性能优化手段从以下几个方面入手 加载优化 执行优化 渲染优化 样式优化 脚本优化 1 加载优化 减少HTTP请求 缓存资源 压缩代码 无阻塞 首屏加载 按需加载 预加载 压缩图像 减少Cookie 避免重定向
  • 时序预测模型汇总

    时序预测模型 一 自回归 AR 在 AR 模型中 我们使用变量过去值的线性组合来预测感兴趣的变量 术语自回归表明它是变量对自身的回归 二 移动平均模型 MA 与在回归中使用预测变量的过去值的 AR 模型不同 MA 模型在类似回归的模型中关注
  • 三角函数的向量表示的原理计算

    在 电路 中 三相电源经常用复数或者是向量来表示 但是与我们初高中熟知的空间向量不同 这里的三相交流电是一种时间向量 由于采用的形式是正弦形式 使得其也可以用空间向量中的平行四边形原则来进行计算合成 下面将介绍一下正弦量可以用向量表示的原理
  • 生成tensorrt引擎错误记录-yolov5

    warning nvinfer1 Dims type is deprecated Wdeprecated declarations note TRT DEPRECATED DimensionType type MAX DIMS lt The
  • c++ virtual 关键字 override 关键字

    文章目录 1 什么是virtual 2 为什么需要 3 通常用在什么情形 4 延伸 虚函数 纯虚函数 override 关键字 9 问题汇总 9 1 非虚函数和虚函数都可以重写 那区别是啥 9 2 基类虚函数 纯虚函数 子类有没有 over