c++编译期多态与运行期多态,显示接口和隐式接口

本文深入探讨了运行期多态和编译期多态的概念,解释了两者在C++中的实现机制,包括虚函数表和模板具现化。分析了各自的优缺点,如运行期多态在处理异质对象集合的优势,以及编译期多态在泛型编程中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

运行期多态(runtime polymorphism)

运行期多态的设计思想要归结到类继承体系的设计上去。对于有相关功能的对象集合,我们总希望能够抽象出它们共有的功能集合,在基类中将这些功能声明为虚接口(虚函数),然后由子类继承基类去重写这些虚接口,以实现子类特有的具体功能。典型地我们会举下面这个例子:
在这里插入图片描述
在这里插入图片描述
运行期多态的实现依赖于虚函数机制。当某个类声明了虚函数时,编译器将为该类对象安插一个虚函数表指针,并为该类设置一张唯一的虚函数表,虚函数表中存放的是该类虚函数地址。运行期间通过虚函数表指针与虚函数表去确定该类虚函数的真正实现。
运行期多态的优势还在于它使处理异质对象集合称为可能:
在这里插入图片描述
总结:运行期多态通过虚函数发生于运行期

编译期多态(compile-time polymorphism)

对模板参数而言,多态是通过模板具现化和函数重载解析实现的。以不同的模板参数具现化导致调用不同的函数,这就是所谓的编译期多态。
相比较于运行期多态,实现编译期多态的类之间并不需要成为一个继承体系,它们之间可以没有什么关系,但约束是它们都有相同的隐式接口。我们将上面的例子改写为:
在这里插入图片描述
在编译之前,函数模板中t.shout()调用的是哪个接口并不确定。在编译期间,编译器推断出模板参数,因此确定调用的shout是哪个具体类型的接口。不同的推断结果调用不同的函数,这就是编译器多态运行期多态与编译期多态优缺点分析

运行期多态优点

OO设计中重要的特性,对客观世界直觉认识。
能够处理同一个继承体系下的异质类集合。

运行期多态缺点

运行期间进行虚函数绑定,提高了程序运行开销。
庞大的类继承层次,对接口的修改易影响类继承层次。
由于虚函数在运行期在确定,所以编译器无法对虚函数进行优化。
虚表指针增大了对象体积,类也多了一张虚函数表,当然,这是理所应当值得付出的资源消耗,列为缺点有点勉强。

编译期多态优点

它带来了泛型编程的概念,使得C++拥有泛型编程与STL这样的强大武器。
在编译器完成多态,提高运行期效率。
具有很强的适配性与松耦合性,对于特殊类型可由模板偏特化、全特化来处理。

编译期多态缺点

程序可读性降低,代码调试带来困难。
无法实现模板的分离编译,当工程很大时,编译时间不可小觑。
无法处理异质对象集合。。这类似于重载函数在编译器进行推导,以确定哪一个函数被调用。

关于显式接口(explicit interfaces)与隐式接口(implicit interfaces)

通常显式接口由函数的签名式(也就是函数名称、参数类型、返回类型)构成
所谓的显式接口是指类继承层次中定义的接口或是某个具体类提供的接口,总而言之,我们能够在源代码中找到这个接口.显式接口以函数签名为中心,例如:
我们称shout为一个显式接口。在运行期多态中的接口皆为显式接口
在这里插入图片描述
隐式接口就完全不同了。它并不基于函数签名式,而是由有效表达式(valid expressions) 组成
例如:
在这里插入图片描述
对于anim来说,必须支持哪一种接口,要由模板参数执行于anim身上的操作来决定,在上面这个例子中,T必须支持shout()操作,那么shout就是T的一个隐式接口。

<think>我们被要求回答关于C++多态的分类,特别是静态多态动态多态的区别实现方。根据引用[1][2][3][4]的内容,我们可以总结如下:1.多态分为静态多态动态多态。2.静态多态编译期多态):在编译期间确定具体执行哪个函数。实现方包括函数重载模板(泛型编程)。3.动态多态(运行时多态):在程序运行时才能确定具体调用的函数。实现方主要是通过虚函数继承。接下来,我们详细说明两者的区别实现方。一、静态多态(StaticPolymorphism)静态多态也称为编译期多态,是指在编译阶段就确定了程序执行哪个函数。主要包括两种方:-函数重载(Overloading):在同一作用域内,函数名相同但参数列表不同(参数类型、个数、顺序不同),编译器根据调用时传递的参数类型来决定调用哪个函数。-模板(Templates):通过模板实现泛型编程,编译器根据模板参数的类型在编译时生成具体的代码。例:函数重载```cpp#include<iostream> usingnamespace std; voidprint(inti){cout <<"Integer: "<< i<< endl; }void print(double f) {cout<< "Float:" <<f <<endl;} intmain(){print(5);//调用voidprint(inti)print(5.5);//调用voidprint(doublef)return0; }```例:模板```cpp#include <iostream>using namespacestd;template <typename T> Tadd(T a, Tb){return a+ b; }int main() {cout<< add(1,2)<< endl;//调用add<int>(int,int)cout<< add(1.1,2.2)<< endl;//调用add<double>(double,double)return0; }```二、动态多态(Dynamic Polymorphism)动态多态也称为运行时多态,是指在程序运行时根据对象的实际类型来确定调用的函数。实现动态多态需要满足三个条件:-继承:存在基类派生类的继承关系。-虚函数:基类中使用虚函数声明,派生类中重写该虚函数。-基类指针或引用:使用基类的指针或引用指向派生类对象,并通过该指针或引用调用虚函数。例:```cpp#include <iostream>using namespacestd;class Shape{ public:virtual voiddraw(){//虚函数cout <<"Drawinga shape." <<endl;}}; classCircle :public Shape{ public:void draw() override{ //重写虚函数cout<< "Drawing acircle."<< endl;} };classRectangle: publicShape {public:voiddraw()override {cout<< "Drawing arectangle."<< endl;} };int main() {Shape* shape1 =new Circle();Shape*shape2= newRectangle();shape1->draw();//实际调用Circle::draw()shape2->draw();//实际调用Rectangle::draw()deleteshape1;delete shape2;return0; }```三、静态多态动态多态的区别根据引用[4]的内容,我们可以总结如下区别: |特性|静态多态|动态多态||--------------|------------------------------|------------------------------||决定时期|编译期|运行期||实现机制|函数重载、模板|虚函数、继承| |接口|接口(基于有效表达)|接口(基于函数签名)| |效率|较高(无运行时开销)|较低(有虚函数表查找开销)| |灵活性|较低(编译时确定)|较高(运行时绑定)| |类型检查|编译时类型检查|运行时类型检查(如dynamic_cast)|四、总结静态多态动态多态都是C++中实现多态性的重要手段,它们适用于不同的场景:-静态多态:在编译期确定,效率高,但缺乏运行时灵活性。适用于性能要求高且类型在编译期可确定的场景。-动态多态:在运行期确定,通过虚函数机制实现,提供了运行时的灵活性,支持面向对象设计中的开闭原则。适用于需要运行时动态绑定的场景,如框架设计、插件系统等。根据引用[2][3],动态多态通过虚函数表(vtable)实现,每个含有虚函数的类都有一个虚函数表,表中存放了虚函数的地址。当通过基类指针调用虚函数时,程序会根据实际对象的虚函数表找到正确的函数地址。最后,我们生成相关问题。</think>### C++ 多态分类:静态多态动态多态 #### 1. **静态多态编译期多态)** - **实现方**: - **函数重载**:同名函数根据参数列表不同实现不同功能 ```cpp void print(int i) { /*...*/ } // 整型版本 void print(double d) { /*...*/ } // 浮点版本 ``` - **模板编程**:编译器根据类型参数生成具体代码 ```cpp template <typename T> T add(T a, T b) { return a + b; } // 适用于int, double等类型 ``` - **特点**: - 在编译期确定具体调用的函数 - 无运行时开销,效率高 - 接口约束(基于有效表达) - **原理**: - 编译器通过名称修饰(name mangling)区分重载函数 - 模板实例化时生成类型特化代码 #### 2. **动态多态(运行时多态)** - **实现方**: - **虚函数机制**:通过虚函数表(vtable)实现 ```cpp class Base { public: virtual void draw() { /* 基类实现 */ } // 虚函数声明 }; class Derived : public Base { public: void draw() override { /* 派生类实现 */ } // 重写虚函数 }; // 使用基类指针调用 Base* obj = new Derived(); obj->draw(); // 运行时确定调用Derived::draw() ``` - **必要条件**: - 继承关系 - 虚函数声明(`virtual`关键字) - 基类指针/引用指向派生类对象 - **特点**: - 在运行时确定具体调用的函数 - 通过虚函数表实现动态绑定 - 有运行时开销(虚表查找) - 接口函数签名 #### 3. **核心区别** | 特性 | 静态多态 | 动态多态 | |--------------|------------------------------|------------------------------| | **决定时机** | 编译期 | 运行期 | | **实现机制** | 模板具现化/函数重载 | 虚函数/继承 | | **开销** | 无运行时开销 | 虚表查找开销 | | **接口** | (表达约束) | (函数签名) | | **灵活性** | 编译时类型绑定 | 运行时动态绑定 | | **典型应用** | STL算法、模板元编程 | 框架设计、插件系统 | #### 4. **技术原理对比** - **静态多态**: - 编译器生成多份特化代码 - 类型检查在编译期完成 - 错误在编译时暴露 - **动态多态**: - 虚函数表存储函数指针 ```mermaid graph LR A[对象] --> B[虚表指针] B --> C[虚函数表] C --> D[draw()地址] C --> E[resize()地址] ``` - 运行时通过对象头部的虚表指针查找函数地址 - 支持`dynamic_cast``typeid`运行时类型识别 > **关键结论**:静态多态通过编译期代码生成实现零开销抽象,适合性能敏感场景;动态多态通过运行期绑定实现灵活扩展,适合需要运行时不确定性的系统设计[^1][^2][^3][^4]。 --- ### 相关问题 1. 虚函数表(vtable)在C++中是如何具体实现的? 2. 模板元编程中如何实现编译期条件判断(类似if-else)? 3. 动态多态中的虚函数调用比普通函数调用慢多少? 4. 什么情况下应该优先选择静态多态而非动态多态? 5. C++20概念(concepts)如何增强静态多态的类型安全?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值