您的位置:首页 > 编程语言 > C语言/C++

深度探索C++对象模型

2012-11-05 11:26 211 查看
1.布局成本:

像C struct的情况一样,member functions 虽然在class的声明之内,却不出现在object之中,每一个non-inline member functions 只会衍生一个函数实体,至于每一个“拥有零个或一个定义”的inline function 则会在其每一个使用者(模块)身上产生一个函数实体。

因此,C++在布局以及存取时间上主要的额外负担是由virtual引起。

a.virtual function机制 用以支持一个有效率的“运行时绑定”

b.virtual base class 用以实现“多次出现在继承体系中的base class,有一个单一而被共享的实体”

c.发生在“一个derived class和其第二或后继之base class的转换”之间。

2.不同类型的指针之间到底有什没不同

例如:

int* p;

String* b;

XX* c;

就内存而言没有什么不同,每个指针都占4个byte,因此,其差异既不在其指针表示法不同,也不在其内容(代表一个地址)不同,而是在其所寻址出来的object类型不同。也就是说,“指针类型”也指示编译器如何解释某个特定地址中的内存内容及其大小。

3.转型(cast)其实就是一种编译器指令,大部分情况下它并不改变一个指针所指向的地址,只是影响“被指针指向的内存大小和其内容”的解释方式。

4.explicit: 可以制止“单一参数的constructor”被当做一个conversion运算符。

5.default constructors和copy constructors 在必要的时候才由编译器产生出来。

6.对象在内存中的大小受三个方面的限制:

class X{}; //空类,会被编译器编译时,安插进去一个隐晦的1byte

class Y : public virtual X{};

class Z : public virtual X{};

class A : public Y, public z{};

sizeof X 的结果为1

sizeof Y 的结果为8

sizeof Z 的结果为8

sizeof A 的结果为12

a.语言本身所造成的额外负担

当语言支持virtual base classes时,就会导致一些额外负担,在derived class中,这个额外负担反映在某种形式的指针身上,它或者指向virtual base class subobject,或者指向一个相关表格.

b.编译器对于特殊情况所提供的优化处理

virtual base class X subobject的1 bytes大小也出现在class Y和Z身上,传统上它会被放在派生类的固定部分的尾部。某些编译器会对empty virtual base class提供特殊支持,来优化存储,因此,在有的编译器上 sizeof Y 和 Z 都是4.

c.字节对齐限制

实际上class Y 和 Z的大小为5bytes,但为了能够更有效地在内存中被存取,会受到字节对齐的限制,在32的计算机上,一般是4字节对齐,也就是会填补3个字节进去,因此,导致最终的结果就是8bytes。

7.C++ Class中如果内含一个或多个virtual base class subobjects,像iostream那样,将被分割为两部分:一个不变局部和一个共享局部。不变局部中的数据,不管后继如何衍化,总是拥有固定的offset(从object的开头算起),所以这一部分的数据可以直接存取;至于共享局部,所表现的就是virtual base class subobject,这一部分的数据,其位置会因为每次的派生操作而有所变化。

8.多重继承下的virtual function

Class Derived : public Base1, public Base2

{....}

Base2 *pb1 = new Derived;

Base2 *pb2 = pb1->clone();

当执行pb1->clone()时,pb1会被调整指向Derived对象的起始地址,于是clone的Derived版本会被调用;它会传回一个指针,指向一个新的Derived对象;该对象的地址在被指定pb2之前,必须先经过调整,以指向Base2 subobject。

9 类的nonmember 或static member 或nonstatic member函数会被转化为完全相同的形式,因此,执行效率完全相同。

inline函数执行效率最高,原因在于编译器对inline函数的处理,其中编译器将“被视为不变的表达式”提到循环之外,因此只计算一次,此例显示,inline函数不只能够节省一般函数调用所带来的额外负担,也提供了程序优化的额外机会。

virtual function的调整成本:

ptr->virt_func();

被转化为:

(*ptr->__vptr[index].addr)(ptr+ptr->__vptr[index].delta)

甚至即使在大部分调用操作中,调整值都是0(只有在第二或后继的base class或virtual base class的情况下,其调整值才不为0).在这种实现技术下,不论是单一继承或多重继承,只要是虚函数调用操作,就会消耗相同的成本。当然在thunk模型中,this指针的调用成本可以被局限于有必要那么做的函数中。

10指向Member function 的指针

nonstatic member function 的地址,如果改函数是nonvirtual,则得到的结果是它在内存中的真正地址。然而这个值也不是完全的,它需要被绑定于某个class object的地址上,才能够通过它调用该函数。所有nonstatic member functions 都需要对象的地址(以参数this指出)。

float (Point::*pmf)() = &Point::z; //pmf是一个指向member function的指针

//在多重继承之下指向member functions的指针

struct __mptr {

int delta;

int index;

union {

ptrtofunc faddr;

int v_offset;

};

}

11.不要把一个基类的virtual destructor声明为pure.

12."virtual base class constructors 的被调用"有着明确的定义,只有当一个完整的class object 被定义出来时,它才会被调用;如果object只是某一个完整object的subobject,它就不会被调用。

Point

/(virtual) \ (virtual)

Point3d Vertext

\(public) / (public)

Vertext3d

|(public)

PVertext

Point3d类的构造函数被扩充为如下的形式:

Point3d* Point3d::Point3d(Point3d *this, bool _most_derived,

float x, float y, float z)

{

if (__most_derived != false)

this->Point::Point(x,y);

this->__vptr_Point3d = __vtbl_Point3d;

this->__vptr_Point3d__Point =

__vtbl_Point3d__Point;

this->_z = rhs.z

}

在更深层次的继承情况下,例如:Vertex3d, 当调用Point3d和Vertex的constructor时,总是会把__most_derived参数设为false,于是就压制两个constructors中对Point constructo的调用操作。

但如果是PVertex, 当调用Vertex3d的constructor时,会把__most_derived参数设置为false,在PVertex的构造函数中进行初始化。

13.如果在constructor或destructor中调用的类的virtual function,编译器会把该调用操作以静态方式决议,不会用到虚拟机制,如果是在Point3d constructor中,就明确调用Point3d::size()。而如果在size()之中,又调用一个virtual function时,这个调用也必须决议为Point3d的函数实体。而在其他情况下,这个调用是纯正的virtual,必须经由虚拟机制来决议其归向问题。也就是说,虚拟本身必须知道是否这个调用来自于一个constructor之中。

14. 一个class对于默认的copy assignment operator,在一下情况不会表现出bitwise copy语意:

a)当class内带一个member object,而其class有一个copy assignment operator时;

b)当一个class的base class 有一个copy assignment operator时;

c)当一个class声明了任何 virtual functions(我们你一定不能拷贝右端class object 的vptr地址,因为它可能是一个derived class object)。

d)当class继承一个virtual base class(不论此base class有没有copy operator)时。

15.class 的destructor被扩展的方式类似constructors被扩展的方式,但顺序相反:

1)destructor的函数本身现在被执行,也就是说vptr会在程序员的代码执行前被reset。

2)如果class如有member class objects,而后者拥有destructors,那么它们会以其声明顺序的相反顺序被调用。

3)如果object内带ptr,那么首先reset相关的virtual table。

4)如果有任何直接的(上一层)nonirtual base classes 拥有destructor,它们会以其声明顺序的相反顺序被调用。

5)如果有任何virtual base classes拥有destructor,而当前讨论的这个class是最尾端的class,那么它们会以其原来的构造顺序的相反顺序被调用。

16编译器必须为template 保持两个scope contexts:

1)"scope of the template declaration",用以专注于一般的template class。

2)"scope of the template instantiation",用以专注于特定的实体。

编译器的决议算法必须决定哪一个才是适当的scope,然后在其中搜寻适当的name。

17.如果一个virtual function被具现出来,其具现点紧跟在其class的具现点之后。

18.如果new运算符丢出一个exception,那么就不需要配置heap中的内存,Point constructor也不需要被调用,所以就没有理由调用delete运算符。然后如果在Point constructor中发生exception,此时内存已配置完成,那么Point之中任何构造好的合成物或子对象(subobject,也就是一个member class object或base class object)都将自动被解析掉,然后heap内存也会被释放掉。无论那种情况下,都不需要调用delete运算符。

19.virtual table 的第一个slot内含type_info object的地址,而type_info是C++ Standard所定义的类型描述器的class名称。

20 dynamic_cast运算符实施于References与Pointers上的不同:

1)程序执行中对一个class指针类型施以dynamic_cast运算符,会获得true或false:

a)如果传回真正的地址,表示这个object的动态类型被确认了,一些与类型有关的操作现在可以施行与其上。

b)如果传回0,表示没有指向任何object,意味应该以另一种逻辑施行于这个动态类型为确认的object身上。

2)dynamic_cast施行于reference时,不能够提供对等于指针情况下的那一组true/false。会发生如下:

a)如果reference真正参考到适当的derived class,downcast会被执行而程序可以继续进行。

b) 如果reference并不真正是某一种derived class, 那么,由于不能够传回0,于是会丢出一个bad_cast exception.

21.typeid运算符传回一个const reference,类型为type_info。

参考:
http://blog.csdn.net/haoel/article/details/1948051 http://www.cppblog.com/xczhang/archive/2008/01/20/41508.html http://blog.csdn.net/bluedog/article/details/4711169 http://www.cnblogs.com/BeyondAnyTime/archive/2012/06/05/2537451.html http://www.cnblogs.com/itech/archive/2009/03/01/1399996.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: