C++对象模型——Data Member的存取(第三章)

3.3    Data Member的存取

已知下面这段代码:

Point3d origin;

origin.x = 0.0;

x的存取成本是什么?

答案视x和Point3d如何声明而定,x可能是个 static member,也可能是个nonstatic member.Point3d可能是个独立(非派生)的 class,也可能从另一个单一的base class 派生而来;虽然可能性,但它甚至可能是从多重继承或虚拟继承而来.下面数节将依次检验每一种可能性.

先看这样一个问题,如果有两个定义,origin和pt:

Point3d origin, *pt = &origin;

用它们来存取data members,像这样:

origin.x = 0.0;

pt->x = 0.0;

通过origin存取和通过pt存取,有很大差别吗?

Static Data Member

static Data member被编译器提取于 class 之外,并被视为一个global变量(但只在 class 生命范围内可见).每一个member的存取权限(private,public,protected)以及与 class 的关联,并不会导致任何空间上或执行时间上的额外负担--不论是在个别的 class objects或者是在 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()会发生什么事情?因为ARM并未指定foobar()是否必须被求值,因此没有人知道会发生什么事情.下面是一种可能的转化:

(void) foobar();

Point3d.chunkSize = 250;

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

&Point3d::chunkSize;

会获得类型如下的内存地址:

const int *

如果有两个classes,每一个都声明了一个 static member freeList,那么当它们都被放在程序的data segment时,就会导致名称冲突.编译器的解决办法是暗中对每一个 static data member编码,以获得一个独一无二的程序识别代码.

Nonstatic Data Members

Nonstatic data members直接存放在每一个 class object中,除非经由明确的(explicit)或隐式的(implicit)class object,没有办法直接存取它们.只要程序员在一个member function中直接处理一个nonstatic data member,所谓"implicit class object"就会发生,例如:

Point3d Point3d::translate(const Point3d &pt) {

x += pt.x;

y += pt.y;

z += pt.z;

}

表面上所看到的对于x,y,z的直接存取,事实上是经由一个"implicit class object"(由 this 指针表达)完成.事实上这个函数的参数是:

// member function的内部转化

Point3d Point3d::translate(Point3d *this, const Point3d &pt) {

this->x += pt.x;

this->y += pt.y;

this->z += pt.z;

}

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

origin._y = 0;

那么地址 &origin._y 将等于:

&origin + (&Point3d::_y - 1);

注意其中的-1操作,指向data member的指针,其offset值总是被加上1,这样可以使编译系统区分出"一个指向data member的指针,用以指出class的第一个member"和"一个指向data member的指针,没有之处任何member"两种情况.

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-23 18:43:37

C++对象模型——Data Member的存取(第三章)的相关文章

C++对象模型——Data Member的绑定(第三章)

3.1    Data Member的绑定 (The Binding of a Data Member) 考虑下面这段代码: // 某个foo.h头文件,从某处含入 extern float x; // 程序员的Point3d.h文件 class Point3d { public: Point3d(float, float, float); // 问题:被传回和被设定的x是哪一个x? float X() const { return x; } void X(float new_x) const

Data Member 的存取

考察以下代码: Point3d origin; origin.x = 0.0; 此例中 x 的存取成本是什么? 答案则是视 x 和 Pointd 而定(别打脸, 我知道这是废话). 具体的呢? 因为 x 可能是个 static member, 也可能是个 nonstiatic member; Point3d 可能是个独立的 class, 也可能是另一个 单一的class 派生而来:甚至可能是从多重继承或虚拟继承而来(请不要小看其他人的代码中的可能性, 你都很有可能不知道 C++ 还能这么写, 有

Data 语意学---Data member的存取效率

<深度探索C++对象模型> 对于data member来说,有两种情况 static data member数据 每一个static data member只有一个实体,存放在程序的data segment之中,无论以何种方式,无论类的继承关系如何复杂,存取路径都是非常直接 Nonstatic data members 直接存放在一个class object之中,是属于一个对象的,是需要一个叫做偏移量的值来索引的. 尤其是虚拟继承,虚拟继承将为"经由base class subobj

C++对象模型——&quot;继承&quot;与Data Member(第三章) .

3.4 "继承"与Data Member 在C++继承模型中,一个derived class object所表现出来的东西,是其自己的members加上其base class members的总和.至于derived class members和base class members的排列次序并未在C++ Standard中强制指定:理论上编译器可以自由安排.在大部分编译器上,base class members总是先出现,但属于 virtual base class的除外. 了解这种继

深度探索C++对象模型 第三章 Data 语意学

一个有趣的问题:下列 类 sizeof大小 class X{}    //1 class Y:public virtual X{} //4 or 8 class Z:public virtual X{} // 4 or 8 class A:public Y,public Z{} // 8 or 12 主要原因:为了保持每一个类生成对象在内存中的唯一性,编译器必须要给空类生成一个char来维持object的唯一性: 而virtual继承中,仅保持了base class的指针,有些编译器会继承bas

C++对象模型——指向Member Function的指针 (Pointer-to-Member Functions)(第四章)

4.4 指向Member Function的指针 (Pointer-to-Member Functions) 取一个nonstatic data member的地址,得到的结果是该member在 class 布局中的byte位置(再加1),它是一个不完整的值,须要被绑定于某个 class object的地址上,才可以被存取. 取一个nonstatic member function的地址,假设该函数是nonvirtual,则得到的结果是它在内存中真正的地址.然而这个值也是不全然的,它也须要被绑定

指向 Data Member 的指针及相关的效率影响

指向 data member 的指针是一个颇有用处的语言特性, 特别是如果你需要详细调查 class members 的底层布局的话.这个调查可以帮助你决定 vptr 是放在尾端还是起始处. 另一个用途是可以用来决定 clas 中 access sections 的次序.考察以下代码, 其中有一个 virtual function, 一个 static data member, 以及三个坐标值: class Point3d { public: virtual ~Point3d(); //...

继承与 Data Member(2)

加上多态的情况如果我要处理一个坐标点, 而不在意这是一个 Point2d 或 Point3d 实例, 那么就需要在继承关系中提供一个 virtual function 接口: class Point2d { public: Point2d(float x = 0.0, float y = 0.0) :_x(x), _y(y){}; //x 和 y 的存取函数与前一个博客中相同 //由于对不同维度的点, 这些函数操作固定不变, 所以不必设为 virtual virtual float Z()(fl

第三章、Data语意学

无虚继承的空类占一个字节,用于标记该类.有虚继承的空类至少占4个字节(可能继承的空类占很大空间).对齐情况class X{float i;//8char j;//1int k;//4double b;//下面重新的字节8,上面用来对齐};sizeof(X)=24 class Y{char j;//1int k;//4};sizeof(Y)=8 class Z{char j;//1int k;//4double b;};sizeof(Y)=16 1.Data Member的绑定有两种情况:情况一: