C++ 多态Polymorphism 介绍+动态绑定、静态绑定
什么是多态?
多态(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 介绍+动态绑定、静态绑定的更多相关文章
- 深入理解OOP(三):多态和继承(动态绑定和运行时多态)
在前面的文章中,我们介绍了编译期多态.params关键字.实例化.base关键字等.本节我们来关注另外一种多态:运行时多态, 运行时多态也叫迟绑定. 深入理解OOP(一):多态和继承(初期绑定和编译时 ...
- 《编程导论(Java)·2.1.2 啊,我看到了多态》-什么是多态(polymorphism)
1.不明觉厉 很多人学习多态时,会认为. 之所以不明觉厉,由于多态的定义:事物存在的多种表现形态:而后,有人将重载(overload).改写(override).多态变量和泛型归结于同一个术语&quo ...
- java之多态(Polymorphic)、动态绑定(Dynamic Binding)、迟绑定(Late Binding)
今天,我们来说说java面向对象最核心的东西,多态.通过多态可以使我们的程序可复用性达到极致,这就是我们为什么要学多态的原因. “多态”(Polymorphic)也叫“动态绑定”(Dynamic Bi ...
- 7.6 GRASP原则六: 多态 Polymorphism
GRASP原则六: 多态 Polymorphism How to handle alternative behaviors based on type 如何处理依据类型不同而有 不同行为的一类需求 ...
- AJPFX关于多态中的动态绑定和静态绑定的总结
在多态中:成员变量和静态方法编译和运行都看左边:成员方法编译看左边,运行看右边,这是为什么:在Java中存在两种绑定方式,一种为静态绑定,又称作早期绑定.另一种就是动态绑定,亦称为后期绑定1.静态绑定 ...
- 深入浅出OOP(三): 多态和继承(动态绑定/运行时多态)
在前面的文章中,我们介绍了编译期多态.params关键字.实例化.base关键字等.本节我们来关注另外一种多态:运行时多态, 运行时多态也叫迟绑定. 运行时多态或迟绑定.动态绑定 在C#语音中,运行时 ...
- java面向对象之 多态 Polymorphism
多态(Polymorphism):用我们通俗易懂的话来说就是子类就是父类(猫是动物,学生也是人),因此多态的意思就是:父类型的引用可以指向子类的对象. 1.多态的含义:一种类型,呈现出多种状态 主要讨 ...
- obj-c编程05:类的多态与id动态绑定
说实在的,写这第5篇的时候十分纠结,代码老是不能动态绑定,在编译时就会出错,最后发现这是开了ARC的原因.开了ARC obj-c就不能动态绑定了吗?这个问题还不清楚哦.把ARC关闭后,虽然会有警告,但 ...
- 多态(Polymorphism)的实现机制
1. 我理解的广义的 override 是指抛开各种访问权限,子类重定义(redefine)父类的函数(即函数签名相同). 2. C++中的三个所谓的原则:never redefine base cl ...
随机推荐
- redis 持久化与备份策略
持久化(persistence) 本文是 Redis 持久化文档 的中文翻译. 这篇文章提供了 Redis 持久化的技术性描述,推荐所有 Redis 用户阅读. 要更广泛地了解 Redis 持久化,以 ...
- vultr vps(ubuntu)忘记密码
参考官方解决方案:https://www.vultr.com/docs/boot-into-single-user-mode-reset-root-password 在此仅给出ubuntu下的解决 D ...
- 2019 Web开发学习路线图
以下 Web 开发人员学习路线图是来自 Github developer-roadmap 项目,目前已经有繁体版翻译 developer-roadmap-chinese. 主要有三个方向,分别为前端开 ...
- 可以获取JVM信息的一些管理工具类
一些可以获取JVM信息的java工具类 BufferPoolMXBean.class ClassLoadingMXBean.class CompilationMXBean.class GarbageC ...
- Docker技术入门与实战 第二版-学习笔记-9-Docker Compose 项目-1-举例说明
Docker Compose 是 Docker 官方编排(Orchestration)项目之一,负责快速在集群中部署分布式应用 Compose 通过一个配置文件来管理多个Docker容器,在配置文件中 ...
- WorldWind源码剖析系列:星球表面渲染类WorldSurfaceRenderer
星球表面渲染类WorldSurfaceRenderer描述如何渲染星球类(如地球)表面影像纹理.该类的类图如下. 星球类World包含的主要的字段.属性和方法如下: public const int ...
- windows/Linux动态加载链接库问题
windows: LoadLibraryA 指定的可执行模块映射到调用进程的地址空间并返回该 DLL 的句柄 HMODULE LoadLibraryA( LPCTSTR lpLibFileName// ...
- Grunt-jsdoc生成JS API文档
Grunt-jsdoc生成JS API文档 具体的请看官网 https://github.com/krampstudio/grunt-jsdoc 一:首先确保本机电脑上是否已经安装了nodejs和np ...
- JS省市区联动
JS省市区使用文档 一:服务器返回JSON格式要求如下网址里面data的格式:(拿KISSY组件data格式来做的) http://gallery.kissyui.com/cityselector/d ...
- Python2.7-内置异常类型
python内置了许多异常类型,他们的继承关系如下:-----------------------------------------------BaseException +-- SystemExi ...