深度探索C++对象模型

3 篇文章 0 订阅
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值