0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

在C++中如何用虚函数实现多态

Android编程精选 ? 来源:编程学习总站 ? 作者:写代码的牛顿 ? 2021-09-29 14:18 ? 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

01

C++虚函数探索

C++是一门面向对象语言,在C++里运行时多态是由虚函数和纯虚函数实现的,现在我们看下在C++中如何用虚函数实现多态。先来看一段代码。

// virtual_function.cpp : 此文件包含 “main” 函数。程序执行将在此处开始并结束。 // #include 《iostream》 class Base { public: Base()

{ std::cout 《《 “Base::constructor run” 《《 std::endl; } virtual void fun1()

{ std::cout 《《 “Base::fun1 run” 《《 std::endl; } virtual void fun2() { std::cout 《《 “Base::fun2 run” 《《 std::endl; } virtual ~Base()

{ std::cout 《《 “Base::desconstructor run” 《《 std::endl; } }; class Derive : public Base { public: Derive() { std::cout 《《 “Derive::constructor run” 《《 std::endl; } void fun1() { std::cout 《《 “Derive::fun1 run” 《《 std::endl; } void fun3()

{ std::cout 《《 “Derive::fun3 run” 《《 std::endl; } ~Derive() { std::cout 《《 “Derive::desconstructor run” 《《 std::endl; } }; int main() { Derive* d = new Derive(); d-》fun1(); d-》fun2(); d-》fun3(); delete d; }

这段代码编译运行后输出了:

Base::constructor run Derive::constructor run Derive::fun1 run Base::fun2 run Derive::fun3 run Derive::desconstructor run Base::desconstructor run

这段代码里基类Base定义了虚函数fun1和fun2,派生类Derive有成员函数fun1和fun3,其中派生类覆盖了继承而来的基类虚函数fun1。在主函数里创建Derive类型对象指针d指向Derive类型对象。由于派生类Derive成员函数fun1覆盖了基类Base成员函数fun1,因此通过d调用fun1实际调用的是派生类Derive类的成员函数fun1,而继承而来的成员函数fun2没有被覆盖,则通过指针d调用fun2实际调用的是基类成员函数fun2。这里好像让看不出虚函数有什么作用,那么我们将主函数修改如下:

int main() { Base* b = new Derive(); b-》fun1(); b-》fun2(); delete b; }

在程序里我们将创建一个基类指针b并指向的是派生类,并且调用delete释放内存时使用的是基类指针b。编译运行输出结果如下:

Base::constructor run Derive::constructor run Derive::fun1 run Base::fun2 run Derive::desconstructor run Base::desconstructor run

通过基类指针b调用fun1函数,实际调用的是派生类Derive的成员函数fun1,由于在派生类Derive中没有定义成员函数fun2,因此通过基类指针b调用fun2函数,实际调用的依旧是基类Base的成员函数fun2。代码里虽然我们没有对派生类的成员函数fun1加virtual,实际上派生类的成员函数fun1是虚函数。但是对于大多数C++初学者会有2个疑问的地方。1、通过基类指针b调用fun1函数,实际调用的是派生类的成员函数fun1。2、通过delete释放内存使用的是基类指针b,会调用派生类析构函数和基类析构函数,成功释放内存,不会存在内存泄露问题。

带有虚函数的类称为虚基类,子类继承虚基类。在C++中虚基类有一个虚函数表指针保存虚函数表地址,而虚函数表保存函数地址,虚函数表并不在虚基类里,但是虚函数表指针在虚基类里,子类继承虚基类,子类也就有了虚函数表指针。那么C++是如何通过虚函数表和虚函数表指针实现多态呢?打开VS2019,并用管理员身份运行“2019开发人员命令提示符”工具,如下图所示:

输入:cl /d1 reportSingleClassLayoutXXX [filename],XXX表示类名,[filename]表示类所在的.cpp文件路径。这里我输入源文件的派生类名和源文件路径,回车输出如下:

从输出可以看出派生类从基类继承了虚函数表指针vfptr,且占用字节数大小是4字节,刚好就是一个指针占用字节数。虚函数表vftable里保存了派生类成员函数fun1,基类成员函数fun2的地址,由于派生类成员函数fun3不是虚函数,因此虚函数表里没有fun3的地址。看到这里我们就明白了,通过基类指针b调用fun1的过程:通过虚函数表指针vfptr找到虚函数表vftable,再通过虚函数表找到派生类成员函数fun1的地址,调用派生类成员函数fun1。而通过基类指针b调用fun2的过程则是:通过虚函数表指针vfptr找到虚函数表vftable,再通过虚函数表找到基类成员函数fun2的地址,调用基类成员函数fun2。看到这里,第一个疑问已经解开了,关键在于虚函数表指针和虚函数表。

在C++中有虚函数的类,其析构函数默认是虚析构函数,只要是虚函数就会在虚函数表里有相应的函数地址,因此派生类里的虚函数表指针vfptr指向的虚函数表vftable必然保存着派生类析构函数的地址,类的析构过程:从继承链的最底端到最顶端依次调用析构函数,因此delete b调用过程:通过虚函数表指针vfptr找到虚函数表vftable,再通过虚函数表找到派生类析构函数地址,调用析构函数。

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数
    +关注

    关注

    3

    文章

    4384

    浏览量

    65136
  • C++
    C++
    +关注

    关注

    22

    文章

    2119

    浏览量

    75501

原文标题:C++虚函数详解

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    C语言中的内联函数与宏

    C编程,内联函数和宏都用于避免函数调用的开销并编写可复用的逻辑部分,但它们工作方式和安全性
    的头像 发表于 07-25 15:10 ?1251次阅读
    <b class='flag-5'>C</b>语言中的内联<b class='flag-5'>函数</b>与宏

    OpenVINO? C++代码启用 AddressSanitizer 时的内存泄漏怎么解决?

    OpenVINO? C++代码启用 AddressSanitizer 时遇到内存泄漏: \"#0 0xaaaab8558370 in operator new(unsigned
    发表于 06-23 07:16

    C++学到什么程度可以找工作?

    C++学到什么程度可以找工作?要使用C++找到工作,特别是作为软件开发人员或相关职位,通常需要掌握以下几个方面: 1. **语言基础**:你需要对C++的核心概念有扎实的理解,包括但不限于指针、内存
    发表于 03-13 10:19

    创建了用于OpenVINO?推理的自定义C++和Python代码,从C++代码获得的结果与Python代码不同是为什么?

    创建了用于OpenVINO?推理的自定义 C++ 和 Python* 代码。 两个推理过程中使用相同的图像和模型。 从 C++ 代码获得的结果与 Python* 代码不同。
    发表于 03-06 06:22

    为什么无法在运行时C++推理读取OpenVINO?模型?

    使用模型优化器 2021.1 版OpenVINO?转换模型 使用 Runtime 2022.3 版本 C++ 推理实现 ( core.read_model()) 读取模型Open
    发表于 03-05 06:17

    Spire.XLS for C++组件说明

    Spire.XLS for C++ 是一款专业的 C++ Excel 组件,可以用在各种 C++ 框架和应用程序。Spire.XLS for C+
    的头像 发表于 01-14 09:40 ?679次阅读
    Spire.XLS for <b class='flag-5'>C++</b>组件说明

    EE-112:模拟C++的类实现

    电子发烧友网站提供《EE-112:模拟C++的类实现.pdf》资料免费下载
    发表于 01-03 15:15 ?0次下载
    EE-112:模拟<b class='flag-5'>C++</b><b class='flag-5'>中</b>的类<b class='flag-5'>实现</b>

    AKI跨语言调用库神助攻C/C++代码迁移至HarmonyOS NEXT

    产品创新与功能迭代,而非技术迁移的细节问题,大幅提升开发效率。 据悉,涉及C/C++/ETS跨越语言调用的鸿蒙化应用,有超过80%的项目都在使用AKI,如某知名购物应用,使用后减少
    发表于 01-02 17:08

    同样是函数,CC++中有什么区别

    同样是函数 CC++ 中有什么区别? 第一个返回值。 C语言的函数可以不写返回值类型,
    的头像 发表于 11-29 10:25 ?954次阅读

    C++新手容易犯的十个编程错误

    简单的总结一下?C++ 新手容易犯的一些编程错误,给新人们提供一个参考。 1 有些关键字 cpp 文件多写了 对于 C++ 类,一些关键字只要写在 .h 中就好,cpp 中就不用再
    的头像 发表于 11-15 12:42 ?1059次阅读

    使用C语言实现函数模板

      用C语言能不能实现一个通用的函数,既能完成整数的相加,又能完成浮点数的相加?
    的头像 发表于 11-09 11:38 ?984次阅读

    C语言和C++结构体的区别

    同样是结构体,看看在C语言和C++中有什么区别?
    的头像 发表于 10-30 15:11 ?822次阅读

    C7000优化C/C++编译器

    电子发烧友网站提供《C7000优化C/C++编译器.pdf》资料免费下载
    发表于 10-30 09:45 ?0次下载
    <b class='flag-5'>C</b>7000优化<b class='flag-5'>C</b>/<b class='flag-5'>C++</b>编译器

    使用OpenVINO GenAI APIC++构建AI应用程序

    许多桌面应用程序是使用 C++ 开发的,而将生成式AI(GenAI)功能集成到这些应用程序可能会很具有挑战性,尤其是因为使用像 Hugging Face 这样的 Python 库的复杂性。C++
    的头像 发表于 10-12 09:36 ?1184次阅读
    使用OpenVINO GenAI API<b class='flag-5'>在</b><b class='flag-5'>C++</b><b class='flag-5'>中</b>构建AI应用程序

    ostreamc++的用法

    ostream 是 C++ 标准库中一个非常重要的类,它位于 头文件(实际上,更常见的是通过包含 头文件来间接包含 ,因为 包含了 和 )。 ostream 类及其派生类(如 std::cout
    的头像 发表于 09-20 15:11 ?1983次阅读