设为首页 加入收藏

TOP

C++对象模型:g++实现(二)(一)
2023-07-23 13:33:10 】 浏览:77
Tags:实现

上一篇博客C++对象模型:g++实现(一)》用我的理解总结了在无继承体系下g++实现的C++对象的内存布局,这篇就来总结一下在有继承情况下的C++对象的内存布局。
本文中所有代码均可从这里拿到(百度网盘)。

1. 有继承情况下的C++对象的内存布局

C++是支持多继承的,而多继承可能就会出现继承的两个(或多个)类有公共的父类,为了防止在一个对象中出现多个公共父类的实体,C++就提出了虚继承。因此总的来说C++的继承有两种:无虚继承和有虚继承。

1.1 无虚继承

在无虚继承情况下,无论是public继承还是protectedprivate继承,其内存布局都是一样的,因此这里只提供public继承的例子。

1.1.1 所有的基类和派生类都没有虚函数

在这种情况下,其实就相当于构造了一个对象,其内部按基类的声明顺序添加了各个基类对象,最后再添加派生类自己的对象,其对齐要求和类作为(非静态)成员变量是一样的。

// test09.cpp
#include <cstdio>

class Base1 {
public:
    Base1(int i, char c)
    :m_i(i), m_c(c)
    {}
private:        // 在Deriverd中:
    int m_i;    //   Offset: 0
    char m_c;   //   Offset: 4
};              //   size: 8

class Base2{
public:
    Base2(short s, int i)
    : m_s(s), m_i(i) {}
private:        // 在Deriverd中:
    short m_s;  //   Offset: 8 
    int m_i;    //   Offset: 10
};              //   size: 8

class Derived: public Base1, public Base2 {
public:
    Derived(int i1, char c, short s, int i2, long l)
    : Base1(i1, c), Base2(s, i2), m_l(l)
    {}
    long getLong() const { return m_l; }
private:
    long m_l;   //   Offset: 16
};              //   size: 8 + 8 + 8 = 24

int main() {
    std::printf("sizeof Derived: %d\n", static_cast<int>(sizeof(Derived)));
    Derived d(1, static_cast<char>(2), static_cast<short>(3), 4, static_cast<long>(5));
    long l = d.getLong();
}

// Output:
//   sizeof Derived: 24

使用gdb调试,查看内存布局,如下:
gdb调试test09.cpp
如果不是按基类整体作为一个(非静态)成员变量的话,Base2::m_s的offset应为6,但实际上其offset为8,因为Base2的对齐要求为4。

1.1.2 基类无虚函数,派生类有虚函数

这个也很简单,只需要在整个类的头部安插一个虚表指针vptr即可。

// test10.cpp
// 只是令Derived::getLong()为虚函数
#include <cstdio>

class Base1 {
public:
    Base1(int i, char c)
    :m_i(i), m_c(c)
    {}
private:        // 在Deriverd中:
    int m_i;    //   Offset: 0
    char m_c;   //   Offset: 4
};              //   size: 8

class Base2{
public:
    Base2(short s, int i)
    : m_s(s), m_i(i) {}
private:        // 在Deriverd中:
    short m_s;  //   Offset: 8 
    int m_i;    //   Offset: 10
};              //   size: 8

class Derived: public Base1, public Base2 {
public:
    Derived(int i1, char c, short s, int i2, long l)
    : Base1(i1, c), Base2(s, i2), m_l(l)
    {}
    virtual                                     // 仅在此有改动
    long getLong() const { return m_l; }
private:
    long m_l;   //   Offset: 16
};              //   size: 8 + 8 + 8 = 24

int main() {
    std::printf("sizeof Derived: %d\n", static_cast<int>(sizeof(Derived)));
    Derived d(1, static_cast<char>(2), static_cast<short>(3), 4, static_cast<long>(5));
    long l = d.getLong();
}

// Output:
//   sizeof Derived: 32
1.1.3 基类也有虚函数

在这种情况下,派生类会继承基类的虚函数,也会复用基类放置虚表指针的内存位置,无论基类有没有定义虚函数,有没有新增虚函数,都不会增加新的虚表指针,而是复用基类的虚表指针的内存位置。

// test11.cpp
// 添加了:
//   virtual char Base1::getChar();
//           int  Base1::getIntOfBase1();
//   virtual char Base2::getShort();
//           int  Base2::getIntOfBase2();
//   virtual int  Derived::getInt();
#include <cstdio>

class Base1 {
public:
    Base1(int i, char c)
    :m_i(i), m_c(c)
    {}
    virtual
    char getChar() const { return m_c; }        // ! 添加
    int getIntOfBase1() const { return m_i; }   // ! 添加
private:
    int m_i;
    char m_c;
};

class Base2{
public:
    Base2(short s, int i)
    : m_s(s), m_i(i) {}

    virtual
    short getShort() const { return m_s; }      // ! 添加
    int getIntOfBase2() const { return m_i; }   // ! 添加

private:
    short m_s;
    int m_i;
};

class Derived: public Base1, public Base2 {
public:
    Derived(int i1, char c, short s, int i2, long l)
    : Base1(i1, c), Base2(s, i2), m_l(l)
    {}
    virtual
    long getLong() const { return m_l; }

    virtual
    int getInt() { return getIntOfBase2(); }    // ! 添加
private:
    long m_l;
};

int main() {
    std::printf("sizeof Derived: %d\n&q
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C++对象模型:g++的实现(一) 下一篇驱动开发:内核监控FileObject文..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目