当前位置:主页 > 软件编程 > C代码 >

C++多态的实现机制深入理解

时间:2021-04-15 11:17:25 | 栏目:C代码 | 点击:

在面试过程中C++的多态实现机制经常会被面试官问道。大家清楚多态到底该如何实现吗?下面小编抽空给大家介绍下多态的实现机制。

1. 用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数。

2. 存在虚函数的类都有一个一维的虚函数表叫做虚表。类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的。

3. 多态性是一个接口多种实现,是面向对象的核心。分为类的多态性和函数的多态性。

4. 多态用虚函数来实现,结合动态绑定。

5. 纯虚函数是虚函数再加上= 0。

6. 抽象类是指包括至少一个纯虚函数的类。

多态的简单介绍

一般来说,多态分为两种,静态多态动态多态。静态多态也称编译时多态,主要包括模板和重载。而动态多态则是通过类的继承和虚函数来实现,当基类和子类拥有同名同参同返回的方法,且该方法声明为虚方法,当基类对象,指针,引用指向的是派生类的对象的时候,基类对象,指针,引用在调用基类的方法,实际上调用的是派生类方法。这就是动态多态。

静态多态的实现

静态多态靠编译器来实现,简单来说就是编译器对原来的函数名进行修饰,在c语言中,函数无法重载,是因为,c编译器在修饰函数时,只是简单的在函数名前加上下划线"_" 。而c++编译器不同,它根据函数的类型,个数来对函数名进行修饰,这就使得函数可以重载,同理,模板也是可以实现的,针对不同类型的实参来产生对应的特化的函数,通过增加修饰,使得不同的类型参数的函数得以区分。

以下段程序为例

#include <iostream>
using namespace std;
template <typename T1, typename T2>
int fun(T1 t1, T2 t2){}
int foofun(){}
int foofun(int){}
int foofun(int , float){}
int foofun(int , float ,double){}
int main(int argc, char *argv[])
{
fun(1, 2);
fun(1, 1.1);
foofun();
foofun(1);
foofun(1, 1.1);
foofun(1, 1.1, 1.11);
return 0;
}

经过编译之后:

只选取main函数部分来看:

可以发现,调用的函数名均发生了变化,都加了相应的修饰,使得调用的函数是不一样的,静态多态就是如此。

动态多态的实现

声明一个类时,如果类中有虚方法,则自动在类中增加一个虚函数指针,该指针指向的是一个虚函数表,虚函数表中存着每个虚函数真正对应的函数地址。动态多态采用一种延迟绑定技术,普通的函数调用,在编译期间就已经确定了调用的函数的地址,所以无论怎样调用,总是那个函数,但是拥有虚函数的类,在调用虚函数时,首先去查虚函数表,然后在确定调用的是哪一个函数,所以,调用的函数是在运行时才会确定的。

在声明基类对象时,虚函数表中绑定的就是基类的方法的地址。在声明派生类对象时,虚函数表中绑定的就是派生类的方法。在对象被创建之后(以指针为例),无论是基类指针还是派生类指针指向这个对象,虚函数表是不会改变的。
以下段程序为例:

#include <iostream>
using namespace std;
class Base
{
public:
virtual void fun()
{
cout << "this is base fun" << endl;
}
};
class Derived : public Base
{
public:
void fun()
{
cout << "this is Derived fun" << endl;
}
};
int main(int argc, char *argv[])
{
Base b1;
Derived d1;
Base *pb = &d1;
Derived *pd = (Derived *)&b1;
b1.fun();
pd->fun();
d1.fun();
pb->fun();
return 0;
}

运行结果如下:

从结果可以看出,当一个对象被创建之后,在调用虚函数的时候,无论是派生类指针还是基类指针指向这个对象,调用虚函数的结果是一样的。因为,虚函数表是不变。当然,有可能在多继承中会有多个虚函数表从而导致函数调用时调用不同的虚函数表,这里不做考虑。

您可能感兴趣的文章:

相关文章