《深度探索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. urllib.error

    #捕获异常 import urllib.request import urllib.error try: response = urllib.request.urlopen('http://sasd. ...

  2. 使用生成器把Kafka写入速度提高1000倍

    title: 使用生成器把Kafka写入速度提高1000倍 toc: true comment: true date: 2018-04-13 21:35:09 tags: ['Python', '经验 ...

  3. laravel Passport - 创建 REST API 用户认证以及Dingo/Api v2.0+Passport实现api认证

    第一部分: 安装passport 使⽤ Composer 依赖包管理器安装 Passport : composer require laravel/passport 接下来,将 Passport 的服 ...

  4. 【微信小程序】对微信http请求API的封装,方便对错误码进行处理

    /**   * App 微信配置文件app.js   * author: nujey   * versions: 1.0.0   */   App({   /**   * @param {Object ...

  5. 是否可能两个ETH私钥对应同一个地址

    原提问在这里. 笔者在使用到neon-js中的私钥生成方法时发现其使用了getRandomValues方法来生成64字符长度的私钥,进而考虑到其随机性,若是调用足够多次,依然有可能生成两个完全一样的私 ...

  6. [LeetCode] Network Delay Time 网络延迟时间

    There are N network nodes, labelled 1 to N. Given times, a list of travel times as directed edges ti ...

  7. [LeetCode] Teemo Attacking 提莫攻击

    In LLP world, there is a hero called Teemo and his attacking can make his enemy Ashe be in poisoned ...

  8. 机器学习基石:14 Regularization

    一.正则化的假设集合 通过从高次多项式的H退回到低次多项式的H来降低模型复杂度, 以降低过拟合的可能性, 如何退回? 通过加约束条件: 如果加了严格的约束条件, 没有必要从H10退回到H2, 直接使用 ...

  9. Mac终端开启代理

    使用shadowsocks开启代理,只支持浏览器开启代理,其他软件是不能够开启代理的,因为这个问题,自己一直无法用本地终端 git clone 一些库,网上查找方法,总结如下: 步骤一:开启shado ...

  10. POJ1222熄灯问题

    千年老题,以前用枚举做,现在用高斯消元做 自由元直接做成0即可 #include<cstdio> #include<cstdlib> #include<algorithm ...