什么是多态?

多态(polymorphism)一词最初来源于希腊语polumorphos,含义是一种物质的多种形态。

在专业术语中,多态是一种运行时绑定机制(run-time binding) ,通过这种机制,实现将函数名绑定到函数具体实现代码的目的。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

 

多态的目的

根据赋值兼容,用基类类型的指针指向派生类,就可以通过这个指针来使用派生类的成员函数。如果这个函数是普通的成员函数,通过基类类型的指针访问到的只能是基类的同名成员。而如果将它设置为虚函数,则可以使用基类类型的指针访问到指针正在指向的派生类的同名函数。这样,通过基类类型的指针,就可以使属于不同派生类的不同对象产生不同的行为,从而实现运行过程的多态。

(“当通过指针或是引用使用派生类的一个对象时,此对象可以被当作是一个基类的对象”(但此时可能需要进行对象类型的判断))

#include <iostream>
using namespace std;
class A
{
public :
    void Print( )
    {
        cout<<"A::Print"<<endl ;
    }
};
class B:public A
{
public :
    void Print( )
    {
        cout<<"B::Print"<<endl;
    }
};
int main( )
{
    A a;
    B b;
    A *pA = &b;//基类型的指针指向派生类
    pA->Print( );
    return 0;
}

运行结果:

A::Print

 

通过指针调用成员函数只与指针类型有关,与此刻指向的对象无关。基类指针无论指向基类还是派生类对象,利用pA->Print()调用的都是基类成员函数Print()。若要调用派生类中的成员函数Print()必须通过对象来调用,或定义派生类指针实现。这种通过用户自己指定调用成员函数,在编译时根据类对象来确定调用该类成员函数的方式,是静态绑定。
若将A类中的Print( )函数声明为virtual,则此时就为动态绑定Dynamic binding
程序执行结果为:
        B::Print

class A
{
public :
    virtual void Print( )
    {
        cout<<"A::Print"<<endl ;
    }
};
class B:public A
{
public :
    void Print( )
    {
        cout<<"B::Print"<<endl;
    }
};

 

 

虚函数(Virtual functions)允许程序员在基类中声明一个函数,然后在各个派生类中对其进行重定义(redefined). 编译器与装入器将保证对象及其所调用函数的正确对应。这里的 redefine 叫做 Overriding(覆盖,又称重置)。这是C++语言中唯一的一种动态绑定(Dynamic binding)机制。

 

动态绑定和静态绑定

绑定是程序自身彼此关联的过程,确定程序中的操作调用与执行该操作的代码间的关系。例如把一个标示符名和一个存储地址联系在一起的过程。
用面向对象的术语讲,就是把一条消息和一个对象的方法相结合的过程。
按照绑定进行的阶段的不同,可以分为静态绑定和动态绑定两种。

 

1.静态绑定:绑定工作在编译连接阶段完成。
因为绑定过程是在程序开始执行之前进行的,因此也称为早期绑定或前绑定。
在编译、连接过程中,系统就可以根据类型匹配等特征确定程序中操作调用与执行该操作代码的关系,即确定了某一个同名标识到底是要调用哪一段程序代码。
C++中,除虚函数外,其他函数均是静态绑定的。

重载函数是静态绑定

普通函数重载与静态联编:

void print(char)
void print(char *)
void print(float)
void print(int)
……
print("Hello, overload!");

 

成员函数重载与静态联编: 

class MyClass
{
public:
    MyClass( );
    MyClass(int i);
    MyClass(char c);
};
MyClass c2(34);

 

2.动态绑定:和静态绑定相对应,绑定工作在程序运行阶段完成的。
以下面的代码说明:

class A
{
public:
    virtual void Get( );
};
class B : public A
{
public:
    virtual void Get( );
};
void MyFunction( A * pa )
{
    pa->Get( );
}

pa->Get( ) 调用的是 A::Get( )还是B::Get( ),编译时无法确定,因为不知道MyFunction被调用时,形参会对应于一个 A 对象还是B对象。
所以只能等程序运行到 pa->Get( )了,才能决定到底调用哪个Get( )。

动态绑定的实现需要如下三个条件:
(1)调用的函数是虚函数
(2)调用虚函数操作的是指向对象指针或者对象引用;或者是由成员函数调用虚函数;
(3)派生类型关系的建立。

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄