龙空技术网

C++对象模型(2)

温柔ClarkH 236

前言:

此刻咱们对“静态数据成员不是所属类的所有对象共有的”大致比较注意,朋友们都需要学习一些“静态数据成员不是所属类的所有对象共有的”的相关内容。那么小编同时在网络上搜集了一些对于“静态数据成员不是所属类的所有对象共有的””的相关资讯,希望大家能喜欢,我们一起来了解一下吧!

接着“C++对象模型(1)”,现在我们假设Rectangle派生自抽象基类Shape,Shape有一个属性m_color,并且把Draw()移到Shape中作为纯虚函数,代码如下:

class Shape {public:    Shape(): m_color(0) {}    virtual ~Shape() {}    float GetColor() const {return m_color;}    void SetColor(float color) {m_color = color;}    virtual void Draw() = 0;private:    float m_color;};class Rectangle : public Shape {public:    // 构造函数    Rectangle():m_length(1), m_width(1) {}    // 析构函数    ~Rectangle() {}    // 公共非静态成员函数    float GetLength() const {return m_length;}    void SetLength(float length) {m_length = length;}    float GetWidth() const {return m_width;}    void SetWidth(float width) {m_width = width;}    // 静态成员函数    static unsigned int GetCount() {return m_count;}protected:    Rectangle(const Rectangle& copy) {}    Rectangle& operator=(const Rectangle& assign) {return *this;}private:    // 非静态数据成员    float m_length;    float m_width;    // 静态数据成员    static unsigned int m_count;};

增加了继承和虚函数的类的对象模型变得更加复杂,规则如下:

派生类继承基类的非静态数据成员,并作为自己对象的专用数据成员;派生类继承基类的非静态成员函数并可以像自己的成员函数一样访问;为每一个多态类创建一个虚函数指针数组vtable,该类的所有虚函数(继承自基类或者新增的)的地址都保存在这张表里;多态类的每一个对象中安插一个指针成员vptr,其类型为指向函数指针的指针,它总是指向所属类的vtable,也就是说:vptr当前所在的对象是什么类型的,那么它就指向这个类型的vtable。vptr是C++对象的隐含数据成员之一;如果基类已经插入了vptr,则派生类将继承和重用该vptr;如果派生类是从多个基类继承或者有多个继承分支(从所有根类开始算起),而其中若干个继承分支上出现了多态类,则派生类将从这些分支中的每一个分支上继承一个vptr,编译器也将为它生成多个vtable,有几个vptr就生成几个vtable,分别与它的多态基类对应;vptr在派生类对象中的相对位置不会随着继承层次的逐渐加深而改变,并且现在的编译器一般都将vptr放在所有数据成员的最前面;为了支持RTTI,为每一个多态类创建一个type_info对象,并把其地址保存在vtable中的固定位置(一般为第一个位置)(具体取决于编译器实现)。

现在的Rectangle的对象模型如下图所示:

该模型有如下特点:

从一个派生类对象入手,可以直接访问到基类的数据成员,因为基类的数据成员被直接嵌入到了派生类对象中(保持基类子对象的完整性);不论派生层次有多深,派生类对象访问基类对象的数据成员和成员函数时,与访问自己的数据成员和成员函数没有任何效率差异;由于派生类数据成员和基类数据成员的这种紧密关系,当基类定义发生改变时,派生类必须重新编译后才能正确使用;派生类新增数据成员和继承来的基类数据成员按照对象的构造顺序来组合,并且每层派生的新增数据成员要么统一放在基类子对象的前面,要么统一放在后面;只有虚函数访问需要经过vptr的间接寻址,增加了一层间接性,因此带来了一些额外的运行时开销。

标签: #静态数据成员不是所属类的所有对象共有的