考察以下代码:

Point3d origin;
origin.x = 0.0;

此例中 x 的存取成本是什么? 答案则是视 x 和 Point3d 而定(别打脸, 我知道这是废话)。 具体的呢? 因为 x 可能是个 static member, 也可能是个 nonstiatic member; Point3d 可能是个独立的 class, 也可能是另一个 单一的class 派生而来;甚至可能是从多重继承或虚拟继承而来(请不要小看其他人的代码中的可能性, 你都很有可能不知道 C++ 还能这么写, 有时在这句话之后还能加一个“我勒个去”) 而下面, 咱们就来检验这每一种可能性。

在开始之前, 先抛出一个问题, 如果我们有两个定义, origin 和 pt:

Point3d origin, *pt = &origin;
//用他们来存取 data members, 像这样:
origin.x = 0.0;
pt->x = 0.0;

通过 origin 存取和 pt 存取, 有什么重大差异呢? 让我们在最后得出答案。

Static Data Members
static data members 被编译器提出于 class 之外, 并被视为一个 global 变量(但只在 class 生命范围内可见)。 每一个 member 的存取许可(无论是 public, private 还是 protected), 以及与 class 的关联, 都不会导致时间或是空间上的额外负担(无论是个别的 class object 还是 static data member 本身), 因为每一个 static data member 都只有一个实体, 存放在程序的 data segment 之中, 每次取用 static member, 就会被内部转化为唯一的 extern 实体的直接参考操作, 如:

//origin.chunkSize = 250;
Point3d::chunkSize = 250;//pt->chunkSize = 250;
Point3d::chunkSize = 250;

从指令执行的观点来看, 这是 C++ 语言中通过一个指针和通过一个对象来存取 member, 结论完全相同的唯一一种情况。 这是因为经由 member selection operators(说人话就是 '.' 运算符) 对一个 static data member 进行存取操作只是语法上的一种变异形式而已。 member 其实并不在 class object 中, 因此 存取 static members 并不需要通过 class object。
那么如果 chunkSize 是一个从复杂关系中继承而来的 member, 又当如何? 或许它是一个 virtual base class 的 virtual base class(或更加复杂的情况) 的 member 也说不定, 那咋办呢?哦, 无所谓, 程序之中对于 static members 还是只有唯一一个实体, 而其存取路径依然是那么直接。
那么如果 static data member 的存取是经由函数调用(或其他某些语法) 而被存取呢? 例如:

Foobar().chunkSize = 250;

调用 Foobar() 会发生什么事? 在 C++ 的标准中, 没人知道会发生什么事,因为 ARM 并未指定 Foobar() 是否必须被求值(evaluated)。 cfront 的做法就是把它丢掉(= =!) 但 C++ Standard 明确要求 Foobar() 必须被求值, 哪怕其结果毫无用处, 下面就是一种可能的转化:

//你看到的
//Foobar().chunkSize = 250;
//实际上可能的代码
//对表达式求之后, 丢弃结果
(void) Foobar();
Point3d.chunkSize = 250;

若取一个 static data member 的地址, 会得到一个指向其数据类型的指针, 而不是一个指向其 class member 的指针, 因为 static member 并不在内含在一个 class object 之中,例如:

&Pointd::chunkSize;
//会得到如下的内存地址:
const int*

那如果有两个 classes, 每一个都声明了一个 static member freeList, 那么当他们都被放在程序的 data segment 时, 就会导致名称冲突, 对此编译器的解决方案是暗中对每一个 static data member 编码(这个手法有一个很美的名称: name-mangling) 以获得一个独一无二的程序识别代码, 有多少编译器就有多少种 name-mangling 做法。所谓的 name-mangling 做法主要就是两点:
1. 一种算法, 推导出独一无二的名称;
2. 万一编译系统必须要和使用者交谈,那些独一无二的名称可以轻易被推导回到原来的名称。

Nonstatic Data Members
Nonstatic data members 直接存放在每一个 class object 之中。 除非经由明确的(explicit) 或暗喻的(implicit) class object, 不然没有办法存取它们。只要程序员在一个 member function 中直接处理一个 nonstatic data member, 所谓 implicit class object 就会发生, 考察以下代码:

//你看到的
Point3d
Point3d::Tranlate(const Point3d &pt)
{_x += pt._x;_y += pt._y;_z += pt._z;
}
//实际可能的代码
//member function 的内部转化
Point3d
Point3d::Transelate(Point3d *const this, const Point3d &pt)
{this->_x += pt._x;this->_y += pt._y;this->_z += pt._z;
}

欲对一个 nonstatic data member 进行存取操作, 编译器需要把 class object 的起始地址加上 data member 的偏移量, 例如:

origin._y = 0.0;

那么地址 &origin + (&Point3d::_y - 1);
要注意的是其中的 -1 操作, 指向 data member 的指针, 其偏移量(offset) 的值总是被加上 1, 这样可以使编译系统区分出 “一个指向 data member 的指针, 用以指出 class 的第一个 member” 和 “一个指向 data member 的指针, 没有指出任何member” 两种情况。其中指向 data members 的指针将在以后的博客中探讨。
每一个 nonstatic data member 的偏移量在编译时起即可获知, 甚至如果 member 属于一个 base class subobject(派生自单一或多重继承串链) 也是一样, 因此存取一个 nonstatic data member 的效率 == C struct member == nonderived class 的 member 。
但是对于虚拟继承略有不同, 虚拟继承将为经由 base class subobject 存取 class members 导入一层新的间接性, 例如:

Point3d *pt3d;
pt3d->_x = 0.0;

其执行效率在 _x 是一个 stuct member, 一个 class member, 单一继承, 多重继承的情况下都完全相同, 但如果 _x 是一个virtual base class 的 member, 存取速度会慢一点。
回到一开始的问题, 从 origin 存取和从 pt 存取 有什么重大差异? 答案是当 Point3d 是一个derived class, 而在其继承结构中有一个 virtual base class, 且被存取的 member 是一个从该 virtual base class 继承来的 member 时, 就会产生重大差异。 因为这个时候我们无法确定 pt 到底指向哪一种 class type(这就导致我们无法知道编译期这个 member 真正的 offset 位置), 所以这个存取必须延迟到执行期, 经由一个额外的间接导引, 才能解决。 但如果用 origin 就不存在这样的问题,因为 origin 的归属毫无疑问, 而他继承自 virtual base class, member 的 offset 位置也在编译时期就固定了, 一个积极进取的编译器甚至可以静态的经由 origin 就解决掉对 _x 的存取。

以上。

转载于:https://www.cnblogs.com/wuOverflow/p/4108128.html

Data Member 的存取相关推荐

  1. 深度探索C++ 对象模型(6)-Data member的存取

    nonstatic data member 需要在class object起始地址加上该member的偏移. class A {public: int x; int y;}; A a; a.y = 0 ...

  2. c++Data Member的绑定

    Data Member的绑定 Data Member的绑定 Data Member的布局 Data Member的存取 继承与Data Member 含多态的继承 多重继承 虚继承 指向Data Me ...

  3. 继承与 Data Member(2)

    加上多态的情况 如果我要处理一个坐标点, 而不在意这是一个 Point2d 或 Point3d 实例, 那么就需要在继承关系中提供一个 virtual function 接口: class Point ...

  4. 深度探索C++ 对象模型(6)-Data member的绑定

    防御性程序设计 1).将class声明起头处放data member; 代码示例: class Point3d{ float x,y,z; public://etc } 2). class的声明处放i ...

  5. Data Member 的绑定

    考察以下代码: extern float _x;//user code class Point3d { public:Point3d(float, float, float);//问题来了, 是哪一个 ...

  6. 提示illegal reference to data member'CPMAgentManageDlg::m_matrixMatrixSt'in a static member function

    当提示"illegal reference to data member'CPMAgentManageDlg::m_matrixMatrixSt'in a static member fun ...

  7. qt error: C2491: ‘ ::staticMetaObject‘: definition of dllimport static data member not all

    场景: Qt 5.14.0 +MSVC2015 32位 ,使用qt导入外部库的时候,发现出现以下问题 error: C2491: ' ::staticMetaObject': definition o ...

  8. 深度探索c++对象模型读书笔记:Data语意学-Data Member的绑定

    一个inline函数实体,在整个class声明未被完全看见之前,是不会被评估求值(evaluated)的,也就是说,对于如下代码: 1 extern int x; 2 3 class A 4 { 5 ...

  9. 深度探索C++ 对象模型(7)-Data member的布局(虚继承)

    虚拟继承 namespace ObjectMultiDerived {class Point2d {public:// has virtual functionsvirtual void print( ...

最新文章

  1. CountDownTimer的简单使用
  2. 用 async/await 来处理异步
  3. Spring常见注解
  4. C# ASP.NET Forms身份认证
  5. 浏览器播放rtsp视频流方案(ffmpeg + nginx转m3u8)
  6. 浅谈!important对CSS的重要性
  7. 欺诈场景中的随机森林实践(基于SAS场景的实现)
  8. springmvc请求返回一个字符_Spring MVC框架详解01
  9. 你还在手写 CRUD?试试 MybatisGenerator,再也不用加班了!
  10. ArcGIS裁剪影像如何保持裁剪完全一致
  11. 计算机方向的综述投稿哪个期刊,人工智能方向论文投稿期刊
  12. Android开发艺术探索读书笔记(第5章 RemoteView)
  13. latex中公式的上下限和左右如何改变,即独立公式和行间公式的切换
  14. html怎么修改td 的宽度,互联网常识:htmltd怎么设置宽度
  15. 搜狗输入法 状态栏 自带的好用工具
  16. 用状态空间方法求解修道士与野人问题
  17. 生鲜行业渠道商经销管理系统:加强生鲜渠道连接,提升销售转化
  18. Axure之倒计时简单实现
  19. 热塑性塑料注射成型中的常见缺陷及产生原因
  20. 【AC自动机+DP】USACO2012 JAN GOLD_Video Game Combos

热门文章

  1. sqlitepython导入数据_python从sqlite读取并显示数据的方法
  2. linux dd文件系统,原来dd命令也可以模拟块设备(文件系统)读写
  3. gitlab的日常使用
  4. 进程间的通信方式(一):共享内存
  5. c语言i o编程,C 语言输入输出 (I/O)
  6. 综述论文要写英文摘要吗_速成本科毕业论文初稿!!!
  7. 自动化监控--zabbix安装和配置详解
  8. C# 调用颜色的RGB值_RGB颜色转换十六进制颜色
  9. TouTiao开源项目 分析笔记10 实现通用普通文章片段页面
  10. 转:什么是即时编译(JIT)!?OpenJDK HotSpot VM剖析