Inside The C++ Object Model 读书笔记

来源:互联网 发布:pt软件下载 编辑:程序博客网 时间:2024/04/27 20:32

深度探索 C++ 对象模型
读书笔记之一  C++对象布局

读完了这本书,要是不做一些笔记、总结一下,是怎么也说不过去的。这本书个我们从头至尾详细的解释了C++对象模型如何为我们服务,是的了解了就是服务,不了解或许就是抱怨^_^。从构造函数语义到Data语义,再到函数语义,再到执行期语义,由浅入深(从单一继承到多态,到多重继承,再到虚拟继承)详细的讲解了C++是如何做到的。这里就不多废话了,想跟多了结这本书大概内容的可以参考这里,或者到作者的BLOG去溜溜。

我们从最基本的对象谈起吧,原谅我还不能一下指出其精髓~.~,C语言中形如

typedef struct point3d {
    float x;
    float y;
    float z;
} Point3d;

这样的结构体,与C++中的

class Point3D {
    float x, y ,z;
public:
    Point3D(float x_, float y_, float z_)
        : x(x_), y(y_), z(z_){}
};

或者甚至是经由 Point –> Point2D –> Point3D 这样派生而来的对象有差别吗?

打开你的编译器,看看 sizeof(Point3D) 是不是12呢?不是的话果断丢掉你那编译器,并去跟Bjarne抱怨C++怎么会这样呢?C++中的对象布局到底如何呢?其实对于Point3D来说,其每一个对象实例所拥有的也就是那三个float而已,也不会带来任何执行期的不良回应。
C++布局和时间上的额外负担来源于 概括起来其实书上总结的两点:
virtual function 机制  用以有效率的执行期绑定
virtual base class 用以实现“多次出现在继承体系结构中的base class,有一个单一的被共享的实体”。

那么C++中究竟是怎么布局的呢?考虑Point3D对象,成员x y z按照声明的先后次数存储,静态或非静态成员函数都只有单一的实体,放在对象的外面,一个特别的情况是类里什么也没有的时候,编译器会放一个byte的数据就去,防止两个对象地址相同的情况。对与继承的情况

class Vetex2D : public Point3D {
    Vetex2D *next;
}

Vetex2D里依次存储了 x, y , z, *next; 如果Point3D有一个虚函数

virtual void printcoo(ostream& os){
    os << x << " " << y << " " << z << " " << endl; 
}

则Point3D的构造函数将负责产生其虚函数表vtbl,同时Point3D中将有一个指向vtbl的指针vptr,在VS中,MS的做法是把vptr放在对象的起始处,而以前的做法是放在对象的尾端,其实标准并没有规定要放在哪里,由编译器厂商厂商决定。简单来说,放在尾部可以与C的struct兼容,而放在首部可以提高效率。对于vtbl,一般第一个slot存放了类的类型,用作RTTI(runtime type identification)。

对于虚拟继承的情况稍显复杂,考虑如下的继承关系

iostream 
ostream中有一个ios,为了保证让ios成为可共享的,ostream不能修改ios的vtbl,所以其会有一个自己的vptr,同时还得有一个指向virtual base class 的指针bptr,对istream一样,对于iostream而言,其大小将为 sizeof(ostream) + sizeof(istream) + 自己的成员 - sizeof(ios)。对于多重继承的情况而言,比如说某类A从N类继承而来,A类会为此多付出N-1个指针的开销,如果基类都有虚函数的话。对于单一继承而言派生类只需要更改其基类的虚函数表就够了,但是多重继承下每个基类的vptr保留下来。其实也挺清楚的,基本上就是这样的了。