您的位置:首页 > 移动开发 > Objective-C

Inside the C++ Object Model 深度探索对象模型 3-DATA 4-Function

2013-03-08 17:09 746 查看
3 DATA语意学
class X {};
class Y : public virtual X {};
class Z : public virtual X {};
class A : public Y, public Z {};

>一个空的class X//sizeof X == 1; 编译器安插了一个char, 记录object在内存中unique的地址;



>//sizeof Y == sizeof Z == 8; (大小和机器以及编译器有关)

1)语言本身造成的额外负担overhead; 指向virtual base class subobject或者指向一个相关的表格的指针; 表格中存放virtual base class subobject的地址或者其偏移量offset; 指针大小=4;

2)编译器对于特殊情况所提供的优化处理; 某些编译器会对empty virtual base class提法特殊支持;

3) Alignment的限制; Y和Z的大小目前为5-->1+4; 大部分机器上群聚结构体大小收到alignment的限制, 使他们能更有效率地在内存中存取; Ex. alignment == 4, class Y和Z需要填补(padding)3byte == 8byte;(使bus的运输量达到最高效率)

>Empty virtual base class提供一个virtual interface, 没有定义数据, 新的编译器提供了特殊处理; 一个empty virtual base class被视为derived class object最开头的一部分, 没有花费额外空间(省去了1b的char指定地址); 这种模型下 Y和Z 大小为4;

>没有特别处理的情况: class A的大小 -被共享的唯一一个class X实体, 1byte; -Base class Y/Z的大小减去因virtual base class X而配置的大小, 4byte; -class A的大小 0byte; -考虑alignment数量, 9=1+4+4, 调整边界12 = 9+3(padding)

>考虑特别处理empty virtual base class的情况: 省去class X的1byte, 不用调整边界, size为8;

>nonstatic data member直接存放在每个class object中, 继承而来的也是; static data members被放置在一个global data segment中, 不会影响class object的大小. 不管class产生多少个object, static data member永远只有一份实体.

>class object的大小 1)编译器自动加上的额外data members, 用以支持某些语言特性(virtual); 2)边界调整alignment的需要;

3.1 Data Member的绑定 The Binding of a Data Member

>防御性程序风格 始终把nested type声明放在class的起始处.

typedef int length;
class Point3dTest
{
typedef double length;
private:
length mVal;
};


3.2 Data Member的布局 Layout

>Nonstatic data members在class object中的排列顺序将和被声明的顺序一样, 任何中间介入的static data members都不会被放进对象布局中, 他们存储在程序的data segment中.

>在同一个access section(private, public, protected)中, members的排列只需符合较晚出现的members在class object中有较高的地址. members并不一定了连续排列.(Ex. members alignment padding)

>内部合成的data members,用来支持对象模型, Ex. vptr; 安插在内含virtual function 的class object

>C++ Standrad允许编译器将多个access section之中的data members 自由排列, 不必在乎他们出现在class声明之中的顺序.

3.3 Data Member的存取

Static Data Members

>被视为一个global变量, 只在class生命范围内可见; 每个static member的存取许可(private, etc)以及与class的关联不会导致任何空间上或执行时间上的额外负担.

>存取static member不需要通过class object, 通过member selection operators('.'操作符)对static data member进行存取操作只是语法上的行为, member不在object中. Ex. pointObject.staticMem = 1; pPointObject->staticMem = 1; Point::staticMem = 1;

>对static data member取址会得到一个指向其数据类型的指针, 不是一个指向其class member的指针. Ex. &Point3d::chunkSize; --> const int*; (static const int chunkSize = 2;)

>为了避免名称冲突(不同class声明同名static member, 都会放在程序的data segment), 编译器会对每一个static data member编码(name-mangling), 以获得独一无二的程序识别代码;

Nostatic Data Members

>直接存放在每一个class object中. 存取: 需要经由明确的explicit或暗喻的implicit class object.

Ex. Point3d Point3d::translate(const Point3d& pt) { x+= pt.x; y+= pt.y; } 实际上经由一个"implicit class object"(this 指针)完成. --> Point3d Point3d::translate(Point3d* const this, const Point3d& pt) { this->x+= pt.x; this->y+= pt.y; }

>对nonstatic data member进行存取操作, 编译器需要把object的起始地址加上data member的偏移量offset. Ex. origin._y = 0.0; --> &origin._y的地址为 &origin + (&Point3d::_y - 1); "1"表示指向data member的指针的offset被加上了1.

3.4 继承与Data Member

只要继承不要多态 Inheritance without Ploymorphism

>具体继承concrete inheritance相对于虚拟继承virtual inheritance, 不会增加空间或存取时间上的额外负担;

Ex. [Concrete2 : public Concrete1]Concrete2* pc2; Concrete1* pc1; Concrete1* pc11; pc1 = pc2; //pc1指向Concrete2对象; *pc2 = *pc11; //derived class subobject被覆盖掉; bit2 member会有一个非预期的数值;



加上多态

>导入vtbl, 存放virtual function的地址, vfunction + slots; 每个class object中导入一个vptr, 提供执行期的链接; 加强constructor, 为vptr设定初值, 指向vtbl; 加强destructor, 消除指向class相关vtbl的vptr; destructor的调用是反向次序, derived class --> base class.

多重继承 Multiple Inheritance

>单一继承提供了一种自然多态natural polymorphism形式;

Ex. class Point2d { public://virtual接口; protected: float _x,_y;} class Point3d : public Point2d { protected: float _z;}

class Vertex { public: //virtual接口; protected: Vertext* next; } class Vertext3d : public Point3d, public Vertex { protected: float mumble;}

Vertex3d v3d; Vertex *pv; Point2d* p2d; Point3d* p3d; pv = &3d; //pv = (Vertex*)(((char*)&v3d) + sizeof(Point3d));

p2d = &v3d; p3d = &v3d; //地址拷贝;

Vertex3d* pv3d; pv = pv3d; //pv = pv3d ? (Vertex*)(((char*)pv3d) + sizeof(Point3d)) : 0; //case pv3d为0;

虚拟继承 Virtual Inheritance

>shared subobjct继承; 一般而言, virtual base class最有效的运用形式是: 一个抽象的virtual base class, 没有任何data members;

3.5 对象成员的效率 Object Member Efficiency

聚合aggregation 封装encapsulation 继承inheritance >打开优化, 封装就不会带来执行期的效率成本. 使用inline set get亦然;

3.6 指向Data Members的指针

>打印地址/偏移量offset, 三个坐标值在对象布局中的offset 1) vptr在对象起头 4 8 12; 2)vptr在对象的尾端 0 4 8;

class Point3d{
public:
virtual ~Point3d() {};
static Point3d origin;
float x, y ,z;
};
printf("x = %p\n", &Point3d::x);
printf("y = %p\n", &Point3d::y);
printf("z = %p\n", &Point3d::z);


>给class Derived加入一个member: int v; &Derived::v的offset会是8, 表示之前有val1和val2;>多重继承的offset值, 结果都为0;

struct Base1 { public: int val1;};
struct Base2 { public: int val2;};
struct Derived : Base1, Base2 { /*int v;*/};
printf("Base1 val1 = %p\n", &Base1::val1);
printf("Base2 val2 = %p\n", &Base2::val2);
printf("Derived val1 = %p\n", &Derived::val1);
printf("Derived val2 = %p\n", &Derived::val2);


指向Member的指针的效率问题

>虚拟继承会妨碍优化的有效性(降低"把所有的处理都搬到缓存器中执行"的优化能力), 每一层虚拟继承都导入一个额外层次的间接性; Ex. pB.*bx; --> &pB->__vbcPoint + (bx - 1); 没有虚拟继承则为 &pB + (bx -1);

---Section3 End---

4 Function语意学

4.1 Member的各种调用方式

Nonstatic Member Functions 非静态成员函数

>C++设计准则之一: nonstatic member function至少必须和一般的nonmember function有相同的效率.

Ex. float magnitude3d(const Point3d* _this); float Point3d::magnitude3d() const;

float magnitude3d(const Point3d* _this) { //nonmember

return sqrt(_this->_x * _this->_x + _this->_y * _this->_y + _this->_z * _this->_z); }

>member function的扩张

1) 改写函数的signature(函数原型), 安插一个const的this指针; 如果函数是const的, this对象也是const; -->float Point3d::magnitude3d(const Point3d* const this);

2) 将每一个对 nonstatic data member的存取操作改为经由this指针来存取; ---> { return sqrt(_this->_x * _this->_x + _this->_y * _this->_y + _this->_z * _this->_z)};

3) 将member function重新写成外部函数, 对函数名进行mangling处理. --> extern magnitude3d__7Point3dFv( register Point3d* const this);

Ex. a) obj.magnitude3d(); --> magnitude3d__7Point3dFv(&obj); b) ptr->magnitude3d(); --> magnitude3d__7Point3dFv(ptr);

名称的特殊处理 Name Mangling

>member的名称前加上class名称; Ex. class Foo : public Bar { public: int iVal; }; --> class Foo { public: int iVal__3Bar; int iVal__3Foo;};

>重载化的member function; Ex. class Point { public: void x(float newX); float x(); } --> 转化 void _x_5Point(float newX); float x__5Point(); 函数体拥有一样的名称; --> mangling: void x__5PointFf(float newX); float x__5PointFv();

>"extern C"会压抑住nonmember function的mangling效果;

>signature: 函数名称+参数数目+参数类型, 确保类型安全链接 type-safe linkage; "demangling"

Virtual Member Functions

Ex. ptr->normalize(); 转化-->(*ptr->vptr[1])(ptr); //(ptr)表示this指针;

-vptr由编译器产生, 指向virtual table; 安插在每个声明(继承自)有virtual function的class object中; 复杂的class派生体系可能有多个vptr, 他的名称也会被mangled. -"1"是vtbl的索引值, 关联到normalize()函数;

>明确的调用操作explicitly invocation会压制虚拟机制, 提高效率. Ex. Point3d::magnitude3d(); 转化方式和nonstatic member function一样; -声明为inline函数提高效率;

>"经由一个class object调用virtual function" 转化和nonstatic member function一样; object调用函数不支持多态, 只有pointer和referencer可以;

Static memeber Function

Ex. obj.normalize(); ptr->normalize(); 转化为一般的nonmember函数调用 --> normalize__7Point3dSFv();

>只有当有nonstatic data members在member function中被直接存取时才需要class object. Ex. ((Point3d*)0)->object_count(); // return _object_count; 转化-->object_cout((Point3d*)0); //把0强转为class指针作为this指针实体;

>Static member functions的主要特性是没有this指针; -不能直接存取其class中的nonstatic members; -不能被声明为const volatile virtual; -不必经过class object就能被调用;

>member selection: Ex. foo().object_count();//foo返回object -->(void) foo(); Point3d::object_count();

>Ex. int Point3d::object_count() { return _object_count;} --> int object_count__5Point3dSFv() { return _object_count__5Point3d;}

>static member function没有this指针, 其地址的类型不是一个指向class member function的指针, 而是一个nonmember函数指针; &Point3d::object_count();会得到int(*)(); 而不是int(Point::*)();

>类似于nonmember function可以作为callback函数. 应用在线程threads函数上;

4.2 Virtual Member Functions

>执行期类型判断方法 runtime type resolution; runtime type identification RTTI;

>一个class只有一个virtual table, 每个table内含对应的class object中所有active virtual functions函数实体的地址. 这些函数包括-此class定义的函数实体, 会改写overriding一个可能存在的base class virtual function函数实体;-继承自base class的函数实体(derived class不改写virtual function时); -pure_virtual_called()函数实体, 可以保持空间, 也可作为异常处理函数;

>derived class 1)继承base class声明的virtual functions的函数实体函数实体的地址会被拷贝到derived class的vtbl相应的slot中; 2)可以使用自己的函数实体(对于pure_virtual_called()需要自己定义), 自己的函数实体放入对应的slot; 3)加入新的virtual function, vtbl相应增大一个slot;

Ex. class Point { public: virtual ~Point(); virtual Point& mult(float) = 0; float x() const { return _x;} virtual float y() const { return 0;} virtual float z() const { return 0;} protected: Point(float x = 0.0); float _x; };

class Point2d { public: Point2d (float x = 0.0, float y = 0.0) : Point(x) : _y(y) {} ~Point2d(); Point2d& mult(float); float y() const { return _y;} protected: float _y; }



ptr->z(); --> (*ptr->vptr[4])(ptr);

多重继承下的Virtual Function

>多重继承下, derived class内含n-1个额外的vtbl.(n代表上一层base classes数量) //Mircosoft - address points

虚拟继承下的Virtual Function

>对象不再相符, 转换需要调整this指针. 避免在一个virtual base class中声明 nonstatic data members.

4.3 函数的效能

>inline函数节省一般函数带来的额外负担, 也提供程序优化的额外机会. Ex.编译器将"被视为不变的表达式expressions"提到循环之外, 只计算一次;

4.4 指向Member Function的指针

>nonstatic data member的地址-该member在class布局中的bytes位置, 并且需要被绑定与某个class objet的地址上;

>指向member function的指针的声明: double (Point::*pmf) ();

Ex. double(Point::*coord)() = &Point::x; 也可以 coord = &Point::y; 调用方式 (origin.*coord)(); 或 (ptr->*coord)() 转化-->(coord)(&origin) 和 (coord)(ptr);

支持"指向Virtual Member Functions的指针"

Ex. float(Point::*pmf)() = &Point::z; Point* ptr = new Point3d; ptr->z(); <--> (ptr->*pmf)(); //间接调用 -->(*ptr->vptr[(int)pmf])(ptr);

多重继承下指向Member Functions的指针

>单一继承, vcall thunk地址或函数地址; 多重继承, fadder delta; 虚拟继承 delta index faddr v_offset;

Ex. struct __mptr{ int delta; int index; union { ptrtofunc faddr; int v_offset;}; };

"指向Member Functions的指针"的效率

Ex. Point3d* (Point3d::*pmf)(const Point3d&) const = &Point3d::corss_product; (pA.*pmf)(pB); -->pmf.index<0 ? (*pmf.faddr)(&pA+pmf.delta, pB) : (*pA.__vptr__Point3d[pmf.index].faddr)(&pA+pA.__vptr__Point3d[pmf.index].delta, pB);

4.5 Inline Function

>inline是一项请求, 编译器接受时会将它用一个表达式expression合理地把函数展开; (执行成本比一般的函数调用和返回机制带来的负荷低);

>处理阶段1) 分析函数定义, 决定函数的intrinsic inline ability(本质的inline能力/与编译器相关); 如果函数被判为不可inline(由于复杂度, 函数构建问题), 会被转为static函数, 在模块内产生对应的函数定义; 2) 真正的inline函数扩展操作是在调用的那一点上, 会带来参数的求值操作evaluation以及临时对象的管理;

>在汇编器assembler中才能看到是否实现了inline;

形式参数 Formal Arguments

>一般来说对于"会带来副作用的实际参数", 通常需要引入临时对象;

Ex. inline int min(int i, int j) { return i<j ? i:j ;} int val1 = 1024; int val2 = 2048; 1)min(val1, val2); --> val1<val2 ? val1:val2; 2)min(1024, 2048); -->编译时计算结果 1024; 3)min(foo1(), foo2()+1); -->临时对象 int t1; int t2; (t1 = foo1()), (t2 = foo2()+1), t1<t2
? t1:t2;

局部变量 Local Variables

>一般而言inline函数中的局部变量必须被放在函数调用的一个封闭区段中,mangling出一个unique的名称;

>inline函数中的局部变量, 加上有副作用的参数, 会导致大量临时对象的产生;

>inline函数对于封装提供了支持, 可以有效存取封装与class中的nonpublic数据; 是C程序中#define预处理宏的安全替代品(特别对于有副作用的参数);

>inline函数被调用太多次的话会产生大量扩展码, 增大程序的大小;

>副作用的参数, 或一个单一表达式多重调用, 或inline函数中有局部变量, 都会产生临时对象; inline中的inline, 会因为连锁复杂度而无法展开;

---Section4 End---
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: