C++ 多态的实现原理与内存模型
多态在C++中是一个重要的概念,通过虚函数机制实现了在程序运行时根据调用对象来判断具体调用哪一个函数。
具体来说就是:父类类别的指针(或者引用)指向其子类的实例,然后通过父类的指针(或者引用)调用实际子类的成员函数。在每个包含有虚函数的类的对象的最前面(是指这个对象对象内存布局的最前面)都有一个称之为虚函数指针(vptr)的东西指向虚函数表(vtbl),这个虚函数表(这里仅讨论最简单的单一继承的情况,若果是多重继承,可能存在多个虚函数表)里面存放了这个类里面所有虚函数的指针,当我们要调用里面的函数时通过查找这个虚函数表来找到对应的虚函数,这就是虚函数的实现原理。注意一点,如果基类已经插入了vptr, 则派生类将继承和重用该vptr。vptr(一般在对象内存模型的顶部)必须随着对象类型的变化而不断地改变它的指向,以保证其值和当前对象的实际类型是一致的。
以上这些概念都是C++程序员很熟悉的,下面通过一些具体的例子来强化一下对这些概念的理解。
1.
#include<iostream>
using namespace std; class IRectangle
{
public:
virtual ~IRectangle() {}
virtual void Draw() = ;
}; class Rectangle: public IRectangle
{
public:
virtual ~Rectangle() {}
virtual void Draw(int scale)
{
cout << "Rectangle::Draw(int)" << endl;
}
virtual void Draw()
{
cout << "Rectangle::Draw()" << endl;
}
}; int main(void)
{
IRectangle *pI = new Rectangle;
pI->Draw();
pI->Draw();
delete pI;
return ;
}
该段代码编译失败:
C:\Users\zhuyp\Desktop>g++ -Wall test.cpp -o test -g
test.cpp: In function 'int main()':
test.cpp:29:17: error: no matching function for call to 'IRectangle::Draw(int)'
pI->Draw(200);
^
test.cpp:29:17: note: candidate is:
test.cpp:8:18: note: virtual void IRectangle::Draw()
virtual void Draw() = 0;
^
test.cpp:8:18: note: candidate expects 0 arguments, 1 provided
C:\Users\zhuyp\Desktop>
以上信息表明,在父类IRectangle中并没有Draw(int)这个函数。确实,在父类IRectangle中没有这样签名的函数,但是不是多态吗,new 的不是子类Rectangle吗?我们注意到指针 pI 虽然指向子类,但是本身确是父类 IRectangle 类型,因此在执行 pI->draw(200)的时候查找父类vtable,父类的vtable 中没有Draw(int)类型的函数,因此编译错误。
如果将 pI->draw(200) 这一句修改,将pI进行一个down cast 则编译正常,dynamic_cast<Rectangle *>(pI)->draw(200); 此时调用的是子类的指针,查找的是子类的vtable,该vtable中有签名为 draw(int) 的函数,因此不会有问题。
2.
#include <iostream>
using namespace std; class Base
{
public:
~Base()
{
cout << "~Base()" << endl;
}
void fun()
{
cout << "Base::fun()" << endl;
}
}; class Derived : public Base
{
public:
~Derived()
{
cout << "~Derived()" << endl;
}
virtual void fun()
{
cout << "Derived::fun()" << endl;
}
}; int main()
{
Derived *dp = new Derived;
Base *p = dp;
p->fun();
cout << sizeof(Base) << endl;
cout << sizeof(Derived) << endl;
cout << (void *)dp << endl;
cout << (void *)p << endl;
delete p;
p = NULL; return ;
}
编译并运行程序:
C:\Users\zhuyp\Desktop>test.exe
Base::fun()
1
8
0x3856a0
0x3856a0
~Base()
编译器使用的是gcc4.8.1 可以看出 p 和 pb 的值是相同的,因此可以得出结论,现代C++编译器已经没有为了性能的问题将vptr指针放在类内存模型的最前面了。
3.
#include<iostream>
using namespace std; class B
{
int b;
public:
virtual ~B()
{
cout << "B::~B()" << endl;
}
}; class D: public B
{
int i;
int j;
public:
virtual ~D()
{
cout << "D::~D()" << endl;
}
}; int main(void)
{
cout << "sizeB:" << sizeof(B) << " sizeD:" << sizeof(D) << endl;
char *ch = NULL; B *pb = new D[]; cout<<"size *pb "<<sizeof(pb)<<"\tend"<<endl; delete [] pb; return ;
}
程序运行出错,在输出 pb 的大小之后。可见是在delete [] pb 的时候出了问题。
我们知道释放申请的数组空间的时候需要使用 delete [] ,那 delete 怎么知道要释放多大的内存呢?delete[] 的实现包含指针的算术运算,并且需要依次调用每个指针指向的元素的析构函数,然后释放整个数组元素的内存。
由于C++中多态的存在,父类指针可能指向的是子类的内存空间。由于上面的例子中delete [] 释放的是多态数组的空间,delete[] 计算空间按照 B 类的大小来计算,每次偏移调用析构函数是按照B类来进行的,而该数组实际上存放的是D类的指针释放的大小不对(由于 sizeof(B) != sizeof(D) ,),因此会崩溃。
C:\Users\zhuyp\Desktop>test.exe
sizeB:16 sizeD:24
size *pb 8 end
注意:本代码在64bit环境中执行的,因此 *pb 是 8.
C++ 多态的实现原理与内存模型的更多相关文章
- Java内存模型深度解读
Java内存模型规范了Java虚拟机与计算机内存是如何协同工作的.Java虚拟机是一个完整的计算机的一个模型,因此这个模型自然也包含一个内存模型——又称为Java内存模型. 如果你想设计表现良好的并发 ...
- Java内存模型---并发编程网 - ifeve.com
Java内存模型 转自:http://ifeve.com/java-memory-model-6/ 原文地址 作者:Jakob Jenkov 译者:张坤 Java内存模型规范了Java虚拟机与计算机 ...
- JVM内存模型及内存分配过程
一.JVM内存模型 JVM主要管理两种类型内存:堆(Heap)和非堆(Permanent区域). 1.Heap是运行时数据区域,所有类实例和数组的内存均从此处分配.Heap区分两大块,一块是 Youn ...
- Java并发编程(四)-- Java内存模型
Java内存模型 前面讲到了Java线程之间的通信采用的是共享内存模型,这里提到的共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入何时对另一个线程可见.从抽象的角 ...
- 11、Java并发性和多线程-Java内存模型
以下内容转自http://ifeve.com/java-memory-model-6/: Java内存模型规范了Java虚拟机与计算机内存是如何协同工作的.Java虚拟机是一个完整的计算机的一个模型, ...
- 【转】Java 内存模型及GC原理
一个优秀Java程序员,必须了解Java内存模型.GC工作原理,以及如何优化GC的性能.与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能 ...
- Java内存模型原理总结(转自51CTO)
转载地址:http://developer.51cto.com/art/201811/587220.htm [51CTO.com原创稿件]这篇文章主要介绍模型产生的问题背景,解决的问题,处理思路,相关 ...
- Java 内存模型、GC原理及算法
Java 内存模型.GC原理:https://blog.csdn.net/ithomer/article/details/6252552 GC算法:https://www.cnblogs.com/sm ...
- Java 内存模型及GC原理 (转载)
一个优秀Java程序员,必须了解Java内存模型.GC工作原理,以及如何优化GC的性能.与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能 ...
随机推荐
- 【Android开发坑系列】之Fragment
这个东西有顾名思义是碎片,和之前的Activity对应. 坑1:一般情况都会想当然的以为进程被杀掉之后,Fragment也会被回收 其实,Fragment有自己的生命周期,有自己的管理器(Fragme ...
- Android UI系列-----时间、日期、Toasts和进度条Dialog
您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内 ...
- [leetcode]Find Minimum in Rotated Sorted Array @ Python
原题地址:https://oj.leetcode.com/problems/find-minimum-in-rotated-sorted-array/ 解题思路:话说leetcode上面的二分查找题目 ...
- [Aaronyang] 写给自己的WPF4.5 笔记23 [3d交互与动画 4/4]
效果图预览: 1. 3d中的命中测试 我新建了一个空的窗口,用zam做了一个长方体,深度很小.然后导出xaml <Viewport3D x:Name="ZAM3DViewport3D& ...
- GNU Radio 之 rtl-sdr
http://sdr.osmocom.org/trac/wiki/rtl-sdr 我使用的是去年买的一个电视棒(ezcap DVB-TFMDAB),50多元,它的频宽为52Mhz - 2.2GHz ! ...
- Apache+PHP+Mysql OS X 10.9 Mavericks WEB 服务器配置
在 OS X 10.9 上基本没有什么特别大的差异. 为了新系统用户方便小弟重新整理了一下,因为在 OSX 10.9 下的 Server 软件进行了不少升级,有些步骤不太一样了. 硬件方面就不在详细描 ...
- android sudio 打包资料汇总
.http://blog.csdn.net/fengyuzhengfan/article/details/43876489 混淆2.http://my.oschina.net/fallenpanda/ ...
- java-cef系列视频第三集:添加flash支持
上一集我们介绍了如何搭建java-cef调试环境. 本视频介绍如何给java-cef客户端添加flashplayer支持 第四集视频我们将介绍java-cef中的自定义协议. 本作品采用知识共享署名- ...
- 构建基于WinRT的WP8.1 App 03:Page控件
单页面模板 通常利用Visual Studio 2013创建的最简单的WP8.1应用是Blank App,它只包含一个不带任何UI的页面,并且没有任何状态管理的逻辑. 该不带任何UI的页面称为Blan ...
- [论文笔记] Methodologies for Data Quality Assessment and Improvement (ACM Comput.Surv, 2009) (2)
本篇博文主要对DMQ(S3.7)的分类进行了研读. 1. 这个章节提出了一种DQM的分类法(如下图) 由上图可见,该分类法的分类标准是对assessment & improvement阶段的支 ...