C++运行期多态和编译期多态(以不同的模板参数调用不同的函数)

2021-02-06    分类: 网站建设

在面向对象C++编程中,多态是OO三大特性之一,这种多态称为运行期多态,也称为动态多态;在泛型编程中,多态基于template(模板)的具现化与函数的重载解析,这种多态在编译期进行,因此称为编译期多态或静态多态。

该类对象安插一个虚函数表指针,并为类(基类和派生类)设置虚函数表,虚函数表中存放的是该类虚函数地址。同时,改写虚函数调用代码(函数指针调用)。运行期间通过虚函数表指针与虚函数表去确定该类虚函数的真正实现。

#include
using namespace std;
class Animal
{
public :virtual void shout() = 0;
};
class Dog :public Animal
{
public:virtual void shout(){ cout << "汪汪!"<shout();//虚函数的调用会被编译器转换为对虚函数表的访问Cat cat;// ② 编译器会为每个新建对象添加一函数指针,指向虚函数表animal = &catanimal->shout();//animal代表this指针,shout是虚函数// ③ 编译器会改写对象调用成员函数代码,改写成指针调用的形式Animal * anim3 = new Bird;anim3->shout();delete anim3;system("pause");return 0;
}
/*
汪汪!
喵喵~
叽喳!
*/

编译器会构建一张虚表( vtable ),每一个类都有自己独特的虚表。同时,在这个继承链上,编译器会为基类插入一个隐式的指针(一般是对象的首地址),指向虚表,称为__vptr。然后,子类继承父类时,会获得继承下来的__vptr,再根据自己的类的情况兼容(修改虚函数表里的值、发生偏移等。于是,当我们构建具体的类时,若是基类类型,__vptr就会指向父类的vtable,若是子类类型,__vptr就会指向子类的vtable。


下面再看一个实例来了解虚函数表,及对象的函数指针是如何指向虚函数表以及调用虚函数代码是如何改写的?

#include 
using namespace std;
class A
{
public:
 A(int _a1 = 1) : a1(_a1) { }
 virtual void f() { cout << "A::f" << endl; }
 virtual void g() { cout << "A::g" << endl; }
 virtual void h() { cout << "A::h" << endl; }
 ~A() {}
private:
 int a1;
};
class C : public A
{
public:
 C(int _a1 = 1, int _c = 4) :A(_a1), c(_c) { }
 virtual void f() { cout << "C::f" << endl; }
 virtual void g() { cout << "C::g" << endl; }
 virtual void h() { cout << "C::h" << endl; }
private:
 int c;
};
// 通过访问类对象的前4字节(32位编译器)找到虚函数表。
// 虚函数表最后一项用的是0,代表虚函数表结束。
typedef void(*FUNC)(); //重定义函数指针,指向函数的指针
void PrintVTable(long* vTable) //访问虚函数表
{
 if (vTable == NULL)
 {
 return;
 }
 cout << "vtbl:" << vTable << endl;
 int i = 0;
 for (; vTable[i] != 0; ++i)
 {
 printf("function : %d :0X%x->", i, vTable[i]);
 FUNC f = (FUNC)vTable[i];
 f(); //访问虚函数
 }
 cout << endl;
}
void main()
{A a1;long *p = (long *)(*(long*)&a1);PrintVTable(p);C c;long *p2 = (long *)(*(long*)&c);PrintVTable(p2);system("pause");
}
/*
vtbl:00471048
function : 0 :0X40105a->A::f
function : 1 :0X4012c6->A::g
function : 2 :0X4010b9->A::h
vtbl:00471070
function : 0 :0X4010eb->C::f
function : 1 :0X4011d1->C::g
function : 2 :0X401280->C::h
*/

不同的推断结果调用不同的函数,这就是编译器多态。这类似于重载函数在编译器进行推导,以确定哪一个函数被调用。

3 运行期多态与编译期多态优缺点分析

3.1 运行期多态优点

OO设计中重要的特性,对客观世界直觉认识。

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

3.2 运行期多态缺点

运行期间进行虚函数绑定,提高了程序运行开销。

庞大的类继承层次,对接口的修改易影响类继承层次。

由于虚函数在运行期在确定,所以编译器无法对虚函数进行优化。

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

3.3 编译期多态优点

它带来了泛型编程的概念,使得C++拥有泛型编程与STL这样的强大武器。

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

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

3.4 编译期多态缺点

程序可读性降低,代码调试带来困难。

无法实现模板的分离编译,当工程很大时,编译时间不可小觑。

无法处理异质对象集合。

新闻标题:C++运行期多态和编译期多态(以不同的模板参数调用不同的函数)
浏览路径:/news/99414.html

成都网站建设公司_创新互联,为您提供Google域名注册自适应网站定制网站面包屑导航手机网站建设

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

微信小程序开发