【深入探索c++对象模型】data语义学二
单一继承中,base class 和derived class的对象都是从相同的地址开始,其间差异只在于derived class比较大,用以容纳自己的nonstatic members。
若vptr放在class object的起始处,如果base class没有虚函数而derived class有,那么单一继承的上述机制就被打破,把一个derived object转换为其base类型,就需要编译器的介入,用于调整地址。

虚拟继承
class ios1
{
int a;
};
class istream1 :public ios1
{
int b;
};
class ostream1 :public ios1
{
int c;
};
class iostream1 :public istream1, public ostream1
{
int d;
}; void main()
{
cout << sizeof(ios1) << " " << sizeof(istream1) << " " << sizeof(ostream1) << " " << sizeof(iostream1);
system("pause");
}
//vs2013输出为4 8 8 20
class ios1
{
int a;
};
class istream1 :public virtual ios1
{
int b;
};
class ostream1 :public virtual ios1
{
int c;
};
class iostream1 :public istream1, public ostream1
{
int d;
}; void main()
{
cout << sizeof(ios1) << " " << sizeof(istream1) << " " << sizeof(ostream1) << " " << sizeof(iostream1);
system("pause");
}
//输出为4 12 12 24
各类经由拷贝操作取得所有的nested virtual base class指针,放到derived class object之中。所以上述代码中的24为4个int成员的16个字节和继承自两个直接父类的指针8个字节。istream和ostream中都含有一个ios subobject,为了让iostream的对象布局中只有一个ios subobject,使用虚拟继承。
如何合并istream和ostream中的共同部分?
class如果内含一个或多个virtual base class subobjects,像istream那样,将被分割为两部分:一个不变局部和一个共享局部。不变局部中的数据,不管后继如何演变,总是拥有固定的offset,所以这一部分数据可以直接存取。共享局部(表现virtual base class subobject),其位置会因为每次的派生操作而有变化,所以只可以被间接存取(指针)。
一般的布局策略是先安排好派生类的不变部分,然后再建立其共享部分。
cfront编译器会在每一个派生类对象中安插一些指针,每个指针指向一个virtual base class,要存取继承得来的virtual base class members,可以通过相关指针间接完成
例子:
void point3d::operator+=(const point3d &rhs)
{
_x+=rhs._x;
_y+=rhs._y;
_z+=rhs._z;
}
在cfront策略下,这个运算符被内部转换为:
_vbcPoint2d->_x+=rhs._vbcPoint2d->_x;
_vbcPoint2d->_y+=rhs._vbcPoint2d->_y;
_z+=rhs._z;
以上模型有两个缺点:
1.每一个对象必须针对其每一个virtual base class背负一个额外的指针
2.由于虚继承串链的加长,导致间接存取层次的增加
为了第二个问题,编译器经由拷贝操作取得所有的nested virtual base class指针,放到derived class object之中,以空间换取时间。
为了不让对象因为其virtual base class的数目的变化导致指针数目变化,进而改变存取所需空间,可以引入virtual base class table,每一个class object如果有一个或多个virtual base class,就会由编译器安插一个指针,指向virtual base class table。该table中存放指向virtual base class的指针(microsoft编译器实现)。
第二个解决方法是在virtual function table中放置virtual base class 的offset。virtual function table可经由正值或负值来索引,如果是正值,就索引到virtual function,如果是负值,则索引到virtual base class offset。

虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。虚拟继承在一般的应用中很少用到,所以也往往被忽视,这也主要是因为在C++中,多重继承是不推荐的,也并不常用,而一旦离开了多重继承,虚拟继承就完全失去了存在的必要因为这样只会降低效率(通过指针来间接存取虚基类的成员)和占用更多的空间。
笔试,面试中常考的C++虚拟继承的知识点:
第一种情况: 第二种情况: 第三种情况 第四种情况:
class a class a class a class a
{ { { {
virtual void func(); virtual void func(); virtual void func(); virtual void func();
}; }; char x; char x;
class b:public virtual a class b :public a }; };
{ { class b:public virtual a class b:public a
virtual void foo(); virtual void foo(); { {
}; }; virtual void foo(); virtual void foo();
}; };
四种情况分别求sizeof(a),sizeof(b),结果如下:
第一种:4,12
第二种:4,4
第三种:8,16
第四种:8,8
若是虚继承,派生类通过vbptr指向父类,派生类大小包括自己的vptr,vbptr,以及由vbptr指向的父类的大小;普通继承,派生类大小包括父类的数据成员以及自己的vptr
多重继承

对一个多重派生对象,将其地址指定给最左端base class的指针,情况将和单一继承时相同,因为二者都指向相同的起始地址,需付出的成本只有地址的指定操作而已。至于第二个或后继的base class的地址指定操作,则需要将地址修改过:加上介于中间的base class subobject大小。
注意:存取第二个(或后继)base class中的一个data member,不需要额外的成本,member的位置在编译时就固定了。不管是经由指针、引用还是object来存取
class X {
public:
static void fun() {
printf("%d\n", &X::a);
printf("%d\n", &X::b);
printf("%d\n", &X::m);
printf("%d\n", &X::n);
printf("%d\n", &X::x);
printf("%d\n", &X::y);
}
int a;
int b;
protected:
int m;
int n;
private:
int x;
int y;
};
int main()
{
X::fun();
system("pause");
return ;
}
输出为0,4,8,12,16,20
取某个成员的地址,表示该成员在class object中的偏移量,c++要求同一个access level中的members在内存中的排列次序应该和其声明次序相同,vptr在vs2013中应该是放到对象的尾部了。
参考:
http://blog.csdn.net/littlehedgehog/article/details/5442430
http://blog.csdn.net/hyg0811/article/details/11951855#
http://blog.csdn.net/wangqiulin123456/article/details/8059536
【深入探索c++对象模型】data语义学二的更多相关文章
- 《深度探索C++对象模型》读书笔记(一)
前言 今年中下旬就要找工作了,我计划从现在就开始准备一些面试中会问到的基础知识,包括C++.操作系统.计算机网络.算法和数据结构等.C++就先从这本<深度探索C++对象模型>开始.不同于& ...
- Data 语义学(1)
一.Data Member 的绑定(The binding of Data Member) extern float x; class Point3d { public: Point3d( float ...
- 拾遗与填坑《深度探索C++对象模型》3.2节
<深度探索C++对象模型>是一本好书,该书作者也是<C++ Primer>的作者,一位绝对的C++大师.诚然该书中也有多多少少的错误一直为人所诟病,但这仍然不妨碍称其为一本好书 ...
- [读书系列] 深度探索C++对象模型 初读
2012年底-2014年初这段时间主要用C++做手游开发,时隔3年,重新拿起<深度探索C++对象模型>这本书,感觉生疏了很多,如果按前阵子的生疏度来说,现在不借助Visual Studio ...
- 深入探索C++对象模型(三)
Data 语义学 一个class的data members,一般而言,可以表现这个class在程序执行时的某种状态.Nonstatic data members放置的是"个别的class o ...
- 拾遗与填坑《深度探索C++对象模型》3.3节
<深度探索C++对象模型>是一本好书,该书作者也是<C++ Primer>的作者,一位绝对的C++大师.诚然该书中也有多多少少的错误一直为人所诟病,但这仍然不妨碍称其为一本好书 ...
- 深度探索C++对象模型
深度探索C++对象模型 什么是C++对象模型: 语言中直接支持面向对象程序设计的部分. 对于各个支持的底层实现机制. 抽象性与实际性之间找出平衡点, 需要知识, 经验以及许多思考. 导读 这本书是C+ ...
- 深入探索C++对象模型(一)
再读<深入探索C++对象模型>笔记. 关于对象 C++在加入封装后(只含有数据成员和普通成员函数)的布局成本增加了多少? 答案是并没有增加布局成本.就像C struct一样,memeber ...
- c++学习书籍推荐《深度探索C++对象模型》下载
百度云及其他网盘下载地址:点我 百度云及其他网盘下载地址:点我 编辑推荐 如果你是一位C++程序员,渴望对于底层知识获得一个完整的了解,那么这本<深度探索C++对象模型>正适合你 作者简介 ...
随机推荐
- HDU6199 gems gems gems (DP)
题意:有n颗石子 两个人轮流拿 如果上一个人拿了x颗 这个人就可以拿x或x+1颗 问先手能获得与后手的价值差最大是多少 题解:看起来是博弈 其实是DP dp[i][j][0/1]表示当前该0/1拿 拿 ...
- java登录拦截Filter
此例子为一个简单的登录拦截. 首先在web.xml中配置拦截类. <filter-mapping> <filter-name>SessionFilter</filter- ...
- 暴力破解UltraEdit
使用x32dbg(x64dbg)做为破解工具: 使用x64dbg 打开 udeit64.exe 点击运行,直到UltraEdit启动,转到符号页: 找到: CheckForUpdatesNoPromp ...
- CF666E Forensic Examination SAM+倍增,线段树和并
题面: 给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[p_l..p_r]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数.如有多解输出最靠前的那一个. 分析: 第 ...
- FastNet C++/Python 网络通信库之 协议
协议可以使用的基础数据类型: UInt8,UInt16,UInt32,UInt64Int8,Int16,Int32,Int64Float,Double,Bool,String [T] 数组,T代表元 ...
- 学习PyQuery库
学习PyQuery库 好了,又是学习的时光啦,今天学习pyquery 来进行网页解析 常规导入模块(PyQuery库中的pyquery类) from pyquery import PyQuery as ...
- zoj 2818 Root of the Problem
Root of the Problem Time Limit: 2 Seconds Memory Limit: 65536 KB Given positive integers B and ...
- oracle 恢复中的switch datafile all是什么意思
使用rman进行恢复时,如果使用了set name修改文件路径,那么恢复后,控制文件里面的信息是没有修改该的,如果要同步控制文件的信息那么就需要使用 switch datafile allall这个可 ...
- 将登录等信息保存到session中和退出session
JShop简介:jshop是一套使用Java语言开发的B2C网店系统,致力于为个人和中小企业提供免费.好用的网店系统. 项目主页:http://git.oschina.net/dinguangx/js ...
- Nginx,Apache,Tomcat区别
Nginx:一款能承受高并发的HTTP服务器,异步的,多个连接(万级别)可以对应一个进程,进行响应.基于事件驱动模型. Nginx优点:负载均衡.反向代理.处理静态文件优势. Apache:相对于Ng ...