《深度探索C++对象模型》读书笔记(二)
第三章:Data语意学
这一章主要讲了类中的数据在内存中是如何分配的,包括(多重)继承和多态。
让我们首先从一段代码开始:
class X{};
class Y :virtual public X{};
class Z :virtual public X{};
class A :public Y, public Z{};
std::cout << sizeof(X) << std::endl;
std::cout << sizeof(Y) << std::endl;
std::cout << sizeof(Z) << std::endl;
std::cout << sizeof(A) << std::endl;
在VS2013上输出的结果为1,4,4,8。为什么会这样呢?这就涉及到编译器针对C++语法而采取的对象模型。X虽然是一个空类,但为了使它的对象具有地址,编译器实际上是会为它的对象分配1个字节大小的空间,因此它的大小为1。Y和Z是对X的虚拟继承,编译器会为它们各分配一个指针,指向X的对象,因此它们的大小为4.A同理,它是Y和Z的多重继承,因此编译器会为它分配两个指针,所以它的大小为8.内存分布如图所示:
书中还提到了一种模型,其输出的结果为1,8,8,12。这种模型的思路是给每一个空类分配1 byte的空间,再考虑到4 byte的alignment,因此可以推导出书中的结果,在这里不再赘述。
关于类中Member的布局,C++ Standard是这样规定的:static data members不会被放到对象的布局之中;在同一个access section(public、private、protected等区段),member的排列是按照声明顺序分布的,但不一定是连续排列(因为存在alignment);同时,对于不同的access section,它们的data members自由排列,不必在乎声明顺序(也就是说access section之内是按照声明顺序排列,而access section之间自由排列)。
之后,书中讨论了单一继承、多态、多重继承、虚拟继承下类中内存分布的表现,这也是本章的重中之重。
这是一个很简单的程序:
Point3d origin;
origin.x=0.0;
执行这段程序所需要的时间和空间代价,随x的性质而不同。让我们分情况讨论:
1.x是static data members
类的每个static成员都只有一个实例,存放在程序的data segment之中,和对象无关;因此对于这种情况,对x的存取并不会招致任何空间和时间上的额外负担。
2.x是 nonstatic data members
访问类的nonstatic data members时,实际上编译器做了如下工作:
//源代码
origin.x=0.0;
//编译后的代码
*(&origin+(&Point3d::x))=0.0;
也就是说,为了得到x的地址,编译器需要将origin的起始地址加上x的地址偏移量。(实际上,起码在VS里用&Point3d::x来表示偏移量是有问题的,但思想可以先这样理解)
3.x是基类的变量,没有多态与多重继承
在有继承的情况下,可能会导致空间上的浪费。我们来看这样一个例子:
这个类中存有一个int和三个char,如果我们把这些变量都放到一个类中声明,那么算上alignment,它的对象大小为8字节。
假设我们要继承:
那么Concrete3的对象的大小将达到16字节,比原先的设计多了100%!
这是因为alignment导致的,因为C++的对象模型中,在一个继承而来的类的内存分布里,各个基类需要分别遵循alignment,从而导致了空间的浪费。具体地对象布局可见下图:
4.加上多态
在这种情况下,无论是时间还是空间上,访问类的成员都会带来一定额外的负担,主要体现在以下几个方面:
1.virtual table,用来存放它所声明的每一个virtual functions的地址。
2.每一个对象中会有一个vptr,提供执行期的链接。
3.编译器会重写constructor和destructor,使其能够创建和删除vptr。
5.多重继承
在多重继承的条件下,对于指针之间的赋值需要运行时计算。举个例子,以下的继承结构:
我们声明几个对象和指针并赋值:
Vertex3d v3d;
Vertex *pv;
Point2d *p2d;
Point3d *p3d;
pv=&v3d;
p2d=&v3d;'
p3d-&v3d;
对于p3d和p2d的赋值,只需要直接将v3d的地址赋过去就好。但对于pv的赋值,编译器需要计算一个Vertex在Vertex3d中的偏移量,从这个偏移量起始来得到pv的地址。因为类之间的内存分布如下所示:
Vertex3d中Vertex部分的起始地址并不是Vertex3d对象的起始地址,因此对pv赋值需要一个运行时计算的开销。
6.虚拟继承
在虚拟继承中,C++对象模型将Class分为两个区域,一个是不变区域,直接存储在对象中;一个是共享区域,存储的是virtual base class subobjects,它在内存中单独存储在某处,derived class object持有指向它的指针。在cfront编译器中,每一个derived class object中安插一些指针,每个指针指向一个virtual base class,为此需要付出相应的时间和空间成本。如下所示:
//具体的类同上一节多重继承,不同的是Vertex和Point3d虚拟继承了Point2d
void Point3d::operator+=(const Point3d&rhs)
{
x+=rhs.x;
y+=rhs.y;
z+=rhs.z;
} //编译器翻译后的版本
_vbcPoint2d->x+=rhs._vbcPoint2d->x;
_vbcPoint2d->y+=rhs._vbcPoint2d->y;
z+=rhs.z;
这只是最基本的解决方案,书中还提出了一些编译器优化时间和空间的方法,感兴趣可以深入阅读一下。
最后,书中探讨了如何获得类中某个成员的地址偏移。我在这里就总结两种方法:
1.((int)&((structure*)0)->member)
2.先通过 int Test::* pOffset = &Test::x;获取偏移变量,再利用reinterpret_cast<int>(*(void**)(&pOffset))将其转化为整形量。
《深度探索C++对象模型》读书笔记(二)的更多相关文章
- 《CSS世界》笔记二:盒模型四大家族
上一篇:<CSS世界>笔记一:流/元素/尺寸下一篇:<CSS世界>笔记三:内联元素与对齐 写在前面 在读<CSS世界>第四章之前,粗浅的认为盒模型无非是margin ...
- CSS揭秘读书笔记 (一)
CSS揭秘读书笔记 (一) 一.半透明边框 要想实现半透明边框可以使用border: border: 10px solid hsla(0,0%,100%,.5); background: ...
- 《你必须知道的.NET》读书笔记二:小OO有大原则
此篇已收录至<你必须知道的.Net>读书笔记目录贴,点击访问该目录可以获取更多内容. 一.单一职责原则 (1)核心思想:一个类最好只做一件事,只有一个引起它变化的原因 (2)常用模式:Fa ...
- spring揭秘 读书笔记 二 BeanFactory的对象注册与依赖绑定
本文是王福强所著<<spring揭秘>>一书的读书笔记 我们前面就说过,Spring的IoC容器时一个IoC Service Provider,而且IoC Service Pr ...
- ES6读书笔记(二)
前言 前段时间整理了ES6的读书笔记:<ES6读书笔记(一)>,现在为第二篇,本篇内容包括: 一.数组扩展 二.对象扩展 三.函数扩展 四.Set和Map数据结构 五.Reflect 本文 ...
- 《精通CSS》读书笔记(一)
最近新添16本书,目前开始看陈剑瓯翻译的<精通CSS——高级Web标准解决方案>(Andy Budd, CSS Mastery -- Advanced Web Standards Solu ...
- spring揭秘 读书笔记 二 BeanFactory的对象注冊与依赖绑定
本文是王福强所著<<spring揭秘>>一书的读书笔记 我们前面就说过,Spring的IoC容器时一个IoC Service Provider,并且IoC Service Pr ...
- 【记】《.net之美》之读书笔记(二) C#中的泛型
前言 上一篇读书笔记,很多小伙伴说这本书很不错,所以趁着国庆假期,继续我的读书之旅,来跟随书中作者一起温习并掌握第二章的内容吧. 一.理解泛型 1.为什么要使用泛型?-----通过使用泛型,可以极大地 ...
- Mastering Web Application Development with AngularJS 读书笔记(二)
第一章笔记 (二) 一.scopes的层级和事件系统(the eventing system) 在层级中管理的scopes可以被用做事件总线.AngularJS 允许我们去传播已经命名的事件用一种有效 ...
- how tomcat works 读书笔记(二)----------一个简单的servlet容器
app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/arti ...
随机推荐
- 网络知识--OSI七层网络与TCP/IP五层网络架构及二层/三层网络
作为一个合格的运维人员,一定要熟悉掌握OSI七层网络和TCP/IP五层网络结构知识. 废话不多说!下面就逐一展开对这两个网络架构知识的说明:一.OSI七层网络协议OSI是Open System Int ...
- Virtualbox 虚拟机安装Linux
背景:Win10系统 MSI主板 目标:基于Win10 利用虚拟机Virtualbox安装Linux 准备工作:Ctrl+Alt+Del打开任务管理器——>性能(查看CPU虚拟化是否开启) ...
- Teaching yourself programming -一个编程爱好者的碎碎念
多数时候,个人活动的展开都源于某个具体的动机.或许你是为了可以写点小工具,解决日常生活中的一些重复劳动:或许,你心怀梦想,梦想着某一天完成一款你心目中完美的游戏:又或许是,你内心憧憬电影里的hack, ...
- Spring(转载二)
在网上看到一篇文章,感觉写得挺不错的,转载一下,本文转载自:http://blog.csdn.net/m13666368773/article/details/7802126 一. IoC理论的背景 ...
- Where are your from!!!!!!!!!!!! !Baby! {封装}
在说Java 三个特性之前,我们先了解一下什么是面向对象,以及为什么Java是面向对象的语言. 面向对象是区别于面向过程的一种编程的思想.我们可以通过这个例子冰箱装大象的例子来了解一下面向对象与面向过 ...
- localStore的storage事件
两个浏览器窗口间通信 两个浏览器窗口间通信 补充一下,这里的通讯指遵守同源策略情况下. 为了吸引读者的兴趣,先把demo放到前面:下面有几个我自己写的演示多页面通讯的demo, 为了正常运行,请用 ...
- django中云存储静态文件
Django自带文件存储系统存储在本地文件夹,如果我们将文件存储在云端,需要自定义文件存储系统. 自定义文件存储系统需要继承django.core.files.storage.Storage from ...
- redis五种数据类型和常用命令及适用场景
一.redis的5种数据类型: 1.基础理解: string 字符串(可以为整形.浮点型和字符串,统称为元素) list 列表(实现队列,元素不唯一,先入先出原则) set 集合(各不相同的元素) h ...
- Django 00-socket、wsgi及初始django学习心得
HTTP基本原理1.http简述:http协议永远都是客户端发起请求,服务端回送请求.客户端和服务端本质上是一个socket客户端和服务端,http协议可以说是基于socket的再上层封装2.http ...
- iBatis.Net的基本情况和运行原理
转载http://www.cnblogs.com/13590/archive/2013/02/27/2934580.html 摘要:介绍iBatis.Net的基本情况和运行原理,运行环境中各参数的配置 ...