立即前往

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
查看全部活动
热门活动
  • 智算采购季 热销S6云服务器2核4G限时88元/年起,部分主机可加赠对象存储组合包!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 一键部署Llama3大模型学习机 0代码一键部署,预装最新主流大模型Llama3与StableDiffusion
  • 中小企业应用上云专场 产品组合下单即享折上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云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
    • 关系数据库SQL Server版
    • 企业主机安全
    • 云防火墙
    • CDN加速
    • 物理机
    • GPU云主机
    • 天翼云电脑(政企版)
    • 天翼云电脑(公众版)
    • 云主机备份
    • 弹性云主机
      搜索发现
      关系数据库SQL Server版企业主机安全云防火墙CDN加速物理机GPU云主机天翼云电脑(政企版)天翼云电脑(公众版)云主机备份弹性云主机
    • 文档
    • 控制中心
    • 备案
    • 管理中心
    • 登录
    • 免费注册

    C++虚函数知识点总结

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

    C++虚函数知识点总结

    2023-02-21 06:21:46 阅读次数:539

    虚函数,c++

    C++虚函数知识点总结

    • 虚函数

    • 虚函数的继承

    • 虚函数原理——虚函数表

    • 使用继承的虚函数表

    • 子类虚函数表

    • 使用多重继承的虚函数表

    • 虚函数的修饰

    • final

    • override

    • 父类的虚析构函数

    • 纯虚函数与抽象类

    虚函数

    注意:

    • 在函数声明的返回类型之前加virtual。

    • 并且只在函数的声明中添加virtual,在该成员函数的实现中不用加。


    虚函数的继承

    • 如果某个成员函数被声明成虚函数,那么他的子类,以及子类中的子类 ,所计继承的这个成员函数,也自动是虚函数。

    • 如果在子类中重写这个虚函数,可以不用再加virtual,但仍然建议加上virtual,提高代码的可读性。


    虚函数原理——虚函数表

    对应虚函数的类,该类的对象所占内存大小为,数据成员的大小+一个指向虚函数表指针 (4字节)。

    例如:如下所示Father类所创建的对象

    class Father
    {
    public:
      virtual void func1()
      {
        cout << "虚函数func1" << endl;
      }
      virtual void func2()
      {
        cout << "虚函数func2" << endl;
      }
      virtual void func3()
      {
        cout << "虚函数func3" << endl;
      }
      void func4()
      {
        cout << "非func4" << endl;
      }
    public:
      int x = 200;
      int y = 300;
      static int z;
    };
    int Father::z = 0;
    Father father;
    cout<<sizeof(father)<<endl;

    结果为12,两个int的数据成员4+4一共占了8个字节,再加上一个虚函数表指针(4个字节),一共是12个字节

    ( 如果该类中没有虚函数,就没有虚函数表指针,也就少4个字节)

    如下图所示:

    C++虚函数知识点总结

    C++虚函数知识点总结

    思考:它尽然是个指针,那我们就能通过这个指针来访问它所指向内存所对应的内容。

    (先存的是虚函数表指针,然后才是数据成员。)

    所以说,对象地址就是虚函数表地址。

    cout<<(int*)&father<<endl;

    强转成指针。

    接着,取出虚函数表的指针。

    int* vptr = (int*)*(int*)(&father);

    为了编译器能通过,前面加上int*。

    然后,就找到了虚函数,并执行方法。

    为了便于调用,这里定义个函数指针类型。

    typedef void(*func_t)(void);

    func_t指针,指向参数为void,返回值为void的函数。

    调用虚函数。

    ((func_t)*(vptr))();
    ((func_t)*(vptr + 1))();
    ((func_t)*(vptr + 2))();

    调用成功。

    接着调用x,y两个数据成员。

    cout << *(int*)((int)&father+ 4) << endl;
    cout << *(int*)((int)&father+ 8) << endl;

    取到地址,转成int整数,加上偏移量,通过编译器加上(int*),再解引用,得到里面的值。

    (+上偏移量要先转成int)

    多态的使用:父类指针指向子类对象

    Father* father1 = &son;
    father1->Func1();//调用对应的func1函数,son中的

    使用继承的虚函数表

    在上面的基础上,为Father类添加一个派生类。并且对Father的func1进行重写,再添加一个它独有的func5,声明为虚函数。

    class Son :public Father
    {
    public:
      virtual void  Func1()
      {
        cout << "Son Func1()" << endl;
      }
      virtual void Func5()
      {
        cout << "Son Func5" << endl;
      }
    };

    C++虚函数知识点总结

    同上面通过使用指向虚函数表的指针来访问对应的内容

    for (int i = 0; i < 4; i++)
      {
        //取到这个地址的内容,然后通过自定义指针类型转换,调用该函数,加()
        ((func_t) * (vptr + i))();
      }
    // 访问两个成员
    cout << *(int*)((int)&son + 4) << endl;
    cout << *(int*)((int)&son + 8) << endl;

    子类虚函数表

    1. 直接复制父类的虚函数表

    C++虚函数知识点总结


    1. 如果子类重写了父类的某个虚函数,那么就在这个虚函数表中进行相应的替换

    2. C++虚函数知识点总结

    3. 如果子类中添加的新的虚函数,就把这个虚函数添加到虚函数表中(尾部添加)

    C++虚函数知识点总结

    使用多重继承的虚函数表

    在上面的基础上再添加一个Mother类

    class Mother
    {
    public:
      virtual void handle1()
      {
        cout << "Monther handle1" << endl;
      }
      virtual void handle2()
      {
        cout << "Monther handle2" << endl;
      }
      virtual void handle3()
      {
        cout << "Monther handle3" << endl;
      }
    public://便于测试,所以权限定为public
      int m = 400;
      int n = 500;

    此时的Son类对象

    vs编译器中把子类自己的虚函数放到了第一个父类的虚函数表最后

    C++虚函数知识点总结

    同样通过指针访问对应的虚函数表内容

    Son son;
    cout << (int*)&son << endl;
    //第一个虚函数表指针
    int* vptr1 = (int*)*(int*)&son;
    for (int i = 0; i < 4; i++)
    {
      ((func_t)*(vptr1 + i))();
    }
    // x y 
    for (int i = 0; i < 2; i++)
    {
      cout << *(int*)((int)&son + 4 + 4 * i) << endl;
    }
    
    //第二盒个虚函数表指针
    int* vptr2 = (int*)*((int*)&son + 3);//取出来的是指向第二个虚函数表的指针 
    for (int i = 0; i < 3; i++)
    {
      ((func_t)*(vptr2 + i))();
    }
    //m n
    for (int i = 0; i < 2; i++)
    {
      cout << *(int*)((int)&son + 16 + i * 4) << endl;
    }

    小补充:

    对象地址+偏移量

    转化int类型 + 对应的字节个数

    转化int*类型 + 走几步(几个步长)


    虚函数的修饰

    final

    final——C++11更新

    1.用来修饰类,让该类不能被继承。

    class XiaoMi
    {
    
    };
    class XiaoMi2 final:XiaoMi
    {
    
    };
    class XiaoMi3 :XiaoMi3//报错——XiaoMI2不能被继承
    {
    
    };

    (补充:C++默认继承方式为private)

    2.用来修饰虚函数,使得该虚函数在子类中,不得被重写。但是还可以使用。

    override

    override仅能修饰虚函数。

    只能用在函数的声明,函数的实现不要写。

    作用:

    1. 提示程序的阅读者,这个函数是重写父类的功能。

    2. 防止程序员在重写父类的函数时,把函数名写错。

    父类的虚析构函数

    把father类的指针定义为virtual时,并且对父类的指针执行delete操作时, 就是对该指针使用"动态析构"。

    如果这个指针指向的是子类对象,那么会先调用该子类的析构函数,再调用父类的析构函数。

    如果指向的是父类对象,那么只调用父类的析构函数。

    注意:
    为了防止内存泄露,最好在基类的虚构函数上添加virtual关键字,使基类析构函数为虚函数。

    纯虚函数与抽象类

    什么时候使用纯虚函数?

    某些类,现实项目和实现角度吗,都不需要实例化(不需要创建它的对象)。

    这个类中定义的某些成员函数只是为了提供一个形式上的接口,准备让自子类来做具体的实现。

    此时这个函数就可以定义为"纯虚函数",包含纯虚函数的类,就叫做抽象类(不能创建对象)。

    继承该抽象类的子类如果不重写这个纯虚函数,那么它也是不能创建对象的。

    用法:
    virtual + = 0

    代码示例:

    #include<iostream>
    #include<string>
    using namespace std;
    class Shape
    {
    public:
      Shape(const string& color = "White")
      {
        this->color = color;
      }
      virtual float area() = 0;
      ~Shape()
      {
    
      }
    private:
      string color;
    };
    
    class Circle :public Shape
    {
    public:
      Circle(float radius = 0, const string& color = "White") :Shape(color), r(radius)
      {
    
      }
      virtual float area()
      {
        return 3.14 * r * r;
      }
      ~Circle()
      {
    
      }
    private:
      float r;
    };
    int main(void)
    {
      Circle c1(3);
      cout << c1.area() << endl;
      return 0;
    }

    纯虚函数的注意事项:
    父类声明为某纯虚函数之后,它的子类:

    1. 实现这个纯虚函数

    2. 继续把这个纯虚函数声明为纯虚函数,这个子类也称为抽象类

    3. 不对这个纯虚函数做任何处理,等效于上一种情况(不推荐)

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

    上一篇:Python:kazoo模块与Zookeeper交互

    下一篇:C++ new和malloc区别

    相关文章

    2025-04-14 09:26:51

    【算法入门08】青蛙跳台阶

    【算法入门08】青蛙跳台阶

    2025-04-14 09:26:51
    c++ , 动态规划 , 算法
    2025-04-14 09:26:51

    STL详解(九)—— priority_queue的使用与模拟实现

    优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中的元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。

    2025-04-14 09:26:51
    c++ , stl , 数据结构
    2025-04-14 09:26:51

    STL详解(八)—— stack和queue的模拟实现

    stack和queue有一点需要注意的是,虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和queue只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque容器。

    2025-04-14 09:26:51
    c++ , queue , stack , stl
    2025-04-14 09:24:23

    【算法入门09】矩形覆盖

    【算法入门09】矩形覆盖

    2025-04-14 09:24:23
    c++ , 动态规划 , 算法
    2025-04-14 09:24:23

    【算法入门14】二叉树的镜像

    【算法入门14】二叉树的镜像

    2025-04-14 09:24:23
    c++ , 算法
    2025-04-01 10:29:12

    golang与 C++数据结构类型对应关系是怎样的?

    uintptr和unsafe.Pointer相当于c++的void*,也就是任意指针。

    2025-04-01 10:29:12
    c++ , golang , 函数指针 , 数据结构
    2025-03-31 08:49:25

    MFC编程 -- 判断是否按下ctrl和shift键

    MFC编程 -- 判断是否按下ctrl和shift键

    2025-03-31 08:49:25
    c++ , mfc , 开发语言
    2025-03-31 08:48:59

    MFC编程 -- 读取文件内容

    MFC编程 -- 读取文件内容

    2025-03-31 08:48:59
    c++ , mfc
    2025-03-31 08:48:59

    MFC编程 -- 浮点数与字符串之间转换

    MFC编程 -- 浮点数与字符串之间转换

    2025-03-31 08:48:59
    c++ , mfc , 字符串 , 浮点数
    2025-03-24 08:52:59

    VC++CopyFile函数的用法

    VC++CopyFile函数的用法

    2025-03-24 08:52:59
    c++
    查看更多
    推荐标签

    作者介绍

    天翼云小翼
    天翼云用户

    文章

    32777

    阅读量

    4801110

    查看更多

    最新文章

    golang与 C++数据结构类型对应关系是怎样的?

    2025-04-01 10:29:12

    MFC编程 -- 判断是否按下ctrl和shift键

    2025-03-31 08:49:25

    MFC编程 -- 读取文件内容

    2025-03-31 08:48:59

    MFC编程 -- 浮点数与字符串之间转换

    2025-03-31 08:48:59

    VC++CopyFile函数的用法

    2025-03-24 08:52:59

    C++和C#中数组的比较

    2025-03-24 08:52:53

    查看更多

    热门文章

    Lambda函数

    2023-02-08 10:33:56

    QT中多线程的使用

    2023-02-07 10:34:04

    0030 简单的四则运算 c/c++

    2023-03-21 10:39:47

    (10)Qt对象模型

    2023-02-13 07:55:59

    Essential C++  第5章 面向对象编程风格

    2023-02-13 07:44:18

    【C&C++】二进制数据的位运算(如何存储字符)

    2023-04-10 08:53:37

    查看更多

    热门标签

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

    相关产品

    弹性云主机

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

    天翼云电脑(公众版)

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

    对象存储

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

    云硬盘

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

    查看更多

    随机文章

    C语言解题 || 数字字符串转整数

    C++ 获取MacOS版本号

    只需要传实参的地址就能修改实参吗?以二级指针与一级指针为例,进行讲解

    hdu 1166:敌兵布阵(C++_树状数组(单点修改+区间查询))

    PAT_Advanced Level_1030 Travel Plan(C++_链式向前星_dfs_回溯)

    C语言解题 || 矩阵转置

    • 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号