3.4 “继承”与Data Member

一、基本Class模型

在C++继承模型中,一个derived class object所表现出来的东西,是其自己的members加上其base class(es) members的总和。置于derived class members和base class(es) members的排列顺序,则未在C++ Standard中强制指定:理论上编译器可以自由安排之。在大部分编译器的上头,base class members总是先出现,但属于virtual base class 除外(一般而言,任何一条通则一旦碰到virtual base class就没辙了,这里也不例外)。了解这个模型,看下面的class声明:

class Point2d
{
public:
    // constructors
    // oprations
    // access functions
private:
    float x, y;
};

class Point3d
{
public:
    // constructors
    // oprations
    // access functions
private:
    float x, y, z;
};

在没有virtual functions的情况下(如本例),它们和C struct完全一样。这种最基本的C++class内存布局如下图所示:


二、只要继承不要多态

现让Point3d继承自Point2d,它们的声明如下:

class Point2d
{
public:
    Point2d(float x = 0.0, float y = 0.0)
        :_x(x), _y(y) { }

    float x() { return _x; }
    float y() { return _y; }

    void x(float new_x) { _x = new_x; }
    void y(float new_y) { _y = new_y; }

    void operator+=(const Point2d &rhs)
    {
        _x += rhs._x;
        _y += rhs._y;
    }

protected:
    float _x, _y;
};



class Point3d : public Point2d
{
public:
    Point3d(float x = 0.0, float y = 0.0, float z = 0.0)
        :Point2d(x, y), _z(z) { }

    float z() { return _z; }

    void z(float new_z) { _z = new_z; }

    void operator+=(const Point3d &rhs)
    {
        Point2d::operator+=(rhs);
        _z += rhs._z;
    }

protected:
    float _z;
};

这种单一继承而且没有virtual function时的数据布局如下图所示:

这里写图片描述

其中,sizeof(Point2d) == 8;sizeof(Point3d) == 12。


如果把一个类分解为两层或多层,有可能会为了“表现class体系之抽象化”而膨胀所需的空间。C++语言保证:“出现在derived class 中的base class subobject有其完整的原样性”。理解这些,从代码入手最简单粗暴:

class Concrete
{
public:
    // ...
private:
    int val;
    char c1;
    char c2;
    char c3;
};

/* ------------ 把一个class分解为多层 ----------- */
class Concrete1
{
public:
    // ...
private:
    int val;
    char bit1;
};

class Concrete2 : public Concrete1
{
public:
    // ...
private:
    char bit2;
};

class Concrete3 : public Concrete2
{
public:
    // ...
private:
    char bit3;
};

在一台32位或64位机器中(因为Concrete类中的char和int类型变量各自在32或64位机器中所占内存一样),每一个Concrete class object的大小都是8字节:
1、val为int类型,占4个字节;
2、char类型占1个字节。三个char类型变量各占1个字节;
3、alignment(对齐)需要1个字节。

Concrete class object内存布局如下:


下面来分析 Concrete1<—Concrete2<—Concrete3,它们的内存布局如下图所示:

这里通常会有一个疑问,为什么Concrete2中char bit2没有填补Concrete1中余下3个字节的空间?[详细讨论见P105,不再阐述]。关键是:

C++语言保证:“出现在derived class 中的base class subobject有其完整的原样性

现在sizeof(Concrete1) == 8字节;sizeof(Concrete2) == 12字节;sizeof(Concrete3) == 16字节。


三、加上多态


四、多重继承

class Point2d
{
public:
    virtual void func_Point() { cout << "Point2d"; }
protected:
    float _x, _y;
};// sizeof(Point2d)为4 + 4 + 4(指针vptr所占内存的大小) = 12字节

class Point3d : public Point2d
{
public:
    virtual void func_Point() { cout << "Point3d"; }
protected:
    float _z;
};// sizeof(Point3d)为12 + 4(_z的大小) = 16字节

class Vertex
{
public:
    virtual void func_Vertex() { cout << "Vertex"; }
protected:
    Vertex* next;
};// sizeof(Vertex)为4(指针next所占内存的大小) + 4(vptr指针所占内存的大小) = 8字节

class Vertex3d : public Point3d, public Vertex
{
public:
    Vertex3d(float m = 0.0) : mumble(m){  }

    virtual void func_Vertex() { cout << "Vertex3d"; }

    float getMumble(){ return mumble; }
protected:
    float mumble;
};// sizeof(Vertex3d)为16(Point3d所占内存的大小) + 8(Vertex所占内存的大小) + 4 = 28字节

继承关系图为:

内存布局为:


五、虚拟继承

多重继承的语意上的副作用是,它必须支持某种形式的“shared subobject继承”。典型的一个例子是最早的iostream library:

class ios { ... }
class istream : public ios { ... }
class ostream : public ios { ... }
class iostream : public istream, public ostream { ... }

不论是istream还是ostream都内含有一个ios subobject。然而在iostream的对象布局中,我们只需要单一一份ios subobject就好。语言层面的解决办法就是导入虚拟继承

class ios { ... }
class istream : public virtual ios { ... }
class ostream : public virtual ios { ... }
class iostream : public istream, public ostream { ... }

它们的继承关系图如下:

接下来的需要继续学习,updating……


参考文献:《深度探索C++对象模型》侯捷 译

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值