目录

    • 静态类型 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++中虚函数的动态绑定和多态性的更多相关文章

  1. C++入门经典-例8.7-多态,利用虚函数实现动态绑定

    1:多态性是面向对象程序设计的一个重要特征,利用多态性可以设计和实现一个易于扩展的系统.在C++语言中,多态是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数,发出同样 ...

  2. C++中虚函数的作用浅析

    虚函数联系到多态,多态联系到继承.所以本文中都是在继承层次上做文章.没了继承,什么都没得谈. 下面是对C++的虚函数这玩意儿的理解. 一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你 ...

  3. C++中虚函数的作用是什么?它应该怎么用呢?(转)

    虚函数联系到多态,多态联系到继承.所以本文中都是在继承层次上做文章.没了继承,什么都没得谈. 下面是对C++的虚函数这玩意儿的理解. 一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你 ...

  4. c++中虚函数

    虽然很难找到一本不讨论多态性的C++书籍或杂志,但是,大多数这类讨论使多态性和C++虚函数的使用看起来很难.我打算在这篇文章中通过从几个方面和结合一些例子使读者理解在C++中的虚函数实现技术.说明一点 ...

  5. C++中虚函数的作用和虚函数的工作原理

    1 C++中虚函数的作用和多态 虚函数: 实现类的多态性 关键字:虚函数:虚函数的作用:多态性:多态公有继承:动态联编 C++中的虚函数的作用主要是实现了多态的机制.基类定义虚函数,子类可以重写该函数 ...

  6. 关于C++与Java中虚函数问题的读书笔记

    之前一直用C++编程,对虚函数还是一些较为肤浅的理解.可近期由于某些原因搞了下Java,发现有些知识点不熟,于是站在先驱巨人的肩上谈谈C++与Java中虚函数问题. Java中的虚函数 以下是段别人的 ...

  7. C++中虚函数功能的实现机制

    要理解C++中虚函数是如何工作的,需要回答四个问题. 1.  什么是虚函数. 虚函数由于必须是在类中声明的函数,因此又称为虚方法.所有以virtual修饰符开始的成员函数都成为虚方法.此时注意是vir ...

  8. c++中虚函数和多态性

    1.直接看下列代码: #include <iostream> using namespace std; class base{ public: void who(){ cout<&l ...

  9. C++中虚函数的作用

    一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你就应该从这里开始) 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Po ...

随机推荐

  1. 【Win2D】【译】Win2D 快速入门

    原文链接:http://microsoft.github.io/Win2D/html/QuickStart.htm 快速入门 这是 Win2D 的快速入门教程,将会介绍 Win2D 中的基本功能.你将 ...

  2. LeetCode145:Binary Tree Postorder Traversal

    题目: Given a binary tree, return the postorder traversal of its nodes' values. For example: Given bin ...

  3. 关于Git bash 在win10重装系统情况下闪退并生成mintty.exe.stackdump文件的问题

    问题内容:在重装win10系统情况下,有可能会出现安装Git后右击Git bash会出现闪退并生成mintty.exe.stackdump文件 个人解决方案:查看网络上各位网友的意见和解决方法后,自己 ...

  4. SignalR 设计理念(二)

    SignalR 设计理念(二) 实现客户端和服务器端的实时通讯. 前言: 客户端方法忽略大小写,主要原因基于是URL对大小写不敏感的问题,开发者之间为了更好的协同开发,定下的开发者协议. 问题阐述 客 ...

  5. 巧用XML配置校验导入Excel的列数据格式

    <?xml version="1.0"?> <ColumnsSeting xmlns:xsd="http://www.w3.org/2001/XMLSc ...

  6. VUE.js 中取得后台原生HTML字符串 原样显示问题

    今天使用vue调试页面,发现了页面上的一个问题,后台数据传过来的HTML字符串并没有被转换为正常的HTML代码,一拍脑门,发现忘记转换了,于是满心欢喜加上了{{{}}}.但是之后构建发现报错: 为此去 ...

  7. C#多线程编程系列(三)- 线程同步

    目录 1.1 简介 1.2 执行基本原子操作 1.3 使用Mutex类 1.4 使用SemaphoreSlim类 1.5 使用AutoResetEvent类 1.6 使用ManualResetEven ...

  8. 用MVC5+EF6+WebApi 做一个考试功能(五) 前端主题

    内容概述 前面絮絮叨叨没正事,到现在为止也没有开始写代码,不过在考虑下貌似这一节还是开始不了. B/S架构开发有一个特点,就是用浏览器打开,不同的用户群体可能有不同的风格,不论是管理平台还是普通的网站 ...

  9. Spring 开发第一步(四)Spring与JDBC事务

    Spring使用各种不同的TransactionManager来管理各种不同数据源事务底层(比如jdbc数据源.hibernate数据源.JPA数据源等等).在此基础上使用各种对应的Template来 ...

  10. MySQL(外键变种)

    day58 外键的变种                  a. 用户表和部门表                      用户:        不唯一                1 alex    ...