拾遗与填坑《深度探索C++对象模型》3.2节
《深度探索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节的更多相关文章
- 拾遗与填坑《深度探索C++对象模型》3.3节
<深度探索C++对象模型>是一本好书,该书作者也是<C++ Primer>的作者,一位绝对的C++大师.诚然该书中也有多多少少的错误一直为人所诟病,但这仍然不妨碍称其为一本好书 ...
- 读书笔记《深度探索c++对象模型》 概述
<深度探索c++对象模型>这本书是我工作一段时间后想更深入了解C++的底层实现知识,如内存布局.模型.内存大小.继承.虚函数表等而阅读的:此外在很多面试或者工作中,对底层的知识的足够了解也 ...
- 柔性数组-读《深度探索C++对象模型》有感 (转载)
最近在看<深度探索C++对象模型>,对于Struct的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下 ...
- 柔性数组-读《深度探索C++对象模型》有感
最近在看<深度探索C++对象模型>,对于Struct的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下 ...
- [读书系列] 深度探索C++对象模型 初读
2012年底-2014年初这段时间主要用C++做手游开发,时隔3年,重新拿起<深度探索C++对象模型>这本书,感觉生疏了很多,如果按前阵子的生疏度来说,现在不借助Visual Studio ...
- 深度探索C++对象模型
深度探索C++对象模型 什么是C++对象模型: 语言中直接支持面向对象程序设计的部分. 对于各个支持的底层实现机制. 抽象性与实际性之间找出平衡点, 需要知识, 经验以及许多思考. 导读 这本书是C+ ...
- 《深度探索C++对象模型》读书笔记(一)
前言 今年中下旬就要找工作了,我计划从现在就开始准备一些面试中会问到的基础知识,包括C++.操作系统.计算机网络.算法和数据结构等.C++就先从这本<深度探索C++对象模型>开始.不同于& ...
- c++学习书籍推荐《深度探索C++对象模型》下载
百度云及其他网盘下载地址:点我 百度云及其他网盘下载地址:点我 编辑推荐 如果你是一位C++程序员,渴望对于底层知识获得一个完整的了解,那么这本<深度探索C++对象模型>正适合你 作者简介 ...
- 《深度探索c++对象模型》chapter1关于对象对象模型
在c++中,有2种class data member:static和nostatic,以及3钟class member function:static,nostatic和virtual.已知下面这个c ...
随机推荐
- Item Pipeline
当Item在Spider中被收集之后,它将会被传递到Item Pipeline,一些组件会按照一定的顺序执行对Item的处理. 每个item pipeline组件(有时称之为"Item Pi ...
- requests-证书验证
import requests #response = requests.get('https://www.12306.cn') #print(response.status_code) #以上会显示 ...
- jacascript 原生选项卡插件
前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! 在布局的时候,想到了很多以前看到过的案例,再次熟悉一下: a链接之间的竖线:可以用a链接的border-r ...
- Linux下内存问题检测神器:Valgrind
在写大型C/C++工程时难免会发生内存泄漏现象,系统编程中一个重要的方面就是有效地处理与内存相关的问题.你的工作越接近系统,你就需要面对越多的内存问题.有时这些问题非常琐碎,而更多时候它会演变成一个调 ...
- 【转】如何使用slave_exec_mode优雅的跳过1032 1062的复制错误
今天线上的主从复制发生1062的错误,使用sql_slave_skip_counter跳过之后,由于后面的事务需要对刚刚的数据进行update,后续造成了新的1032的错误. 后来,无意中发现还有更好 ...
- c++模板使用及实现模板声明定义的分离
c++模板是编译器构造具体实例类型的模型,使类型参数化,是泛型编程的基础,泛型就是独立于特定类型. 一.模板分为函数模板和类模板两种. 函数模板:template <class 形参名,clas ...
- xcode8的那些坑儿
前几天手又贱,更新了xcode8....被几个坑玩坏了.最起码,字体改了,我现在还没有适应.下面列举了这两天遇到的问题 1.关于相册,照相,通讯录,麦克风的权限问题 xcode8打完包安装后,你会发现 ...
- bzoj1071[SCOI2007]组队
1071: [SCOI2007]组队 Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 2472 Solved: 792[Submit][Status][ ...
- hdu 3436 splay树+离散化*
Queue-jumpers Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) To ...
- NTT+多项式求逆+多项式开方(BZOJ3625)
定义多项式$h(x)$的每一项系数$h_i$,为i在c[1]~c[n]中的出现次数. 定义多项式$f(x)$的每一项系数$f_i$,为权值为i的方案数. 通过简单的分析我们可以发现:$f(x)=\fr ...