c++中继承多态virtual和override

目录

virtual:

易错点:

未声明虚函数:

忘记使用 override 关键字:

内存泄漏:

基类指针不指向任何对象:

访问权限问题:

不正确的类设计:

不正确的对象切片:

混淆 virtual 和 static 成员函数:

多重继承的二义性:

override:

继承(Inheritance):

多态(Polymorphism):

virtual 关键字:

override 关键字:


virtual:

C++中,继承和多态通常与虚函数(virtual)一起使用,以实现运行时多态性。以下是有关继承、多态和虚函数的更详细说明:

  1. 继承

    • 继承是一种机制,允许你创建一个新的类(派生类/子类),该类可以继承另一个类(基类/父类)的属性和方法。
    • 派生类可以访问基类的公有和受保护成员,但不能访问基类的私有成员。
  2. 多态

    • 多态是一种特性,允许不同对象对相同的消息(函数调用)做出不同的响应。
    • 多态可以通过函数重载和虚函数来实现。
  3. 虚函数

    • 虚函数是在基类中声明的函数,可以在派生类中被重写。
    • 声明一个函数为虚函数需要在函数声明前加上 virtual 关键字,例如:virtual void someFunction();
    • 当通过基类指针或引用调用虚函数时,实际执行的是派生类中的版本,而不是基类中的版本。这个过程被称为动态绑定,它实现了多态性。

示例:

class Base {
public:virtual void print() {std::cout << "Base class" << std::endl;}
};class Derived : public Base {
public:void print() override {std::cout << "Derived class" << std::endl;}
};int main() {Base* ptr = new Derived();ptr->print(); // 调用 Derived 类中的 print() 函数delete ptr;return 0;
}

在上面的示例中,Base 类中的 print 函数被声明为虚函数,而 Derived 类中重写了这个虚函数。当通过指向 Derived 对象的 Base 指针调用 print 函数时,会调用 Derived 类中的版本,实现了多态性。这是因为虚函数的调用在运行时动态绑定到正确的函数版本。

易错点:

当使用C++中的继承、多态和虚函数时,以下是每个点的更详细解释以及如何避免相关错误:

  1. 未声明虚函数

    • 错误说明:如果在基类中未将成员函数声明为虚函数,那么派生类中对该函数的重写将不会触发动态绑定,这可能导致多态性无法正常工作。
  2. 解决方法:确保在基类中需要多态性的成员函数前使用 virtual 关键字进行声明。例如:
    class Base {
    public:virtual void myFunction() {// ...}
    };
    

忘记使用 override 关键字

  • 错误说明:在派生类中重写虚函数时,使用 override 关键字可以帮助编译器检查是否正确地重写了基类函数。如果忘记使用它,编译器无法提供此种检查。
  • 解决方法:在派生类中重写虚函数时,使用 override 关键字,这样编译器可以检查错误:
    class Derived : public Base {
    public:void myFunction() override {// ...}
    };
    

  1. 内存泄漏

    • 错误说明:内存泄漏是未释放动态分配的内存的情况。特别是在使用指向派生类对象的基类指针时,容易忘记释放内存。
    • 解决方法:使用 delete 来手动释放内存是一种方法,但更好的做法是使用智能指针(如 std::shared_ptrstd::unique_ptr)来管理内存。这些智能指针会自动处理内存释放,减少内存泄漏的风险。
  2. 基类指针不指向任何对象

    • 错误说明:如果将基类指针设置为未初始化或空指针,然后尝试调用虚函数,会导致未定义行为。
    • 解决方法:始终确保基类指针指向有效的派生类对象,或者在调用虚函数之前检查指针是否为 nullptr。避免未初始化的指针

 

Base* ptr = nullptr; // 初始化为 nullptr
// ...
if (ptr != nullptr) {ptr->myFunction(); // 在调用前检查指针是否为 nullptr
}
  1. 访问权限问题

    • 错误说明:派生类尝试访问基类的私有成员,这是不允许的,因为私有成员只能在基类内部访问。
    • 解决方法:确保派生类只访问基类中的公有或受保护成员,或者使用友元关系来解决权限问题。
  2. 不正确的类设计

    • 错误说明:不良的类设计可能导致难以维护和理解的代码,或者使多态性无法正确工作。
    • 解决方法:在设计类层次结构时,仔细考虑每个类的责任和关系,确保清晰的抽象和继承关系。使用合理的类层次结构来避免混乱和不必要的复杂性。
  3. 不正确的对象切片

    • 错误说明:将派生类对象赋值给基类对象会导致对象切片,丢失派生类的信息。这可能导致意外行为和数据丢失。
    • 解决方法:在处理派生类对象时,始终使用基类指针或引用,以避免对象切片。不要试图将派生类对象隐式转换为基类对象。
Base baseObj = derivedObj; // 对象切片,避免这种情况

 

  1. 混淆 virtualstatic 成员函数:

    • 错误说明:混淆虚函数和静态成员函数是一种常见的错误。静态成员函数与类关联,而虚函数与对象关联。
    • 解决方法:理解 virtual 成员函数是动态绑定的,而 static 成员函数是与类关联而不是与对象关联的。确保在适当的情况下使用每种类型的函数。
  2. 多重继承的二义性

    • 错误说明:在多重继承中,如果两个基类具有相同名称的成员,可能会导致二义性,编译器无法确定应该使用哪个成员。
    • 解决方法:使用作用域解析运算符(::)来明确指定要使用的基类成员,或者使用虚继承来解决二义性问题。虚继承用于在多重继承中解决菱形继承问题,以避免二义性。

override:

在C++中,继承、多态和 override 关键字是面向对象编程中的重要概念,它们一起用于创建和使用基类和派生类之间的关系。以下是有关这些概念的详细解释:

  1. 继承(Inheritance)

    • 继承是一种面向对象编程的机制,允许一个类(子类/派生类)从另一个类(父类/基类)继承属性和方法。
    • 子类可以继承父类的成员变量和成员函数,以及它们的访问权限。
      class Base {
      public:int baseVar;void baseFunction() {// ...}
      };class Derived : public Base {
      public:// Derived 继承了 baseVar 和 baseFunction// 可以访问它们并添加新的成员void derivedFunction() {// ...}
      };
      

多态(Polymorphism)

  • 多态是一种特性,允许不同对象对相同的消息(函数调用)做出不同的响应。
  • 多态可以通过虚函数和函数重载来实现。

 

class Shape {
public:virtual void draw() {// 基类虚函数}
};class Circle : public Shape {
public:void draw() override {// 派生类重写了基类虚函数}
};void drawShape(Shape* shape) {shape->draw(); // 调用虚函数,根据对象类型执行正确的函数
}

virtual 关键字

  • virtual 是用于声明虚函数的关键字。
  • 虚函数是在基类中声明的函数,可以在派生类中被重写。
  • 当通过基类指针或引用调用虚函数时,实际执行的是派生类中的版本,这实现了运行时多态性。、
class Base {
public:virtual void myFunction() {// 基类虚函数}
};

override 关键字

  • override 是 C++11 引入的关键字,用于明确指示一个函数是派生类中对基类虚函数的重写。
  • 使用 override 可以帮助编译器检查是否正确地重写了基类的虚函数,防止错误。
    class Derived : public Base {
    public:void myFunction() override {// 派生类重写了基类虚函数}
    };
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/69951.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C高级-Linux终端基础指令

在线下载软件 检测网络 ping baidu.com在下载软件前&#xff0c;需将Linux系统中的软件源更新成国内的软件源&#xff1a;清华源、阿里源、163源、中科大源… 更新软件列表 将系统中的软件源更新为国内的软件源后&#xff0c;使用命令sudo apt-get update 使Ubuntu连接到国…

[HDCTF 2023]YamiYami

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言涉及知识点解题详细过程session伪造反弹shell 前言 从暑假末尾一直搁置&#xff0c;当时卡在反弹shell搞得离flag就差一步。不过最近一两天学习完反弹shell的知…

8.(Python数模)(预测模型一)马尔科夫链预测

Python实现马尔科夫链预测 马尔科夫链原理 马尔科夫链是一种进行预测的方法&#xff0c;常用于系统未来时刻情况只和现在有关&#xff0c;而与过去无关。 用下面这个例子来讲述马尔科夫链。 如何预测下一时刻计算机发生故障的概率&#xff1f; 当前状态只存在0&#xff08;故…

肖sir__设计测试用例方法之_(白盒测试)

白盒测试技术 一、定义&#xff1a; 白盒测试也叫透明盒测试&#xff0c;检查程序内部结构及路径一是否符合规格说明&#xff0c;二是否符合其代码规范。 因此&#xff0c;也叫结构测试或者逻辑驱动测试。 二、白盒测试常见方法&#xff1a; a、语句覆盖&#xff1b; b、判断覆…

虚拟机扩容

系统环境centos8&#xff0c;分两步&#xff0c;第一步先在vmware扩容&#xff0c;第二部在虚拟机内部扩容 1.vmware分配磁盘空间 2.虚拟机内部扩容 查看当前磁盘信息&#xff0c;这个是扩容之前的&#xff0c;扩容完成才会显示新的 df -h查看系统分区信息 fdisk -l查看目录…

C语言基础知识理论版(很详细)

文章目录 前述一、数据1.1 数据类型1.2 数据第一种数据&#xff1a;常量第二种数据&#xff1a;变量第三种数据&#xff1a;表达式1、算术运算符及算术表达式2、赋值运算符及赋值表达式3、自增、自减运算符4、逗号运算符及其表达式&#xff08;‘顺序求值’表达式&#xff09;5…

Spring Boot日志基础使用 设置日志级别

然后 我们来说日志 日志在实际开发中还是非常重要的 即可记录项目状态和一些特殊情况发生 因为 我们这里不是将项目 所以 讲的也不会特别深 基本还是将Spring Boot的日志设置或控制这一类的东西 相对业务的领域我们就不涉及了 日志 log 初期最明显的作用在于 开发中 你可以用…

深入浅出了解BeanFactory 和 ApplicationContext

一.区别 BeanFactory和ApplicationContext是Spring的两大核心接口&#xff0c;都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。 1.依赖关系 BeanFactory&#xff1a;是Spring里面最底层的接口&#xff0c;包含了各种Bean的定义&#xff0c;读取bean…

Mac 手动安装 sshpass

1. 下载安装包 https://sourceforge.net/projects/sshpass/ 解压并进入到安装包目录 tar -zxvf sshpass-xx.xx.tar.gz cd sshpass-xx.xx2. 检验环境&#xff0c;编译源码安装 ./configuremake&&make install3. 检测安装是否成功 ▶ sshpass Usage: sshpass [-f|-…

uniapp 使用mqtt 报错 socketTask onOpen is not a function

1. 报错的解决方法 在man.js文件添加这个 // #ifndef MP // 处理 wx.connectSocket promisify 兼容问题&#xff0c;强制返回 SocketTask uni.connectSocket (function(connectSocket) {return function(options) {console.log(options)options.success options.success ||…

Golang专题精进

Golang专题精进 Golang单元测试Golang错误处理Golang正则表达式Golang反射Golang验证码Golang日期时间处理库CarbonGolang发送邮件库emailGolang log日志Golang log日志框架logrusGolang加密和解密应用Golang访问权限控制框架casbinGolang使用swagger生成api接口文档Golang jwt…

Vue + Element UI 实现权限管理系统 前端篇(十四):菜单功能实现菜

Vue Element UI 实现权限管理系统 前端篇&#xff08;十四&#xff09;&#xff1a;菜单功能实现 菜单功能实现 菜单接口封装 菜单管理是一个对菜单树结构的增删改查操作。 提供一个菜单查询接口&#xff0c;查询整颗菜单树形结构。 http/modules/menu.js 添加 findMenu…

2分钟讲清楚C#的委托, C语言的函数指针,Java的函数式接口

很多小伙伴学习C# 的委托时往往一头雾水, 不明白委托是什么, 有什么作用, 今天我就用2分钟讲清楚 这是一个C# 的控制台程序 定义一个最简单的委托 delegate int Calculate(int a, int b); 这相当于定义了一个Calculate类型, 只不过这个类型需要传入2个int类型的参数 返回值也…

uniapp 解决跨域的问题

uniapp 解决跨域的问题 我真的是个 沙雕 找对了解决办法 写错了地方 "h5" : {"devServer" : {"disableHostCheck" : true,"https": false,"proxy" : {"/app" : {"target" : "https://192.16…

MRI多任务技术及应用

目录 一、定量心血管磁共振成像&#xff08;CMR&#xff09;的改进方法二、磁共振多任务三、磁共振多任务的成像框架四、磁共振多任务的图像模型和采样和重建策略五、利用MR多任务进行快速三维稳态CEST(ss-CEST)成像5.1 利用MR多任务进行快速三维稳态CEST(ss-CEST)成像介绍5.2 …

LeetCode 2605. Form Smallest Number From Two Digit Arrays【数组,哈希表,枚举;位运算】1241

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

Java调用Web Service接口

方法1. 用IDEA生成相关代码调用方法。 在IDEA插件商店下载插件 然后新建一个Java项目 创建一个包来存放生成的代码&#xff0c;(点击一下)选中这个包&#xff0c;点击Tools 填入接口url&#xff0c;记住后面拼接“?wsdl”&#xff0c;选择生成方法&#xff0c;然后OK即可生…

超详解| Yolov8模型手把手调参 | 配置 | 模型训练 | 验证 | 推理

YOLOv8是一款前沿、最先进&#xff08;SOTA&#xff09;的模型&#xff0c;基于先前YOLO版本的成功&#xff0c;引入了新功能和改进&#xff0c;进一步提升性能和灵活性。 然而&#xff0c;要充分发挥Yolov8的潜力&#xff0c;合理的参数配置是至关重要的。本文将带您深入了解…

stable diffusion实践操作-VAE

系列文章总目录 stable diffusion实践操作 文章目录 系列文章总目录一、 前言1 定义功能全局介绍2 模型全局介绍2.1 后缀以及存放位置2.2 查看大模型是否有VAE 二、正文1 原理1.1 基础原理 2 使用2.1 增加饱和度2.2 增加细节 3 下载3.1 自动下载3.2 手动下载 三 、总结 一、 前…

掌握逻辑漏洞复现技术,保护您的数字环境

环境准备 这篇文章旨在用于网络安全学习&#xff0c;请勿进行任何非法行为&#xff0c;否则后果自负。 1、支付逻辑漏洞 攻击相关介绍 介绍&#xff1a; 支付逻辑漏洞是指攻击者利用支付系统的漏洞&#xff0c;突破系统的限制&#xff0c;完成非法的支付操作。攻击者可以采…