1. C++ 编译期多态与运行期多态

C++ 编译期多态与运行期多态

今日的C++不再是个单纯的“带类的C”语言,它已经发展成为一个多种次语言所组成的语言集合,其中泛型编程与基于它的STL是C++发展中最为出彩的那部分。在面向对象C++编程中,多态是OO三大特性之一,这种多态称为运行期多态,也称为动态多态;在泛型编程中,多态基于template(模板)的具现化与函数的重载解析,这种多态在编译期进行,因此称为编译期多态或静态多态。在本文中,我们将了解:

  • 什么是运行期多态
  • 什么是编译期多态
  • 它们的优缺点在哪
运行期多态

运行期多态的设计思想要归结到类继承体系的设计上去。对于有相关功能的对象集合,我们总希望能够抽象出它们共有的功能集合,在基类中将这些功能声明为虚接口(虚函数),然后由子类继承基类去重写这些虚接口,以实现子类特有的具体功能。典型地我们会举下面这个例子:

图片

class Animal
{
public :
    virtual void shout() = 0;
};
class Dog :public Animal
{
public:
    virtual void shout(){ cout << "汪汪!"<<endl; }
};
class Cat :public Animal
{
public:
    virtual void shout(){ cout << "喵喵~"<<endl; }
};
class Bird : public Animal
{
public:
    virtual void shout(){ cout << "叽喳!"<<endl; }
};

int main()
{
    Animal * anim1 = new Dog;
    Animal * anim2 = new Cat;
    Animal * anim3 = new Bird;

   //藉由指针(或引用)调用的接口,在运行期确定指针(或引用)所指对象的真正类型,调用该类型对应的接口
    anim1->shout();
    anim2->shout();
    anim3->shout();

    //delete 对象
    ...
   return 0;
}

运行期多态的实现依赖于虚函数机制当某个类声明了虚函数时,编译器将为该类对象安插一个虚函数表指针,并为该类设置一张唯一的虚函数表,虚函数表中存放的是该类虚函数地址。运行期间通过虚函数表指针与虚函数表去确定该类虚函数的真正实现

运行期多态的优势还在于它使处理异质对象集合称为可能:

//我们有个动物园,里面有一堆动物
int main()
{
    vector<Animal*>anims;

    Animal * anim1 = new Dog;
    Animal * anim2 = new Cat;
    Animal * anim3 = new Bird;
    Animal * anim4 = new Dog;
    Animal * anim5 = new Cat;
    Animal * anim6 = new Bird;

    //处理异质类集合
    anims.push_back(anim1);
    anims.push_back(anim2);
    anims.push_back(anim3);
    anims.push_back(anim4);
    anims.push_back(anim5);
    anims.push_back(anim6);

    for (auto & i : anims)
    {
        i->shout();
    }
    //delete对象
    //...
    return 0;
}

总结:运行期多态通过虚函数发生于运行期

编译期多态

对模板参数而言,多态是通过模板具现化和函数重载解析实现的。以不同的模板参数具现化导致调用不同的函数,这就是所谓的编译期多态。
相比较于运行期多态,实现编译期多态的类之间并不需要成为一个继承体系,它们之间可以没有什么关系,但约束是它们都有相同的隐式接口。我们将上面的例子改写为:

class Animal
{
public :
    void shout() { cout << "发出动物的叫声" << endl; };
};
class Dog
{
public:
     void shout(){ cout << "汪汪!"<<endl; }
};
class Cat
{
public:
     void shout(){ cout << "喵喵~"<<endl; }
};
class Bird
{
public:
     void shout(){ cout << "叽喳!"<<endl; }
};
template <typename T>
void  animalShout(T & t)
{
    t.shout();
}
int main()
{
    Animal anim;
    Dog dog;
    Cat cat;
    Bird bird;

    animalShout(anim);
    animalShout(dog);
    animalShout(cat);
    animalShout(bird);

    getchar();
}

在编译之前,函数模板中t.shout()调用的是哪个接口并不确定。在编译期间,编译器推断出模板参数,因此确定调用的shout是哪个具体类型的接口。不同的推断结果调用不同的函数,这就是编译器多态。这类似于重载函数在编译器进行推导,以确定哪一个函数被调用。

运行期多态与编译期多态优缺点分析
运行期多态优点
  • OO设计中重要的特性,对客观世界直觉认识。

  • 能够处理同一个继承体系下的异质类集合。

运行期多态缺点
  • 运行期间进行虚函数绑定,提高了程序运行开销。
  • 庞大的类继承层次,对接口的修改易影响类继承层次。
  • 由于虚函数在运行期在确定,所以编译器无法对虚函数进行优化。

虚表指针增大了对象体积,类也多了一张虚函数表,当然,这是理所应当值得付出的资源消耗,列为缺点有点勉强。

编译期多态优点
  • 它带来了泛型编程的概念,使得C++拥有泛型编程与STL这样的强大武器。

  • 在编译器完成多态,提高运行期效率。

  • 具有很强的适配性与松耦合性,对于特殊类型可由模板偏特化、全特化来处理。

编译期多态缺点
  • 程序可读性降低,代码调试带来困难。
  • 无法实现模板的分离编译,当工程很大时,编译时间不可小觑。
  • 无法处理异质对象集合。
关于显式接口与隐式接口

所谓的显式接口是指类继承层次中定义的接口或是某个具体类提供的接口,总而言之,我们能够在源代码中找到这个接口。显式接口以函数签名为中心,例如

void AnimalShot(Animal & anim)
{
    anim.shout();
}

我们称shout为一个显式接口。在运行期多态中的接口皆为显式接口。

而对模板参数而言,接口是隐式的,奠基于有效表达式。例如:

template <typename T>
void AnimalShot(T & anim)
{
    anim.shout();
}

m)
{
anim.shout();
}


我们称shout为一个显式接口。在运行期多态中的接口皆为显式接口。

而对模板参数而言,接口是隐式的,奠基于有效表达式。例如:

```c++
template <typename T>
void AnimalShot(T & anim)
{
    anim.shout();
}

对于anim来说,必须支持哪一种接口,要由模板参数执行于anim身上的操作来决定,在上面这个例子中,T必须支持shout()操作,那么shout就是T的一个隐式接口。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

澳大利亚昆士兰大学博士后—成瘾和青少年药物使用

澳大利亚昆士兰大学博士后—成瘾和青少年药物使用 昆士兰大学&#xff08;The University of Queensland&#xff09;&#xff0c;简称“昆大”“UQ”&#xff0c;该校始建于1909年&#xff0c;是一所位于澳大利亚昆士兰州的公立综合性大学。同时还是六所砂岩学府之一&#xff…

Linux ubuntu 写c语言Hello world

文章目录 创建hello.c 文件进入hello.c 文件使用vim 编辑器进行编辑下载gcc 编辑器调用gcc 进行编译hello.c 创建hello.c 文件 touch hello.c进入hello.c 文件 vi hello.c使用vim 编辑器进行编辑 下载gcc 编辑器 sudo apt update sudo apt install gcc第一个语句是更新&am…

7.2 支付模块 - 付费课程选课、支付

支付模块 - 付费课程选课、支付模型 文章目录 支付模块 - 付费课程选课、支付模型一、需求分析1.1 执行流程1.2 订单服务设计1.3 创建订单服务1.4 支付宝接口 说明1.5 支付宝下单执行流程 二、支付 准备2.1 Maven 坐标2.2 支付宝配置参数2.3 生成二维码2.3.1 Maven坐标2.3.2 工…

英伟达板子4----存储满了系统黑屏

记录一个bug&#xff0c;因为最近在做边缘端视频处理的内容&#xff0c;就把视频存储在边端设备&#xff0c;但是发现由于边缘端设备的存储太小了&#xff0c;导致把ubuntu端的存储&#xff08;只有28个Gib&#xff09;给吃满了。 然后搜了一篇博客说重启就能释放一些空间&…

任务调度新境界:探秘ScheduledExecutorService的异步魔力

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 任务调度新境界&#xff1a;探秘ScheduledExecutorService的异步魔力 前言ScheduledExecutorService的基本概念基本概念&#xff1a;为何它是 Java 中任务调度的首选工具&#xff1a;基本用法&#xf…

使用mapbox navigation搭建一个安卓导航 示例

一.代码示例地址&#xff1a; https://github.com/mapbox/mapbox-navigation-android-examples/tree/main 二. 具体步骤&#xff1a; git clone gitgithub.com:mapbox/mapbox-navigation-android-examples.git Go to app/src/main/res/values Look for mapbox_access_token.…

力扣每日一题 找出字符串的可整除数组 数论

Problem: 2575. 找出字符串的可整除数组 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 灵神题解 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1) Code class Solution {public int[] divisibilityArray(String word, int m){in…

多层菜单的实现方案(含HierarchicalDataTemplate使用)

1、递归 下面是Winform的递归添加菜单栏数据&#xff0c;数据设置好父子id方便递归使用 在TreeView的控件窗口加载时&#xff0c;调用递归加载菜单 private void LoadTvMenu(){this.nodeList objService.GetAllMenu(); // 通过Service得到全部数据// 创建一个根节点this.t…

Salesforce 2024财年爆发式增长!第一次现金分红

对于Salesforce来说&#xff0c;这是非凡的转型之年&#xff0c;所有的关键指标都表现强劲&#xff0c;现金流和利润增长创下了纪录。截至第四季度末&#xff0c;Salesforce的剩余履约价值&#xff08;RPO&#xff09;总额为569亿美元&#xff0c;同比增长17%。 Marc Benioff …

Unity引擎关于APP后台下载支持的实现问题

1&#xff09;Unity引擎关于APP后台下载支持的实现问题 2&#xff09;Prefab对DLL中脚本的引用丢失 3&#xff09;Unity DOTS资源加载问题 4&#xff09;UnitySendMessage和_MultiplyMatrixArrayWithBase4x4_NEON调用导致崩溃 这是第376篇UWA技术知识分享的推送&#xff0c;精选…

一文彻底搞懂从输入URL到显示页面的全过程

简略版&#xff1a; 用户输入URL后&#xff0c;浏览器经过URL解析、DNS解析、建立TCP连接、发起HTTP请求、服务器处理请求、接收响应并渲染页面、关闭TCP连接等步骤&#xff0c;最终将页面显示给用户。 详细版&#xff1a; URL解析&#xff1a;浏览器根据用户输入的URL&#x…

字符函数

1.字符分类函数 专门做字符分类的函数&#xff0c;都包含一个头文件#include <ctype.h> islower() 是一个用于判断字符是否为小写字母的函数。 通常情况下&#xff0c;如果一个字符是小写字母&#xff0c;则 islower() 函数会返回 true 或者一个表示真的值&#xff08…

STM32标准库——(19)PWR电源控制

1.PWR简介 PWR属于外设部分 调用时需要先开启时钟 2.电源框图 这个图可以分为三个部分&#xff0c;最上面是模拟部分供电叫做VDDA&#xff0c;中间是数字部分供电&#xff0c;包括两块区域&#xff0c;VDD供电区域和1.8v供电区域&#xff0c;下面是后备供电&#xff0c;叫做VB…

VM 虚拟机 ubuntu 解决无法连接网络问题

添加网卡法 就是在虚拟机的设置那里多增加一个网卡

python小白考教资(教资中的简单编程)

首先&#xff0c;写习惯了c语句的我&#xff08;虽然也会一丢丢&#xff09;&#xff0c;当然得深知python与C语言的一些简单的语句区别&#xff0c;这里为什么我要学习python呢&#xff0c;因为有些题目&#xff0c;python一句话就可以解决&#xff0c;但是以我的水平&#xf…

数据结构:AVL树

目录 1、AVL树的概念 2、二叉搜索树的功能与实现 1、AVL树节点定义 2、AVL树的插入 3、AVL树的旋转操作 1、左旋 2、右旋 3、左右旋 4、右左旋 3、AVL树完整代码实现 1、AVL树的概念 在前面的文章中&#xff0c;我们学过了二叉搜索树&#xff0c;二叉搜索树虽可以缩短查…

论文阅读_解释大模型_语言模型表示空间和时间

英文名称: LANGUAGE MODELS REPRESENT SPACE AND TIME 中文名称: 语言模型表示空间和时间 链接: https://www.science.org/doi/full/10.1126/science.357.6358.1344 https://arxiv.org/abs/2310.02207 作者: Wes Gurnee & Max Tegmark 机构: 麻省理工学院 日期: 2023-10-03…

142.乐理基础-音程的构唱练习

内容参考于&#xff1a;三分钟音乐社 上一个内容&#xff1a;141.乐理基础-男声女声音域、模唱、记谱与实际音高等若干问题说明-CSDN博客 本次内容最好去看视频&#xff1a; https://apphq3npvwg1926.h5.xiaoeknow.com/p/course/column/p_5fdc7b16e4b0231ba88d94f4?l_progra…

Python变量类型常用的函数【函数】

一、Python Number(数字)常用的函数 主要有math模块和cmath模块。 math模块&#xff1a;提供了许多对浮点数的数学运算函数。 cmath模块&#xff1a;提供了一些用于复数运算的函数。 使用两个模块里的函数时要先导入&#xff1a; import math查看math模块里的函数&#xff1a…

ky10 server 离线编译安装nginx

代码地址 https://gitcode.net/zengliguang/linux_video_audio_nginx_proxy.git 下载代码 查看服务器上下载的代码 编译安装 进入代码路径 cd /root/linux_video_audio_nginx_proxy 执行离线编译安装脚本 source centos7_nginx_offline_comp_install.sh安装编译相关依赖 …
最新文章