C++中虚函数的动态绑定和多态性
目录
- 静态类型 vs 动态类型、静态绑定 vs 动态绑定
- 虚函数动态绑定实现机制、虚析构函数
- 多态性
一.静态 vs 动态
静态类型 VS 动态类型。静态类型指的是对象声明的类型,在编译器确定的。动态类型指的是对象的所指向的类型,动态类型是可以更改的,静态类型无法更改。继承关系会导致对象的指针和引用具有静态类型和动态类型,因为继承关系的存在中可能会存在类型之间的向上向下类型转换。静态绑定 VS 动态绑定。某特性(函数)依赖与对象的静态类型,在编译期确定,某特性(函数)依赖于对象的动态类型,在运行期确定。只有通过基类的引用或者指针调用虚函数时,才能发生动态绑定,如果使用对象来操作虚函数的话,仍然会采用静态绑定的方式。因为引用或者指针既可以指向基类对象,也可以指向派生类对象的基类部分,用引用或者指针调用的虚函数。例如下面的例子中,pd的静态类型是D*,pb的静态类型是B*,是在编译器确定的;pd和pb的动态类型都是D*。DoSomething()普通的成员函数是在编译器静态绑定的,调用各自的静态类型中的方法。vfun()是虚成员函数,虚函数是动态绑定的,因此调用动态类型的方法,也就是D类型中vfun。
#include <iostream>
using namespace std; class B
{
public:
void DoSomething(){
std::cout << "B::DoSomething()" << endl;
}
virtual void vfun(){
std::cout << "B::vfun()" << endl;
}
}; class D : public B
{
public:
void DoSomething(){
std::cout << "D::DoSomething()" << endl;
}
virtual void vfun(){
std::cout << "D::vfun()" << endl; }
}; int main()
{
D* pd = new D(); //指针存在注意静态类型和动态类型
B* pb = pd; //指正注意静态类型和动态类型
pd->DoSomething(); //普通成员函数,静态绑定静态类型中的方法
pb->DoSomething(); //普通成员函数,静态绑定静态类型中的方法 pd->vfun(); //虚成员函数,动态绑定到动态类型中的方法
pb->vfun(); //虚成员函数,动态绑定到动态类型中的方法
return ;
}
二.虚函数动态绑定的实现机制
对象的指针或者引用调用虚函数的行为是动态绑定。虚函数是通过虚函数表来实现的。对于每一个含有虚函数的类,编译器会为其创建一个虚函数表Vtable,虚函数表中记录JMP指令的地址。类的虚函数表被该类的所有对象共享,编译器会为每个虚函数的实例分配指向该类的虚函数表的指针。当一个含有虚成员函数的类被继承后,器派生类中的同名函数会自动的成为虚函数,因此子类在重写这个虚函数时,可以添加,也可以不加virtual关键字,习惯上会加上virtual使得程序更加的清晰。父类指针调用虚成员函数是多态在代码层面的体现,这在运行过程中,父类指针将动态绑定(关联)到具体的某个虚函数。指的是在运行期根据父类指针的动态类型的虚函数表来绑定到具体的函数。含有虚函数的类记住使用虚析构函数,否则容易导致派生类的资源无法正常释放。例如下面情况。如果想进一步了解虚函数表需要了解,C++的对象模型,建议查看《C++中对象模型》。
#include <iostream>
using namespace std; class B
{
public:
void DoSomething(){
std::cout << "B::DoSomething()" << endl;
}
virtual void vfun(){
std::cout << "B::vfun()" << endl;
} ~B(){
std::cout << "~B()" << std::endl;
}
}; class D : public B
{
public:
void DoSomething(){
std::cout << "D::DoSomething()" << endl;
}
virtual void vfun(){
std::cout << "D::vfun()" << endl; }
~D(){
std::cout << "~D()" << std::endl;
}
}; int main()
{
B* pb = new D();
pb->vfun();
delete pb; // ~B();
return ;
}
三 C++多态性体现
C++多态性代码层面体现的是父类的指针或引用调用虚成员函数。多态产生有三个条件:产生的条件是继承关系、基类中含有虚成员函数、派生类重写基类的虚成员函数。当用父类指针或引用调用虚成员函数,这个调用关系在运行器可能绑定(关联)到不同的子类,产生不同的行为。
#include <iostream>
using namespace std;
class Person
{
public:
virtual void print(){
std::cout << "I'm a person" << endl;
}
virtual void foo(){}
}; class Chinese : public Person
{
public:
virtual void print(){
std::cout << "I'm as Chinese" << endl;
}
virtual void foo2(){}
};
class American : public Person
{
public:
virtual void print(){
std::cout << "I'm as American" << endl;
}
};
//reference
void printPerson(Person& person){
person.print();
}
//pointer
void printPerson(Person* p){
p->print();
}
int main()
{
Person p;
Chinese c;
American a;
printPerson(&p);
printPerson(&c);
printPerson(&a);
return ;
}
C++中虚函数的动态绑定和多态性的更多相关文章
- C++入门经典-例8.7-多态,利用虚函数实现动态绑定
1:多态性是面向对象程序设计的一个重要特征,利用多态性可以设计和实现一个易于扩展的系统.在C++语言中,多态是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数,发出同样 ...
- C++中虚函数的作用浅析
虚函数联系到多态,多态联系到继承.所以本文中都是在继承层次上做文章.没了继承,什么都没得谈. 下面是对C++的虚函数这玩意儿的理解. 一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你 ...
- C++中虚函数的作用是什么?它应该怎么用呢?(转)
虚函数联系到多态,多态联系到继承.所以本文中都是在继承层次上做文章.没了继承,什么都没得谈. 下面是对C++的虚函数这玩意儿的理解. 一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你 ...
- c++中虚函数
虽然很难找到一本不讨论多态性的C++书籍或杂志,但是,大多数这类讨论使多态性和C++虚函数的使用看起来很难.我打算在这篇文章中通过从几个方面和结合一些例子使读者理解在C++中的虚函数实现技术.说明一点 ...
- C++中虚函数的作用和虚函数的工作原理
1 C++中虚函数的作用和多态 虚函数: 实现类的多态性 关键字:虚函数:虚函数的作用:多态性:多态公有继承:动态联编 C++中的虚函数的作用主要是实现了多态的机制.基类定义虚函数,子类可以重写该函数 ...
- 关于C++与Java中虚函数问题的读书笔记
之前一直用C++编程,对虚函数还是一些较为肤浅的理解.可近期由于某些原因搞了下Java,发现有些知识点不熟,于是站在先驱巨人的肩上谈谈C++与Java中虚函数问题. Java中的虚函数 以下是段别人的 ...
- C++中虚函数功能的实现机制
要理解C++中虚函数是如何工作的,需要回答四个问题. 1. 什么是虚函数. 虚函数由于必须是在类中声明的函数,因此又称为虚方法.所有以virtual修饰符开始的成员函数都成为虚方法.此时注意是vir ...
- c++中虚函数和多态性
1.直接看下列代码: #include <iostream> using namespace std; class base{ public: void who(){ cout<&l ...
- C++中虚函数的作用
一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你就应该从这里开始) 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Po ...
随机推荐
- .Net socket服务器编程之为何也高效
说到Socket编程,肯定大部分人举手c,c++.可惜现在已没有机会去追随并达到写服务器的水平,所以将就下还是考虑c#版的Socket服务器吧. 经过一番查询,试用.一些数据和事实还是浮出水面,同时对 ...
- 射线与平面的相交检测(Ray-Plane intersection test)【转】
射线的定义 在欧几里德几何中,射线的定义是:直线上一点和它一旁的部分.由此可知,射线有两个性质,一是只有一个端点,二是一端无限延伸. 射线的参数方程 其中p0是射线的起点, u是射线的方向向量,t & ...
- python3字符集之间--encode与decode之间的转码详解
encode是编码,里面传入的参数是需要转成的字符集,decode是解码,里面传入的参数是本身的字符集,用本身的字符集解码为unicode字符集再转码 字符集之间的爱恨纠缠 # -*- coding: ...
- 自定义两个控件,一个是显示图标和文字的矩形,一个是带边框的label(但是不是label)
记录遇到的两个坑 坑1. 一开始我继承button 来实现下面的控件1,后面发现button没有双击事件.就改成继承UserControl了.重新编译,导致设计时的控件文本全部被清空,因为UserCo ...
- 获取form表单元素值的4种方式
<html><head><title></title><script type="text/javascript"> f ...
- BroadcastReceive的使用
一.注册方式 intent.setAction("BC_One"); 1.静态注册 <receiver android:name = "继承BroadcastRe ...
- Text and Binary modes
http://perlmaven.com/what-is-a-text-file https://cygwin.com/cygwin-ug-net/using-textbinary.html Text ...
- mysql中date,datetime,timestamp数据类型区别
(1)date表示日期,其范围为1000-01-01到9999-12-31 (2)datetime表示日期时间,其范围是1000-01-01 00:00:00到9999-12-31 23:59:59 ...
- Flask从入门到精通之Jinja2模板引擎
我们使用一个简单的例子切入到Jinja2模板引擎,形式最简单的Jinja2模板引擎就是一个包含响应文本的文件,实例如下: <h1>Hello World!</h1> 最简单的包 ...
- 为autoLayout 增加标识符,方便调试
 如上图,是一个十分简单的布局. root view 上加了一个 button 和一个 webview. 不加标识符的样子 视图层级中没有标识  只有 UIView.WKWebView 之类,如果 ...