《深度探索C++对象模型》是一本好书,该书作者也是《C++ Primer》的作者,一位绝对的C++大师。诚然该书中也有多多少少的错误一直为人所诟病,但这仍然不妨碍称其为一本好书。本文志在填坑。

3章2节 Data Member的布局

背景介绍

访问区(access section)即是指private、public、protected下面的代码区域。当然在类中同一种访问区可以多次声明,视作多个访问区,如:

class Point3d {
public:
// ...
private:
float x;
private:
float y;
private:
float z;
};
// 该类有4个访问区

本节重点讲述的是在同一个访问区中声明的数据成员只需符合较晚出现的members在class object中有较高的地址这一条件即可。换言之,并不需要连续,再换言之,数据成员之间编译器可以穿插其他所需的东西,如虚表指针或边界调整的填充字节等。注意该条件是说同一访问区内。如果不同访问区呢?不同的编译器厂商会做不同的调整,有的会把同种的访问区合并,有的则不会。如果你想知道你的编译器针对同种的不同访问区做了什么,对数据成员布局做了何种调整,可以采用代码验证。好的,重点终于来了。

错码

为了验证上述的猜想,作者写了一段代码来检测两个成员的顺序。

template<class class_type, class data_type1, class data_type2>
char* access_order(data_type1 class_type::*mem1, data_type2 class_type::*mem2)
{
return mem1 < mem2 ? "member 1 occurs first" : "member 2 occurs first";
} access_order(&Point3d::z, &Point3d::y);

等等,先停一下,你会说:刚才那个Point3D的类,x,y,z这三个成员可都是private啊,他这个外部的函数可以直接访问吗?呵呵。你说的对,大师犯了这个错误。来吧我们把访问区都改成public

By the Way, 大师的代码测试的不是对象的内存布局,而是直接测试的类的内存布局。

&Pont3d::z这是直接对的成员而非其对象实例的成员来取地址,实际上它获得到的并不是地址,而是成员在类中偏移(offset)。

继续,或许大师当年的编译器是可以通过的。很不幸,在我的机器上报错了(g++ (GCC) 4.8.5 20150623):

invalid operands of types ‘float Point3D::*’ and ‘float Point3D::*’ to binary ‘operator<’
return mem1 < mem2 ? "member 1 occurs first" : "member 2 occurs first";

矛头直指这个比较操作。。

可能是目前的C++标准或者G++编译器自身不支持指针地址和类中offset的比较运算符。或者大师的代码本身就存在问题。真真假假,这点就不得而知了。

改之

最简单的测试方案就是:

干嘛非要测试类中数据成员的先后顺序,直接测试对象中数据成员的先后顺序不久行了嘛

此时Point3D的三个private都已改成public。。

    Point3D p;
cout<< (&p.z < &p.y)<<endl;
printf("%p\n", &p.z);
printf("%p\n", &p.y);

输出:

0
0x7ffffd2d0458
0x7ffffd2d0454

其实到了这里,你该得出什么关于编译器如何调整数据成员布局顺序的结论,早就可以得出了。。不过那早已不是本文的重点,毕竟不同编译器有自己的实现自由,探究这个并无太多意义。。

再来直接打印一下类中成员mem1和mem2的值看看。

// 返回值也先改掉。
template<class class_type, class data_type1, class data_type2>
void access(data_type1 class_type::*mem1, data_type2 class_type::*mem2)
{
printf("%p\n", mem1);
printf("%p\n", mem2);
}

输出结果:

0x8
0x4

这两个如此简洁的地址就是类中offset了(可以看出和指针确实差别挺大,短了好多)。
不过我感觉大师代码中那个模板用的当真漂亮,所以还是想着让大师的思想继续发光发热下去,用来测试你的编译器咋处理的。遂改之。咋办?类型转换呗:

// 方案一 报错
return (char*)mem1 < (char*)mem2 ? "member 1 occurs first" : "member 2 occurs first";
// 方案二 依旧报错
return static_cast<char*>(mem1) < static_cast<char*>mem2 ? "member 1 occurs first" : "member 2 occurs first";
// 方案三 还TM报错
return reinterpret_cast<char *>(mem1) < reinterpret_cast<char *>(mem2) ? "member 1 occurs first" : "member 2 occurs first";

。。等会。看来天亡大师(的代码),强制类型转换是行不通了。。哈哈,如果你看到这里,你还是非要做一个比较操作(不通过对象),让控制台直接告诉你谁前谁后。。那么来吧,带大家一起继续脱裤子放屁。

union Cmp{
float Point3D::* mem;
long offset;
};
template<class class_type, class data_type1, class data_type2>
const char * access_order(data_type1 class_type::*mem1, data_type2 class_type::*mem2)
{
Cmp cmp1 = {mem1}; // 初始化cmp1的第一个成员mem
Cmp cmp2 = {mem2}; // 初始化cmp2的第一个成员mem return cmp1.offset< cmp2.offset?"member 1 occurs first" : "member 2 occurs first";
}

终于看到了member 2 occurs first,,代码调通了。不过丑的一笔。


后记:

代码段中类的数据成员的顺序和实例化后对象中数据成员的顺序是否具有一致性呢?这点我不确定,或许应该是吧。实例化操作应该就是栈中(或堆)模塑了代码段中的类模型,然后进行了初始化。而编译器自己添加的那些东西,那些调整工作在代码段中的类模型中就已完成了。我是这样理解的,希望大家指教。

拾遗与填坑《深度探索C++对象模型》3.2节的更多相关文章

  1. 拾遗与填坑《深度探索C++对象模型》3.3节

    <深度探索C++对象模型>是一本好书,该书作者也是<C++ Primer>的作者,一位绝对的C++大师.诚然该书中也有多多少少的错误一直为人所诟病,但这仍然不妨碍称其为一本好书 ...

  2. 读书笔记《深度探索c++对象模型》 概述

    <深度探索c++对象模型>这本书是我工作一段时间后想更深入了解C++的底层实现知识,如内存布局.模型.内存大小.继承.虚函数表等而阅读的:此外在很多面试或者工作中,对底层的知识的足够了解也 ...

  3. 柔性数组-读《深度探索C++对象模型》有感 (转载)

    最近在看<深度探索C++对象模型>,对于Struct的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下 ...

  4. 柔性数组-读《深度探索C++对象模型》有感

    最近在看<深度探索C++对象模型>,对于Struct的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下 ...

  5. [读书系列] 深度探索C++对象模型 初读

    2012年底-2014年初这段时间主要用C++做手游开发,时隔3年,重新拿起<深度探索C++对象模型>这本书,感觉生疏了很多,如果按前阵子的生疏度来说,现在不借助Visual Studio ...

  6. 深度探索C++对象模型

    深度探索C++对象模型 什么是C++对象模型: 语言中直接支持面向对象程序设计的部分. 对于各个支持的底层实现机制. 抽象性与实际性之间找出平衡点, 需要知识, 经验以及许多思考. 导读 这本书是C+ ...

  7. 《深度探索C++对象模型》读书笔记(一)

    前言 今年中下旬就要找工作了,我计划从现在就开始准备一些面试中会问到的基础知识,包括C++.操作系统.计算机网络.算法和数据结构等.C++就先从这本<深度探索C++对象模型>开始.不同于& ...

  8. c++学习书籍推荐《深度探索C++对象模型》下载

    百度云及其他网盘下载地址:点我 百度云及其他网盘下载地址:点我 编辑推荐 如果你是一位C++程序员,渴望对于底层知识获得一个完整的了解,那么这本<深度探索C++对象模型>正适合你 作者简介 ...

  9. 《深度探索c++对象模型》chapter1关于对象对象模型

    在c++中,有2种class data member:static和nostatic,以及3钟class member function:static,nostatic和virtual.已知下面这个c ...

随机推荐

  1. jacascript JSON对象的学习

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! JSON (javascript object notation) 全称是 javascript 对象表示 ...

  2. canvas图像模糊以及图像变形问题

    问题:有时用canvas作图时发现图像会出现模糊不清晰的问题,甚至做出来的图呈现出的效果与我们给的数值所应该呈现出的效果不一致 原因:当你在支持html5 canvas的浏览器下查看页面的时候,can ...

  3. bcrypt对密码加密的一些认识(学习笔记)

    学习nodejs和mongoDB的时候,接触了用户注册和登录的一些知识. 1.关于增强用户密码的安全性 用户的密码肯定不能保存为明文,避免撞库攻击. 撞库攻击:撞库是一种针对数据库的攻击方式,方法是通 ...

  4. thinkjs升级到3.0后的图片上传

    似乎当thinkjs升级到3.0后,才接手了一个项目.只是在实际运用过程中,还是发现了与2.2的些许差别——今天先分享关于图片上传的一些问题. 1.上传文件,我们选择了jQuery的插件:http:/ ...

  5. django实现model中choices类型字段,后台自由配置

    该功能主要试用于项目中有大量需要动态配置的字典字段,字典建议做缓存处理. 定义字典类型表,字典值两张表 2.配置对应的字典值 3.重写model的init方法,动态赋值choice 4.列表界面效果 ...

  6. C# QQ邮箱授权码发送邮件

    using System.Net;using System.Web.Mail; public class SendMail { /// <summary> /// 发送Email /// ...

  7. 解析spring中的BeanFactory

    我们常把spring看作一个bean工厂或者ioc容器,它帮助我们负责对象的创建管理,以及对象间依赖关系的建立,还有其他的功能. 关于工厂的实现,一般来说与我们接触最多的就是BeanFactory和A ...

  8. 实验吧_貌似有点难(php代码审计)&头有点大

    二话不说先贴代码 <?php function GetIP(){ if(!empty($_SERVER["HTTP_CLIENT_IP"])) $cip = $_SERVER ...

  9. PHPCMS v9.5.8-设计缺陷可重置前台任意用户密码

    验证.参考漏洞:http://wooyun.jozxing.cc/static/bugs/wooyun-2015-0152291.html 漏洞出现在/phpcms/modules/member/in ...

  10. Css实现checkbox及radio样式自定义

    前言 checkbox和radio样式自定义在网页中是很常见的, 比如在进行表单输入时性别的选择,用户注册时选择已阅读用户协议.随着用户对产品体验要求越来越高,我们都会对checkbox和radio重 ...