多态的定义:

同一操作作用与不同的对象,可以有不同的解释,产生不同的执行结果。

(1)编译时多态/静态联编
指联编工作在编译阶段完成,即在编译阶段确定了程序中的操作调用与执行该操作的代码间的关系,基于指向对象的指针类型或引用类型。
(今日的C++不再是个单纯的“带类的C”语言,它已经发展成为一个多种次语言所组成的语言集合,其中泛型编程与基于它的STL是C++发展中最为出彩的那部分。在面向对象C++编程中,多态是OO三大特性之一,这种多态称为运行期多态,也称为动态多态;在泛型编程中,多态基于template(模板)的具现化与函数的重载解析,这种多态在编译期进行,因此称为编译期多态或静态多态。)

(2)运行时多态/动态联编
指联编在程序运行时动态进行,对函数的调用基于对象的类型。

区别总结

代码

(1)编译时多态
1)函数重载

//编译时多态,函数调用与指针类型有关。
#include
using namespace std;
class A{
public:
void print(){cout<<“A”<<endl;}//函数重载
};
class B:public A{
public:
void print(){cout<<“B”<<endl;}//函数重载
};

int main(){
A *p;//指针p为A类型
A a;
B b;
p=&a;
p->print() ;
p=&b;
p->print();
return 0;
}
//输出结果:
A
A

2)模板具现化实例

#include
#include
using namespace std;
class animal{
public:
void voice(){
cout<<“动物叫声”<<endl;
}
};
class Cat:public animal{
public:
void voice(){
cout<<“喵喵~”<<endl;
}
};
class Dog:public animal{
public:
void voice(){
cout<<“汪汪!”<<endl;
}
};
class Bunny:public animal{
public:
void voice(){
cout<<“唧唧”<<endl;
}
};
template //类模板
void pVoice(T&tmp){
tmp.voice();
}

int main(){
animal anim;
Cat cat;
Dog dog;
Bunny bunny;
pVoice(anim);//编译器推测模板参数,以调用不同函数
pVoice(cat);
pVoice(dog);
pVoice(bunny);
return 0;
}
//运行结果:
动物叫声
喵喵~
汪汪!
唧唧

(2)运行时多态
1)使用基类指针指向派生类

//运行时多态,函数调用与指针所指对象类型有关
#include
using namespace std;
class A{
public:
virtual void print(){cout<<“A”<<endl;}//虚函数
};
class B:public A{
public:
virtual void print(){cout<<“B”<<endl;}//虚函数
};

int main(){
A *p;
A a;
B b;
p=&a;//指针p指向a , 基类 指针指向基类
p->print() ;
p=&b;//指针p指向b ,基类指针指向子类 ,调用子类虚函数
p->print();
return 0;
}
//运行结果:
A
B

使用基类引用

#include
using namespace std;
class animal{
public:
virtual void voice(){
cout<<“动物叫声”<<endl;
}
};
class Cat:public animal{
public:
virtual void voice(){
cout<<“喵喵~”<<endl;
}
};
class Dog:public animal{
public:
virtual void voice(){
cout<<“汪汪!”<<endl;
}
};
class Bunny:public animal{
public:
virtual void voice(){
cout<<“唧唧”<<endl;
}
};

void pVoice(animal&tmp){//对基类的引用
tmp.voice();
}

int main(){
Cat cat;
Dog dog;
Bunny bunny;
pVoice(cat);
pVoice(dog);
pVoice(bunny);

return 0;

}
//运行结果如下:
喵喵~
汪汪!
唧唧

异质集合处理

#include
#include
using namespace std;
class animal{
public:
virtual void voice(){
cout<<“动物叫声”<<endl;
}
};
class Cat:public animal{
public:
virtual void voice(){
cout<<“喵喵~”<<endl;
}
};
class Dog:public animal{
public:
virtual void voice(){
cout<<“汪汪!”<<endl;
}
};
class Bunny:public animal{
public:
virtual void voice(){
cout<<“唧唧”<<endl;
}
};

int main(){
vector<animal*>anims;//使用容器
Dog d;Cat c;Bunny b;
animaldog=&d;
animal
cat=&c;
animal*bunny=&b;
//处理异质类集合

anims.push_back(cat);
anims.push_back(dog);
anims.push_back(bunny);
for(int i;i<anims.size();++i)anims[i]->voice();
return 0;

}
//运行结果如下:
喵喵~
汪汪!
唧唧

附加:
运行时多态要求派生类与基类对应的虚函数同时具有相同的:
1函数名称
2函数参数类型、个数和顺序
3返回值
如果不满足以上条件,派生类的虚函数将丢失其虚特性,在调用时采用编译时多态。

范例代码如下:

#include
using namespace std;

class base{
public:
virtual void fun1(){cout<<“baseFun1”<<endl;}
virtual void fun2(){cout<<“baseFun2”<<endl;}
void fun3(){cout<<“basefun3”<<endl;}
void fun4(){cout<<“baseFun4”<<endl;}
virtual void fun5(){cout<<“baseFun5”<<endl;}
};

class derived:public base{
public:
virtual void fun1(){cout<<“derivedFun1”<<endl;}//运行时多态。基类派生类同定义为虚函数
virtual void fun2(int x){cout<<“derivedFun2”<<endl;}//编译时多态。虽然基类、派生类均定义为虚函数,但是两者具有不同的参数个数 ,
//因而派生类虚函数丢失其虚特性,采用编译时多态
virtual fun3(){cout<<“derivedFun3”<<endl;}//编译时多态。基类为一般成员函数,派生类定义为虚函数,但以基类说明的特性为标准。
void fun4(){cout<<“derivedFun4”<<endl;}//编译时多态。基类、派生类均为一般成员函数
void fun5(){cout<<“derivedFun5”<<endl;}//运行时多态。基类为虚函数,派生类为一般成员函数,仍然以基类说明为准
};

int main(){
base *p;
base a;derived b;
p=&b;
p->fun1();
p->fun2();
p->fun3();
p->fun4();
p->fun5();
return 0;
}
//输出结果如下:
derivedFun1
baseFun2
baseFun3
baseFun4
derivedFun5
————————————————
版权声明:上文为CSDN博主「簇僵僵」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/pilipilipan/article/details/79675653

下述为另一篇,主要介绍编译期多态及运行期多态的优缺点

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
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
void AnimalShot(T & anim)
{
anim.shout();
}
对于anim来说,必须支持哪一种接口,要由模板参数执行于anim身上的操作来决定,在上面这个例子中,T必须支持shout()操作,那么shout就是T的一个隐式接口。

c++多态之 运行时多态与编译时多态相关推荐

  1. 常见的运行时异常与编译时异常举例

    常见的运行时异常与编译时异常举例 参考文章: (1)常见的运行时异常与编译时异常举例 (2)https://www.cnblogs.com/william-dai/p/9255158.html (3) ...

  2. Java——异常——运行时异常与编译时异常

    Java--异常--运行时异常与编译时异常 ①编译时异常 1.经常会在程序编译时产生一些异常,而这些异常必须要进行处理,这种异常被称为编译时异常,也称为checked异常. 2.在Java中,Exce ...

  3. 2022Java学习笔记七十三(异常处理:运行时异常、编译时异常、异常的默认处理的流程)

    2022Java学习笔记七十三(异常处理:运行时异常.编译时异常.异常的默认处理的流程) 一.异常体系 1.Exception:java.lang包下,称为异常类,它表示程序本身可以处理的问题 2.R ...

  4. 基于ubuntu-20.04.3的snort+Barnyard2+BASE的入侵检测系统安装时Barnyard2反编译时出错解决

    基于ubuntu-20.04.3的snort+Barnyard2+BASE的入侵检测系统安装时Barnyard2反编译时出错解决 在按照https://www.modb.pro/db/159797大佬 ...

  5. 【Groovy】编译时元编程 ( 编译时方法注入 | 使用 buildFromSpec、buildFromString、buildFromCode 进行方法注入 )

    文章目录 一.在 MyASTTransformation#visit 方法中进行方法注入 1.使用 new AstBuilder().buildFromSpec 进行方法注入 2.使用 new Ast ...

  6. 【Groovy】编译时元编程 ( 编译时方法拦截 | 在 MyASTTransformation#visit 方法中进行方法拦截 )

    文章目录 一.在 MyASTTransformation#visit 方法中进行方法拦截 二.完整代码示例及进行编译时处理的编译过程 1.Groovy 脚本 Groovy.groovy 2.ASTTr ...

  7. 动态so库的链接:运行时链接和编译时链接

    在编写Makefile时,如果一个程序需要链接so库,则需要通过两个步骤来完成,分为运行时动态库的链接和编译时动态库的链接,缺一不可. 1.运行时动态库的链接 在执行可执行文件时,提示: error ...

  8. vs编译c语言停止工作运行库mt,vc++编译时运行库选择(/MT/MTd/MD/MDd)

    vc++编译时运行库选择(/MT/MTd/MD/MDd) vc++编译时运行库选择(/MT./MTd./MD./MDd) 在vs中,项目属性 ->C/C++ ->代码生成 ->运行库 ...

  9. 【Groovy】编译时元编程 ( 编译时元编程引入 | 声明需要编译时处理的类 | 分析 Groovy 类的 AST 语法树 )

    文章目录 一.编译时元编程引入 二.声明需要编译时处理的类 三.分析 Groovy 类的 AST 语法树 一.编译时元编程引入 在之前的 " [Groovy]MOP 元对象协议与元编程 &q ...

  10. 【Android APT】编译时技术 ( 开发编译时注解 )

    文章目录 一.编译时注解 二.编译时注解 使用 三.注解的保留时间 四.博客资源 一.编译时注解 上一篇博客 [Android APT]编译时技术 ( 编译时注解 和 注解处理器 依赖库 ) 中创建并 ...

最新文章

  1. 碰到IBM笔记本电脑无线网络故障确认与排除方法
  2. 颜色传感器TCS230的使用
  3. VS Code插件之Cordova Tools
  4. 免费python基础笔记_python基础笔记(一)
  5. 发布python项目到pypi,以便供他人用pip install安装
  6. 域控服务器取消验证_Linux 开发笔记《使用CentOS 7进行初始服务器设置》
  7. C++自学11:命名空间(using namespace)
  8. 计算机编程思想 —— 缓存
  9. C++ msdn 离线版下载地址
  10. 【课程·研】工程伦理学 | 课堂汇报:个性化推荐技术的伦理学——以平台广告精准投放事件为例
  11. ketchup 消息队列rabbitmq使用
  12. 高效能人士的七个习惯-第四章-阅读
  13. 我和Ruby之父松本行弘的握手
  14. 无监督学习 聚类分析②
  15. M1 Mac安装PostgreSQL
  16. 华为程序员,985本科36岁,被公司解约:中年人路在何方?
  17. 安装Xshell遇到 由于找不到MSVCR110.dll,无法继续执行代码。重新安装程序可能会解决此问题
  18. 音乐播放器实现歌词同步
  19. 很高兴顺利通过PMP考试!
  20. 计算机第三章ppt课件,计算机英语第三章课件.ppt

热门文章

  1. 龙芯中科科创板上市:市值357亿 成国产CPU第一股
  2. 超级解霸, 远去的豪杰
  3. MySQL InnoDB如何解决幻读?
  4. 在word文档中添加目录(包括项目符号、编号、多级列表使用方法)
  5. 大数据多维分析常用操作图解 OLAP Operations
  6. Java阿凯_彩虹城堡之七彩宝石篇最终版攻略
  7. Kotlin use函数的魔法
  8. 电脑和手机连接同一个无线,电脑不能上网,手机能上网?
  9. GF(Go Frame)开发框架
  10. 小米8 SE刷机安卓13