爆款云主机2核4G限时秒杀,88元/年起!
查看详情

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
热门活动
  • 618智算钜惠季 爆款云主机2核4G限时秒杀,88元/年起!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
      • 文档
      • 控制中心
      • 备案
      • 管理中心

      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      首页 知识中心 软件开发 文章详情页

      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      2025-02-11 09:36:37 阅读次数:12

      函数,基类,子类,指向,指针,调用

      文章1(循序渐进解析)

      什么是多态

      概念:

       定义:

      同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。简单的说:就是用基类的引用指向子类的对象。

      用通俗的话说,多态是指多个子类有一个共有操作,我们在父类中定义一个统一的抽象虚接口,然后各个子类分别实现。

       实现:C++多态性主要是通过虚函数实现的,虚函数允许子类重写override


      //code start
      //B is base class,A -- C is sub - class

      class B
      {
      public:
          virtual void do_sth() = 0;
      };
      class A : public B
      {
      public:
          void do_sth()
          {
              cout << "- A do_sth()\n";
          }
      };
      class C : public B
      {
      public:
          void do_sth()
          {
              cout << "- C do_sth()\n";
          }
      };

      void do_sth(B *id_b) //
      {
          id_b->do_sth();
      }
      int main()
      {

          A *id_a = new A();
          C *id_c = new C();

          do_sth(id_a);
          do_sth(id_c);
          return 0;
      }// code end

      函数运行输出结果是:

      A do_sth()
      C do_sth()

      看 “ void do_sth(B *id_b)” ,  我们用的基类指针作为函数的接口参数,但是 “do_sth(id_a); ” 传递参数时,我们传的是 A 或者 C 对象的指针! 多态使得调用的接口一致,更利于抽象和简化。

       

      多态有什么好处?

      目的:接口重用。封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。而多态的目的则是为了接口重用。

      1. 应用程序不必为每一个派生类编写功能调用,只需要对抽象基类进行处理即可。大大提高程序的可复用性。//继承 
      2. 派生类的功能可以被基类的方法或引用变量所调用,这叫向后兼容,可以提高可扩充性和可维护性。          //多态的真正作用

      https:///shihuboke/article/details/79333585

       

      虚函数的底层实现原理

      看到一个说明很好,转过来:

      在C++中,多态是利用虚函数来实现的。比如说,有如下代码:

      #include <iostream>
      using namespace std;
      class Animal
      {
      public:
      	void Cry()
      	{
      		cout << "Animal cry!" << endl;
      	}
      };
      class Dog :public Animal
      {
      public:
      	void Cry()
      	{
      		cout << "Wang wang!" << endl;
      	}
      };
      void MakeAnimalCry(Animal& animal)
      {
      	animal.Cry();
      }
      int main()
      {
      	Dog dog;
      	dog.Cry();
      	MakeAnimalCry(dog);
      	return 0;
      }

      输出如下图:

      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      这里定义了一个Animal类,Dog类继承该类,并覆盖了它的Cry方法。有一个MakeAnimalCry方法,传入了Animal的引用,传入了dog对象,但是输出确是Animal的输出。理想的情况下,用户希望传入的是dog对象,就该调用dog的Cry方法。要实现这种多态行为,需要将Animal::Cry()声明为虚函数。可以通过Animal指针或者Animal引用来访问Animal对象,这种指针或者引用可以指向Animal、Dog、Cat对象,而不需要关心它们具体指向的是哪种对象。修改代码如下:
       

      #include <iostream>
      using namespace std;
      class Animal
      {
      public:
      	virtual void Cry()
      	{
      		cout << "Animal cry!" << endl;
      	}
      };
      class Dog :public Animal
      {
      public:
      	void Cry()
      	{
      		cout << "Wang wang!" << endl;
      	}
      };
      class Cat:public Animal
      {
      public:
      	void Cry()
      	{
      		cout << "Meow meow" << endl;
      	}
      };
      void MakeAnimalCry(Animal& animal)
      {
      	animal.Cry();
      }
      int main()
      {
      	Dog dog;
      	Cat cat;
      	//dog.Cry();
      	MakeAnimalCry(dog);
      	MakeAnimalCry(cat);
      	return 0;
      }

      修改后的输出如下:


      这就是多态的效果,将派生类对象视为基类对象,并执行派生类的Cry实现。如果基类指针指向的是派生类对象,通过该指针调用运算符delete时,即对于使用new在自由存储区中实例化的派生类对象,如果将其赋给基类指针,并通过该指针调用delete,将不会调用派生类的析构函数。这可能会导致资源未释放、内存泄露等问题,为了避免这种问题,可以将基类的析构函数声明为虚函数。

      在上面的程序中,演示了多态的效果,即在函数MakeAnimalCry中,虽然通过Animal引用调用Cry方法,但是实际调用的确是Dog::Cry或者Cat::Cry方法。在编译阶段,编译器并不知道将要传递给该函数的是哪种对象,无法确保在不同的情况下执行不同的Cry方法。应该调用哪个Cry方法显然是在运行阶段决定的。这是使用多态的不可见逻辑实现的,而这种逻辑是编译器在编译阶段提供的。下面详细地说明一下虚函数的底层实现原理。

      比如说有下面的基类Base,它声明了N个虚函数:
       

      class Base
      {
      public:
      	virtual void Func1()
      	{
      		//Func1的实现代码 
      	}
      	virtual void Func2()
      	{
      		//Func2的实现代码 
      	}
      	//Func3、Func4等虚函数的实现 
      	virtual void FuncN()
      	{
      		//FuncN的实现代码 
      	}
      };

      下面的Derived类继承了Base类,并且覆盖了除Func2之外的其他所有虚函数,

      class Derived:public Base
      {
      public:
      	virtual void Func1()
      	{
      		//Func2覆盖Base类的Func1代码 
      	}
      	//除去Func2的其他所有虚函数的实现代码 
      	virtual void FuncN()
      	{
      		//FuncN覆盖Base类的FuncN代码 
      	}
      };

      编译器见到这种继承层次结构后,知道Base定义了虚函数,并且在Derived类中覆盖了这些函数。在这种情况下,编译器将为实现了虚函数的基类和覆盖了虚函数的派生类分别创建一个虚函数表(Virtual Function Table,VFT)。也就是说Base和Derived类都将有自己的虚函数表。实例化这些类的对象时,将创建一个隐藏的指针VFT*,它指向相应的VFT。可将VFT视为一个包含函数指针的静态数组,其中每个指针都指向相应的虚函数。Base类和Derived类的虚函数表如下图所示:

      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承
      每个虚函数表都由函数指针组成,其中每个指针都指向相应虚函数的实现。在类Derived的虚函数表中,除一个函数指针外,其他所有的函数指针都指向本地的虚函数实现。Derived没有覆盖Base::Func2,因此相应的虚函数指针指向Base类的Func2的实现。这就意味着,当执行下面的代码时,编译器将查找Derived类的VFT,确保调用Base::Func2的实现:


      Derived objDerived;
      objDerived.Func2();

      调用被覆盖的方法时,也是这样:

      void DoSomething(Base& objBase)
      {
          objBase.Func1();
      }
      int main()
      {
          Derived objDerived;
          DoSomething(objDerived);
      }

      在这种情况下,虽然将objDerived传递给了objBase,进而被解读成一个Base实例,但该实例的VFT指针仍然指向Derived类的虚函数表,因此通过该VFT执行的是Derived::Func1.虚函数表就是通过上面的方式来实现C++的多态。

      要验证虚函数表的存在其实也很简单,可以通过比较同一个类,一个包含虚函数,一个不包含,对比其大小就知道了。
       

      #include <iostream>
      using namespace std;
      class Test 
      {
      public:
      	int a,b;
      	void DoSomething()
      	{	} 
      };
      class Base
      {
      public:
      	int a,b;
      	virtual void DoSomething()
      	{	} 
      };
      int main()
      {
      	cout<<"sizeof(Test):"<<sizeof(Test)<<endl;
      	cout<<"sizeof(Base):"<<sizeof(Base)<<endl;
      	return 0;
      }

      执行输出如下:

      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承
      虽然两个类几乎相同,因为Base中的DoSomething方法是一个虚函数,编译器为Base类生成了一个虚函数表,并为其虚函数表指针预留空间,所以Base类占用的内存空间比Test类多了8个字节。

       

      转自:https:///u011000290/article/details/50498683

       

      文章2(直白解析)

       

      1. 概述

      简单地说,每一个含有虚函数(无论是其本身的,还是继承而来的)的类都至少有一个与之对应的虚函数表,其中存放着该类所有的虚函数对应的函数指针。例:
      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      其中:

      • B的虚函数表中存放着B::foo和B::bar两个函数指针。
      • D的虚函数表中存放的既有继承自B的虚函数B::foo,又有重写(override)了基类虚函数B::bar的D::bar,还有新增的虚函数D::quz。

      提示:为了描述方便,本文在探讨对象内存布局时,将忽略内存对齐对布局的影响。

      2. 虚函数表构造过程

      从编译器的角度来说,B的虚函数表很好构造,D的虚函数表构造过程相对复杂。下面给出了构造D的虚函数表的一种方式(仅供参考):
      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      提示:该过程是由编译器完成的,因此也可以说:虚函数替换过程发生在编译时。

      3. 虚函数调用过程

      以下面的程序为例:
      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      编译器只知道pb是B*类型的指针,并不知道它指向的具体对象类型 :pb可能指向的是B的对象,也可能指向的是D的对象。

      但对于“pb->bar()”,编译时能够确定的是:此处operator->的另一个参数是B::bar(因为pb是B*类型的,编译器认为bar是B::bar),而B::bar和D::bar在各自虚函数表中的偏移位置是相等的。

      无论pb指向哪种类型的对象,只要能够确定被调函数在虚函数中的偏移值,待运行时,能够确定具体类型,并能找到相应vptr了,就能找出真正应该调用的函数。

      提示:本人曾在“C/C++杂记:深入理解数据成员指针、函数成员指针”一文中提到:虚函数指针中的ptr部分为虚函数表中的偏移值(以字节为单位)加1。

      B::bar是一个虚函数指针, 它的ptr部分内容为9,它在B的虚函数表中的偏移值为8(8+1=9)。

      当程序执行到“pb->bar()”时,已经能够判断pb指向的具体类型了:

      • 如果pb指向B的对象,可以获取到B对象的vptr,加上偏移值8((char*)vptr + 8),可以找到B::bar。
      • 如果pb指向D的对象,可以获取到D对象的vptr,加上偏移值8((char*)vptr + 8) ,可以找到D::bar。
      • 如果pb指向其它类型对象...同理...

      4. 多重继承

      当一个类继承多个类,且多个基类都有虚函数时,子类对象中将包含多个虚函数表的指针(即多个vptr),例:
      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      其中:D自身的虚函数与B基类共用了同一个虚函数表,因此也称B为D的主基类(primary base class)。

      虚函数替换过程与前面描述类似,只是多了一个虚函数表,多了一次拷贝和替换的过程。

      虚函数的调用过程,与前面描述基本类似,区别在于基类指针指向的位置可能不是派生类对象的起始位置,以如下面的程序为例:
      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      5. 菱形继承

      本文不讨论菱形继承的情形,个人觉得:菱形继承的复杂度远大于它的使用价值,这也是C++让人又爱又恨的原因之一。

      如果想要深入研究,可以参考:Itanium C++ ABI。

       

      原文:https:///malecrab/p/5572730.html

       

      虚析构函数的作用及其原理

      https:///article/159253.htm

      总的来说虚析构函数是为了避免内存泄露,而且是当子类中会有指针成员变量时才会使用得到的。

      也就说虚析构函数使得在删除指向子类对象的基类指针时可以调用子类的析构函数达到释放子类中堆内存的目的,而防止内存泄露的.

      我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数。可是,为什么要这样做呢?下面用一个

      小例子来说明:

      #include<iostream>
      using namespace std;
      
      class ClxBase
      {
        public:
          ClxBase() {};
          virtual ~ClxBase() { cout<<"delete ClxBase"<<endl; };
      
          virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
      
      };
      
      class ClxDerived : public ClxBase
      {
        public:
          ClxDerived() {};
          ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
      
          void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
      
      };
      
      int main(int argc, char const* argv[])
      {
         ClxBase *pTest = new ClxDerived;
         pTest->DoSomething();
         delete pTest;
        return 0;
      }

      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      但是,如果把类ClxBase析构函数前的virtual去掉,那输出结果就是下面的样子了:

      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      没有调动子类的析构函数

      也就是说,类ClxDerived的析构函数根本没有被调用!一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。我想所有的C++程序员都知道这样的危险性。当然,如果在析构函数中做了其他工作的话,那你的所有努力也都是白费力气。

      所以,文章开头的那个问题的答案就是--这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。

      当然,并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。

      执行 Base *b = new Der;之后b的最终形态

      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      面试常见问题

      1、C++ 虚函数表是属于类的还是属于对象的

      虚函数表属于类,同一个类的多个对象共享同一张虚函数表。https:///cswuyg/archive/2010/08/20/1804069.html

      2、为什么构造函数不能为虚函数?

      虚函数的调用需要虚函数表指针,而该指针存放在对象的内存空间中;若构造函数声明为虚函数,那么由于对象还未创建,还没有内存空间,更没有虚函数表地址用来调用虚函数——构造函数了。

      3、为什么析构函数可以为虚函数,如果不设为虚函数可能会存在什么问题?

          首先析构函数可以为虚函数,而且当要使用基类指针或引用调用子类时,最好将基类的析构函数声明为虚函数,否则可以存在内存泄露的问题。

          举例说明:

          子类B继承自基类A;A *p = new B; delete p;

        1) 此时,如果类A的析构函数不是虚函数,那么delete p;将会仅仅调用A的析构函数,只释放了B对象中的A部分,而派生出的新的部分未释放掉。

           2) 如果类A的析构函数是虚函数,delete p; 将会先调用B的析构函数,再调用A的析构函数,释放B对象的所有空间。

          补充: B *p = new B; delete p;时也是先调用B的析构函数,再调用A的析构函数。

      4、

      1)每个类都有虚指针和虚表;
      2)如果不是虚继承,那么子类将父类的虚指针继承下来,并指向自身的虚表(发生在对象构造时)。有多少个虚函数,虚表里面的项就会有多少。多重继承时,可能存在多个的基类虚表与虚指针;
      3)如果是虚继承,那么子类会有两份虚指针,一份指向自己的虚表,另一份指向虚基表,多重继承时虚基表与虚基表指针有且只有一份。
      原文链接:https:///xiaxzhou/article/details/76576516

      5、虚析构函数的作用

      虚析构函数使得在删除指向子类对象的基类指针时可以调用子类的析构函数达到释放子类中堆内存的目的,而防止内存泄露的.

       

      多重继承会有多个虚函数表,几重继承,就会有几个虚函数表。这些表按照派生的顺序依次排列,如果子类改写了父类的虚函数,那么就会用子类自己的虚函数覆盖虚函数表的相应的位置,如果子类有新的虚函数,那么就添加到第一个虚函数表的末尾。

      6、虚函数表存放在哪里

      虚函数表vtable在Linux/Unix中存放在可执行文件的只读数据段中(rodata),这与微软的编译器将虚函数表存放在常量段(CONST    SEGMENT)存在一些差别。(https:///laiqun/p/5887372.html)

      (存放在全局数据区)

      1.虚函数表是全局共享的元素,即全局仅有一个.

      2.虚函数表类似一个数组,类对象中存储vptr指针,指向虚函数表.即虚函数表不是函数,不是程序代码,不肯能存储在代码段.

      3.虚函数表存储虚函数的地址,即虚函数表的元素是指向类成员函数的指针,而类中虚函数的个数在编译时期可以确定,即虚函数表的大小可以确定,即大小是在编译时期确定的,不必动态分配内存空间存储虚函数表,所以不再堆中.

      根据以上特征,虚函数表类似于类中静态成员变量.静态成员变量也是全局共享,大小确定.

       

      内存分布

       

      假设有一个基类ClassA,一个继承了该基类的派生类ClassB,并且基类中有虚函数,派生类实现了基类的虚函数。
      我们在代码中运用多态这个特性时,通常以两种方式起手:
      (1) ClassA *a = new ClassB();
      (2) ClassB b; ClassA *a = &b;

      以上两种方式都是用基类指针去指向一个派生类实例,区别在于第1个用了new关键字而分配在堆上,第2个分配在栈上。

      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      二、类的虚函数表与类实例的虚函数指针


      首先不考虑继承的情况。

      如果一个类中有虚函数,那么该类就有一个虚函数表。
      这个虚函数表是属于类的,所有该类的实例化对象中都会有一个虚函数表指针去指向该类的虚函数表。
      第一部分的图中我们也能看到,一个类的实例要么在堆上,要么在栈上。也就是说一个类可以有很多很多个实例。但是!一个类只能有一个虚函数表。在编译时,一个类的虚函数表就确定了,这也是为什么它放在了只读数据段中。

      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      继承

      只要基类有虚函数,子类不论实现或没实现,都有虚函数表。


      (1) ClassA是基类, 有普通函数: func1() func2() 。虚函数: vfunc1() vfunc2() ~ClassA()
      (2) ClassB继承ClassA, 有普通函数: func1()。虚函数: vfunc1() ~ClassB()
      (3) ClassC继承ClassB, 有普通函数: func2()。虚函数: vfunc2() ~ClassB()

      基类的虚函数表和子类的虚函数表不是同一个表。下图是基类实例与多态情形下,数据逻辑结构。注意,虚函数表是在编译时确定的,属于类而不属于某个具体的实例。虚函数在代码段,仅有一份。

      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

       ClassB继承于ClassA,其虚函数表是在ClassA虚函数表的基础上有所改动的,变化的仅仅是在子类中重写的虚函数。如果子类没有重写任何父类虚函数,那么子类的虚函数表和父类的虚函数表在内容上是一致的。

      ClassB,ClassB继承ClassA

      这是一个多次单继承的情况。

      【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      四、多继承下的虚函数表 (同时继承多个基类)

      ClassA1是第一个基类,拥有普通函数func1(),虚函数vfunc1() vfunc2()。
      ClassA2是第二个基类,拥有普通函数func1(),虚函数vfunc1() vfunc2(),vfunc4()。
      ClassC依次继承ClassA1、ClassA2。普通函数func1(),虚函数vfunc1() vfunc2() vfunc3()。

       【C++】什么是多态?虚函数的底层实现原理|多重继承|菱形继承

      在多继承情况下,有多少个基类就有多少个虚函数表指针,前提是基类要有虚函数才算上这个基类。
      如图,虚函数表指针01指向的虚函数表是以ClassA1的虚函数表为基础的,子类的ClassC::vfunc1(),和vfunc2()的函数指针覆盖了虚函数表01中的虚函数指针01的位置、02位置。当子类有多出来的虚函数时,添加在第一个虚函数表中。

      当有多个虚函数表时,虚函数表的结果尾是0代表没有下一个虚函数表。" * "号位置在不同操作系统中实现不同,代表有下一个虚函数表。
      注意:
      1.子类虚函数会覆盖每一个父类的每一个同名虚函数。
      2.父类中没有的虚函数而子类有,填入第一个虚函数表中,且用父类指针是不能调用。
      3.父类中有的虚函数而子类没有,则不覆盖。仅子类和该父类指针能调用。

      版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://blog.csdn.net/bandaoyu/article/details/96054429,作者:bandaoyu,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

      上一篇:【jQuery】jQuery中$.get、$.post、$.getJSON和$.ajax用法的区别

      下一篇:【规范】C/C++注释格式

      相关文章

      2025-05-19 09:04:44

      js小题2:构造函数介绍与普通函数对比

      js小题2:构造函数介绍与普通函数对比

      2025-05-19 09:04:44
      new , 关键字 , 函数 , 对象 , 构造函数
      2025-05-19 09:04:44

      地址与指针

      地址与指针

      2025-05-19 09:04:44
      char , 地址 , 指向
      2025-05-19 09:04:30

      【Canvas技法】辐射式多道光影的实现

      【Canvas技法】辐射式多道光影的实现

      2025-05-19 09:04:30
      代码 , 函数 , 实现
      2025-05-19 09:04:22

      外设驱动库开发笔记54:外设库驱动设计改进的思考

      外设驱动库开发笔记54:外设库驱动设计改进的思考

      2025-05-19 09:04:22
      使用 , 函数 , 初始化 , 定义 , 对象
      2025-05-19 09:04:14

      C语言字符函数和字符串函数--(超全超详细)

      C语言字符函数和字符串函数--(超全超详细)

      2025-05-19 09:04:14
      函数 , 字符 , 字符串
      2025-05-16 09:15:24

      如何将一串数字用函数的方法倒过来(C语言)

      如何将一串数字用函数的方法倒过来(C语言)

      2025-05-16 09:15:24
      函数 , 数字 , 数组
      2025-05-14 10:33:31

      计算机小白的成长历程——习题演练(函数篇)

      计算机小白的成长历程——习题演练(函数篇)

      2025-05-14 10:33:31
      函数 , 字符串 , 数组 , 知识点 , 编写 , 迭代 , 递归
      2025-05-14 10:33:31

      【数据结构】第一章——绪论(2)

      【数据结构】第一章——绪论(2)

      2025-05-14 10:33:31
      函数 , 实现 , 打印 , 理解 , 算法 , 输入 , 输出
      2025-05-14 10:33:25

      30天拿下Rust之高级类型

      Rust作为一门系统编程语言,以其独特的内存管理方式和强大的类型系统著称。其中,高级类型的应用,为Rust的开发者提供了丰富的编程工具和手段,使得开发者可以更加灵活和高效地进行编程。

      2025-05-14 10:33:25
      Rust , type , 代码 , 函数 , 类型 , 返回
      2025-05-14 10:33:16

      30天拿下Python之文件操作

      Python是一种高级编程语言,它提供了许多内置函数和模块来处理文件操作,主要包括:打开文件、读取文件、写入文件、关闭文件、获取目录列表等。

      2025-05-14 10:33:16
      Python , 使用 , 函数 , 文件 , 权限 , 目录
      查看更多
      推荐标签

      作者介绍

      天翼云小翼
      天翼云用户

      文章

      33561

      阅读量

      5264800

      查看更多

      最新文章

      【Canvas技法】辐射式多道光影的实现

      2025-05-19 09:04:30

      外设驱动库开发笔记54:外设库驱动设计改进的思考

      2025-05-19 09:04:22

      C语言字符函数和字符串函数--(超全超详细)

      2025-05-19 09:04:14

      如何将一串数字用函数的方法倒过来(C语言)

      2025-05-16 09:15:24

      30天拿下Rust之高级类型

      2025-05-14 10:33:25

      C++ 11新特性之unique_ptr

      2025-05-14 10:33:16

      查看更多

      热门文章

      Python 函数调用父类详解

      2023-04-23 09:44:31

      C语言结构体与结构体指针的使用

      2023-03-08 10:38:36

      C++入门篇之C++ 指针

      2023-03-14 11:26:53

      指针(*)、取地址(&)、解引用(*)与引用(&)

      2023-04-10 08:54:19

      游戏编程之六 游戏编程的特点

      2024-09-25 10:13:46

      C#8.0新语法

      2023-02-07 10:34:04

      查看更多

      热门标签

      java Java python 编程开发 代码 开发语言 算法 线程 Python html 数组 C++ 元素 javascript c++
      查看更多

      相关产品

      弹性云主机

      随时自助获取、弹性伸缩的云服务器资源

      天翼云电脑(公众版)

      便捷、安全、高效的云电脑服务

      对象存储

      高品质、低成本的云上存储服务

      云硬盘

      为云上计算资源提供持久性块存储

      查看更多

      随机文章

      Java中的多态性详解

      【剑指offer】2.2编程语言(p22-p25)——面试题1:string赋值运算函数

      【Java并发面试】10道不得不会的Java并发基础面试题

      用go语言,给你一个下标从 0 开始的整数数组 nums ,它包含 n 个 互不相同 的正整数, 如果 nums 的一个排列满足以下条件,我们称它是一个特别的排列。

      如何入门Python——学习Python的指南针

      java.super详解

      • 7*24小时售后
      • 无忧退款
      • 免费备案
      • 专家服务
      售前咨询热线
      400-810-9889转1
      关注天翼云
      • 旗舰店
      • 天翼云APP
      • 天翼云微信公众号
      服务与支持
      • 备案中心
      • 售前咨询
      • 智能客服
      • 自助服务
      • 工单管理
      • 客户公告
      • 涉诈举报
      账户管理
      • 管理中心
      • 订单管理
      • 余额管理
      • 发票管理
      • 充值汇款
      • 续费管理
      快速入口
      • 天翼云旗舰店
      • 文档中心
      • 最新活动
      • 免费试用
      • 信任中心
      • 天翼云学堂
      云网生态
      • 甄选商城
      • 渠道合作
      • 云市场合作
      了解天翼云
      • 关于天翼云
      • 天翼云APP
      • 服务案例
      • 新闻资讯
      • 联系我们
      热门产品
      • 云电脑
      • 弹性云主机
      • 云电脑政企版
      • 天翼云手机
      • 云数据库
      • 对象存储
      • 云硬盘
      • Web应用防火墙
      • 服务器安全卫士
      • CDN加速
      热门推荐
      • 云服务备份
      • 边缘安全加速平台
      • 全站加速
      • 安全加速
      • 云服务器
      • 云主机
      • 智能边缘云
      • 应用编排服务
      • 微服务引擎
      • 共享流量包
      更多推荐
      • web应用防火墙
      • 密钥管理
      • 等保咨询
      • 安全专区
      • 应用运维管理
      • 云日志服务
      • 文档数据库服务
      • 云搜索服务
      • 数据湖探索
      • 数据仓库服务
      友情链接
      • 中国电信集团
      • 189邮箱
      • 天翼企业云盘
      • 天翼云盘
      ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
      公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
      • 用户协议
      • 隐私政策
      • 个人信息保护
      • 法律声明
      备案 京公网安备11010802043424号 京ICP备 2021034386号