标签:不同的 偏移量 观察 模型 子类 ati 2.3 point 多继承
关于C++对象的内存模型,由于各家编译器不一样导致的结果也不尽相同,所以以下测试都是基于VS 2017为准。其指针大小为4个字节,为了避免对齐带来的干扰,所有成员变量都为int类型。
首先是最为简单情况下的C++对象的内存模型,即不考虑任何继承等情况。测试代码如下:
class Point2d { public: Point2d(int x_, int y_) : x(x_), y(y_) {} virtual ~Point2d() {} virtual void draw() { std::cout << "Point2d::draw()" << std::endl; } virtual void draw2d() { std::cout << "Point2d::draw2d()" << std::endl; } private: int x, y; public: static int var; };
利用VS查看Point2d的对象的内存布局可以得到下图:
由此可见,在VS中,非继承的模型下,将虚指针放在了第一个元素。其余元素放在之后。
如果考虑一般继承情况(相对于虚继承)的话,则需要分为三种,单继承和多继承,以及棱形继承。
在上一步的代码基础上加上如下测试代码:
class Point3d : public Point2d { public: Point3d(int x_, int y_, int z_) : Point2d(x_, y_), z(z_) {} virtual ~Point3d() {} virtual void draw() { std::cout << "Point3d::draw()" << std::endl; } virtual void draw3d() { std::cout << "Point3d::draw3d()" << std::endl; } private: int z; };
查看内存布局:
对于对象本身的内存布局来说,先是基类部分,然后才是本类的部分。其中虚指针还是在第一个位置。
虚函数表部分发生了较大变化
测试代码如下:
class Base1 { public: Base1(int x_) : x(x_) {} virtual ~Base1() {} virtual void base1_func() { std::cout << "Base1::base1_func" << std::endl; } virtual void func() { std::cout << "func" << std::endl; } private: int x; }; class Base2 { public: Base2(int y_) : y(y_) {}; virtual ~Base2() {}; virtual void base2_func() { std::cout << "Base1::base2_func" << std::endl; } virtual void func() { std::cout << "func" << std::endl; } private: int y; }; class Derived : public Base1, public Base2 { public: Derived(int x_, int y_, int z_) : Base1(x_), Base2(y_), z(z_) {} virtual ~Derived() {} virtual void derived_func() { std::cout << "Derived::derived_func" << std::endl; } virtual void base1_func() { std::cout << "Derived::base1_func" << std::endl; } virtual void base2_func() { std::cout << "Derived::base2_func" << std::endl; } private: int z; };
查看内存布局如下图所示:
从对象本身的内存布局来看,其中按照基类的声明顺序,依次包含了两个基类的部分。每个基类部分都有一个虚指针指向各自虚表。也就是说,在这种情况下,一个对象可能含有多个虚指针指向不同的虚表。通常来讲,排在对象内存最前面的基类部分所包含的虚指针指向的是“虚函数主表”。所以有如下几个规律:
测试函数如下:
class Base { public: Base() {} virtual ~Base() {} virtual void overwrite_func() { std::cout << "Base::overwrite_func" << std::endl; } virtual void Base_func() { std::cout << "Base::base_func" << std::endl; } private: int x; }; class Base1: public Base { public: Base1(){} virtual ~Base1() {} virtual void overwrite_func() { std::cout << "Base1::overwrite_func" << std::endl; } virtual void base1_func() { std::cout << "Base1::base1_func" << std::endl; } }; class Base2 : public Base { public: Base2() {} virtual ~Base2() {} virtual void overwrite_func() { std::cout << "Base2::overwrite_func" << std::endl; } virtual void base2_func() { std::cout << "Base2::base2_func" << std::endl; } }; class Derived : public Base1, public Base2 { public: Derived() {} virtual ~Derived() {} virtual void overwrite_func() { std::cout << "Derived::overwrite()" << std::endl; } virtual void derived_func() { std::cout << "Derived::derived_func" << std::endl; } private: int z; };
查看内存布局如下:
可见内存布局与多继承并无明显差别,可以先按照单继承规则来安排Base1类和Base2类的布局,之后再按照多继承规则安排Derived类对象内存的布局。需要注意的是最终Derived对象的内存模型中会包含两个Base祖父基类的部分。在这种情况下,如果想要使用祖父基类中的成员x就必须这么写:derived_obj.Base1::x,或者derived_obj.Base2::x。否则会引发歧义。
测试代码如下:
class Base1 { public: Base1(){} virtual ~Base1() {} virtual void overwrite_func() { std::cout << "Base1::overwrite_func" << std::endl; } virtual void base1_func() { std::cout << "Base1::base1_func" << std::endl; } private: int x; }; class Derived : virtual public Base1 { public: Derived() {} virtual ~Derived() {} virtual void overwrite_func() { std::cout << "Derived::overwrite()" << std::endl; } virtual void derived_func() { std::cout << "Derived::derived_func" << std::endl; } private: int z; };
观察内存布局:
可以看出此时对象的内存布局大概分为三个部分,由上到下分别为:
所以由上看出,此时子类有着两张表,子类内存部分虚指针指向的那张表存放新添加的虚函数地址,基类部分虚指针指向的那张表存放的是继承下来和改写过后的虚函数的地址。
测试代码如下:
class Base1 { public: Base1(){} virtual ~Base1() {} virtual void overwrite_func() { std::cout << "Base1::overwrite_func" << std::endl; } virtual void base1_func() { std::cout << "Base1::base1_func" << std::endl; } private: int x; }; class Base2 { public: Base2() {} virtual ~Base2() {} virtual void overwrite_func() { std::cout << "Base2::overwrite_func" << std::endl; } virtual void base2_func() { std::cout << "Base2::base2_func" << std::endl; } private: int y; }; class Derived : virtual public Base1, virtual public Base2 { public: Derived() {} virtual ~Derived() {} virtual void overwrite_func() { std::cout << "Derived::overwrite()" << std::endl; } virtual void derived_func() { std::cout << "Derived::derived_func" << std::endl; } private: int z; };
观察内存布局:
可见此时内存布局情况和单虚拟继承下的差不了多少
测试代码如下:
class Base { public: Base() {} virtual ~Base() {} virtual void overwrite_func() { std::cout << "Base::overwrite_func" << std::endl; } virtual void Base_func() { std::cout << "Base::base_func" << std::endl; } private: int x; }; class Base1 : virtual public Base { public: Base1(){} virtual ~Base1() {} virtual void overwrite_func() { std::cout << "Base1::overwrite_func" << std::endl; } virtual void base1_func() { std::cout << "Base1::base1_func" << std::endl; } private: int z; }; class Base2 : virtual public Base { public: Base2() {} virtual ~Base2() {} virtual void overwrite_func() { std::cout << "Base2::overwrite_func" << std::endl; } virtual void base2_func() { std::cout << "Base2::base2_func" << std::endl; } private: int y; }; class Derived : public Base1, public Base2 { public: Derived() {} virtual ~Derived() {} virtual void overwrite_func() { std::cout << "Derived::overwrite()" << std::endl; } virtual void derived_func() { std::cout << "Derived::derived_func" << std::endl; } private: int a; };
观察内存布局:
由上面的结果可以得出以下的一些信息:
标签:不同的 偏移量 观察 模型 子类 ati 2.3 point 多继承
原文地址:http://www.cnblogs.com/linfan255/p/7634866.html