【深入探索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++对象模型>正适合你 作者简介 ...
随机推荐
- edquota - 编辑用户配额
SYNOPSIS(总览) edquota [ -p proto-username ] [ -u | -g ] username... edquota [ -u | -g ] -t DESCRIPTIO ...
- tomcat 虚拟目录配置
1.虚拟目录优点 原始 拷贝到webapps下,然后启动tomcat,就可以访问webapps下的项目.eclipse配置tomcat的原理也是这种方式. 虚拟目录 定位到eclipse工作目录下,实 ...
- centos下安装nodejs的三种种方式
方法一:源码包安装 官网下载 centos下载最新版10.9 https://nodejs.org/dist/v10.9.0/node-v10.9.0-linux-x64.tar.xz mkdir / ...
- QT_7_资源文件_对话框_QMessageBox_界面布局_常用控件
资源文件 1.1. 将资源导入到项目下 1.2. 添加文件—>Qt -->Qt Resource File 1.3. 起名称 res ,生成res.qrc文件 1.4. 右键 open i ...
- 二分+贪心 || CodeForces 551C GukiZ hates Boxes
N堆石头排成一列,每堆有Ai个石子.有M个学生来将所有石头搬走.一开始所有学生都在原点, 每秒钟每个学生都可以在原地搬走一块石头,或者向前移动一格距离,求搬走所有石头的最短时间. *解法:二分答案x( ...
- Thread和Runable的关系
Thread 是一个类 Runnable是一个接口 Thread是实现了Runnable接口的类,使得run支持多线程 因为类的单一继承原则,推荐多使用Runnable接口
- 【java面试宝典】一年工作经验者适用
时光转瞬即逝,一年时间已经过去,自己准备跳槽了,请了几天假,面试了几家公司之后,拿到了一份offer,有了一点儿小小的心得,记录下来分享给搞Java的朋友,面试主要是几个部分内容. 很多公司会要求笔试 ...
- hdu2795(Billboard)线段树
Billboard Time Limit: 20000/8000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...
- PHP fpm配置和优化
pm.max_children = 1024 #最大子进程数 maximum number of child processes when pm is set to 'dynamic' or 'ond ...
- Python处理PDF-通过关键词定位-截取PDF中的图表
起因: 因为个人原因, 这些天了解了一下Python处理PDF的方法. 首先是PDF转txt, 这个方法比较多, 这里就不再赘述, 主要聊一下PDF中的图片获取. 这里用我自己的例子, 不过具体情况还 ...