c++对象模型( 二 )

  • 带有virtual base class的类 , 用来初始化vptr
  • 拷贝构造函数
    类中没有任何member或者base class object带有拷贝构造函数 , 也没有任何的虚函数和虚基类 , 默认情况下 对象的初始化会展示按位拷贝 , 这样效率很高且安全.
    当对一个object做显示初始化或者object被当做参数交给函数时以及函数返回一个object时(传参、返回值、初始化)构造函数会被调用 。
    copy 构造函数不展现按位逐次拷贝的时候有编译器产生出来 , 有四种情况不展现:
    1. 当成员类中生命有copy constructor
    2. 当基类中存在copy constructor
    3. 类中含有virtual function
    4. 类有virtual base class
    1、2中编译器讲member或者bass class的拷贝构造哈数的调用安插到合成的拷贝构造函数中;3 , 4是为了对vptr重新初始化.
    在构造函数中调用memset或者memcopy会使vptr设置为0
    class Shape{ public: Shape(){ memset(this, 0, sizeof(Shape);)} virtual ~Shape();}编译器扩充构造函数的内容如下:
    //扩充后的构造函数Shape::Shape(){ //vptr在用户代码之前被设定 __vptr__Shape = __vtbl__Shape; //memset 会使vptr清0 memset(this, 0, sizeof(Shape));}初始化成员列表
    编译器会操作初始化列表 , 以成员的声明顺序子构造函数内部在用户代码之前安插初始化代码. 当类含有一下四种情况的时候会需要使用成员初始化列表:
    1. 初始化一个引用成员
    2. 初始化一个constchengyuan
    3. 基类构造函数拥有参数
    4. 成员类构造函数拥有参数
    Data语义学
    数据成员的布局
    class X{};一个空类它隐藏1byte的大小 , 他是被编译器安插进去的一个char,这使得这一class的两个object在内存中配置有独一无二的地址.
    非静态的数据成员直接存放在每一个类对象中 , 对于继承而来的费静态成员也是如此 。静态数据成员则放在程序的全局数据段 , 且只存在一份数据实例.
    对成员函数的分析 , 会在整个class声明完成之后才会出现.
    在同一个访问段中member的排列要符合较晚出现的成员在对象中有较高的地址 , 多个访问段中的数据成员是自由排列的.
    数据成员的访问
    1. 静态数据成员只有一个实例放在程序的数据段 , 编译器会对每一个静态数据成员进行编码以获得一个独一无二的识别码
    2. 非静态数据成员 , 会使用隐式类对象机制访问数据(this指针)成员函数的参数中隐藏了一个隐式对象指针.
    3. 指向数据成员的指针 , 其offset值总是被加上1 , 这样可以使编译系统区分出“一个指向数据成员的指针 , 用以指出第一个成员”和“一个指向数据成员的指针 , 没有指出任何成员”.
    单一继承无virtual function下的内存布局
    单一继承下无布局情况下class和struct的布局是一样的.
    c++对象模型

    文章插图
     
    单一继承有virtaual function下的内存布局
    c++对象模型

    文章插图
     
    Point3d中含有基类的子对象Point2d subobject , 子类数据成员放置在基类子对象之后 。
    多重继承下的数据布局
    类体系如下
    class Point2d{ public: virtual ~Point2d(){}; protected: float _x,_y;};class Point3d : public Point2d{ public: //... protected: float _z;};class Vertex{ public: virtual ~Vertex(){}; protected: Vertex *next;}class Vertex3d: public Point3d, public Vertex{ public: //... protected: float mumble;}
    c++对象模型

    文章插图
     
    要存取第二个基类中的数据成员 , 将会是怎样的情况需要付出额外的成本吗?不  , 成员的位置在编译期就时就固定了 , 因此存取数据成员知识一个简单的offset操作 , 就像单一继承一样简单--不管是经由一个指针或者引用或者是一个对象来存取.
    虚拟继承
    对于虚拟继承主要的问题是如何存取class的共享部分,虚拟继承使用两种策略来实现:指针策略和offset策略.
    指针策略
    为了指出共享类对象每个子类对象安插一些指针 , 每个指针指向虚基类 。
    c++对象模型

    文章插图
    【c++对象模型】 
    进一步的优化策略的实现:每一个class object如果有一个或者多个virtual base classes , 就会由编译器安插一个指针指向virtual base class table.真正的虚基类指针放在虚基类表中.


    推荐阅读