什么是多态?

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

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

多态的目的

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

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

#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 ;
}

运行结果:

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();

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

class TradesPerson
{
public:
virtual void sayHi()
{
cout << "Just hi." << endl;
}
void run()
{
cout << "Base::run " << endl;
}
};
class Tinker : public TradesPerson
{
public:
virtual void sayHi()
{
cout << "Tinker." << endl;
}
void run()
{
cout << "Tinker::run " << endl;
}
};
class Tailor : public TradesPerson
{
public:
void sayHi()
{
cout << "Tailor." << endl;
}
void run()
{
cout << "Tailor::run " << endl;
}
};
int main( )
{
TradesPerson* p;
int which ;
cout << "1 == TradesPerson, 2 == Tinker, 3 == Tailor ";
cin >> which;
switch( which )
{
case :
p = new TradesPerson;
break;
case :
p = new Tinker;
break;
case :
p = new Tailor;
break;
}
p->sayHi();///动态绑定,运行结果不可知
p->run();///无virtual,不是虚函数,Base::run
delete p;
return ;
}

C++中对虚函数的限制:

虚函数只能是普通(对象)成员函数;

只有类的成员函数才能说明为虚函数,因为虚函数仅适用于有继承关系的类对象,所以普通函数不能说明为虚函数。在类的定义中前面有virtual关键字的成员函数就是虚函数。

静态成员函数不能是虚函数;

因为静态成员函数没有this指针,是不受限制于某个对象

类的构造函数不能是虚函数;

因为构造的时候,对象还是一片未定型的空间,只有构造完成后,对象才是具体类的实例。

当类的虚函数在类外定义时,不能有virtual关键字。

类中的声明有virtual关键字。

析构函数可以是虚函数,且通常定义为虚函数。

虚函数动态绑定的实现原理

动态选择被执行的函数
函数调用时,跳转到函数代码的入口地址处执行。把函数入口地址作为变量,在不同情况下赋予不同的值,通过该变量调用函数,就可动态选择被执行的函数。
回顾:函数指针、指向成员函数的指针

虚表
每个多态类有一个虚表(virtual table)。虚表中有当前类的各个虚函数的入口地址。每个对象有一个指向当前类的虚表的指针(虚指针vptr)。

动态绑定的实现
构造函数中为对象的虚指针赋值,通过多态类型的指针或引用调用成员函数时,通过虚指针找到虚表,进而找到所调用的虚函数的入口地址,通过该入口地址调用虚函数。

例如:有下面两个父子类

class Base
{
public:
virtual void f();
virtual void g();
private:
int i;
};
class Derived: public Base
{
public:
virtual void f();//覆盖Base::f
virtual void h(); //新增的虚函数
private:
int j;
};

C++ 多态Polymorphism 介绍+动态绑定、静态绑定的更多相关文章

  1. 深入理解OOP(三):多态和继承(动态绑定和运行时多态)

    在前面的文章中,我们介绍了编译期多态.params关键字.实例化.base关键字等.本节我们来关注另外一种多态:运行时多态, 运行时多态也叫迟绑定. 深入理解OOP(一):多态和继承(初期绑定和编译时 ...

  2. 《编程导论(Java)&#183;2.1.2 啊,我看到了多态》-什么是多态(polymorphism)

    1.不明觉厉 很多人学习多态时,会认为. 之所以不明觉厉,由于多态的定义:事物存在的多种表现形态:而后,有人将重载(overload).改写(override).多态变量和泛型归结于同一个术语&quo ...

  3. java之多态(Polymorphic)、动态绑定(Dynamic Binding)、迟绑定(Late Binding)

    今天,我们来说说java面向对象最核心的东西,多态.通过多态可以使我们的程序可复用性达到极致,这就是我们为什么要学多态的原因. “多态”(Polymorphic)也叫“动态绑定”(Dynamic Bi ...

  4. 7.6 GRASP原则六: 多态 Polymorphism

    GRASP原则六: 多态 Polymorphism  How to handle alternative behaviors based on type 如何处理依据类型不同而有 不同行为的一类需求 ...

  5. AJPFX关于多态中的动态绑定和静态绑定的总结

    在多态中:成员变量和静态方法编译和运行都看左边:成员方法编译看左边,运行看右边,这是为什么:在Java中存在两种绑定方式,一种为静态绑定,又称作早期绑定.另一种就是动态绑定,亦称为后期绑定1.静态绑定 ...

  6. 深入浅出OOP(三): 多态和继承(动态绑定/运行时多态)

    在前面的文章中,我们介绍了编译期多态.params关键字.实例化.base关键字等.本节我们来关注另外一种多态:运行时多态, 运行时多态也叫迟绑定. 运行时多态或迟绑定.动态绑定 在C#语音中,运行时 ...

  7. java面向对象之 多态 Polymorphism

    多态(Polymorphism):用我们通俗易懂的话来说就是子类就是父类(猫是动物,学生也是人),因此多态的意思就是:父类型的引用可以指向子类的对象. 1.多态的含义:一种类型,呈现出多种状态 主要讨 ...

  8. obj-c编程05:类的多态与id动态绑定

    说实在的,写这第5篇的时候十分纠结,代码老是不能动态绑定,在编译时就会出错,最后发现这是开了ARC的原因.开了ARC obj-c就不能动态绑定了吗?这个问题还不清楚哦.把ARC关闭后,虽然会有警告,但 ...

  9. 多态(Polymorphism)的实现机制

    1. 我理解的广义的 override 是指抛开各种访问权限,子类重定义(redefine)父类的函数(即函数签名相同). 2. C++中的三个所谓的原则:never redefine base cl ...

随机推荐

  1. MSChart 设置饼图颜色 图例背景色 图例显示位置

    chartField.Series.Clear();            chartField.ChartAreas.Clear();            chartField.Legends.C ...

  2. 51nod 1636 教育改革

    题目链接 令f[i][j][k]为第i天选择的课程为j,设置作业为a[j]+k时的最大作业量. 那么f[i][j][k]可以由哪些状态转移而来?先把课程按复杂度排序,那么可以转移来的课程是f[i-1] ...

  3. Symbol Tables

    符号表 符号表是键值对的集合,支持给定键查找值的操作,有很多应用: API put() 和 get() 是最基础的两个操作,为了保证代码的一致性,简洁性和实用性,先说下具体实现中的几个设计选择. 泛型 ...

  4. SDN第三次上机

    1.创建以下拓扑(可采用任意方式) 2.利用OVS命令下发流表,实现VLAN功能 3.利用OVS命令查看流表 4.验证性测试 5.Wireshark抓包验证

  5. 深入学习css之background属性

    css中允许应用纯色作为背景,也允许使用图片作为背景. background一共有8个对应的属性: 1.background-color:颜色值 用于设定背景的颜色 有3种定义颜色的形式, 1, 颜色 ...

  6. linux配置路径PATH问题

    临时:           终端输入          export PATH=/myPath:$PATH  等号左右无空格   永久:           在用户家目录下即-目录,         ...

  7. mpvue使用vant Weapp运行npm run build命令打包后失效

    最近在使用mpvue开发微信小程序,在开发过程中使用有赞的小程序ui框架—— vant Weapp ,至于如何使用在我个人博客中有一篇关于如何使用vant Weapp ,需要的同学请点进这里自行查看. ...

  8. JS播放声音

    <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>JS播放声音</tit ...

  9. WorldWind源码剖析系列:挂件类Widgets

    WorldWindow用户定制控件类中所包含的的挂件类Widgets控件主要有如下图所示的派生类.它们的类图如下所示. 鉴于挂件类Widgets及其派生类,相对简单,基本上都是些利用DirectX3D ...

  10. P3195 [HNOI2008]玩具装箱TOY

    列出DP方程式:设f[i]表示分组完前i件物品的最小花费,为方便计算,设sum[i]表示是前i件物品的长度和. f[i]=min(f[j]+(sum[i]-sum[j]+i-j-L-1)^2) [0& ...