C ++ Object Memory Model (Note), Model

Source: Internet
Author: User

C ++ Object Memory Model (Note), Model

 

 

 

For the Memory Model of C ++ objects, the results vary according to different compilers, so the following tests are based on VS 2017. The pointer size is 4 bytes. To avoid interference caused by alignment, all member variables are of the int type.

1. Non-inherited Object Model

First, it is the memory model of the C ++ object in the simplest case, that is, it does not consider any inheritance or other situations. The test code is as follows:

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;};

 

You can use VS to view the memory layout of Point2d objects:

  

It can be seen that in VS, the virtual pointer is placed in the first element under the non-inherited model. Put the remaining elements behind them.

2. General inheritance considerations

If we consider general inheritance (relative to virtual inheritance), we need to divide it into three types: single inheritance and multi-inheritance, and pyramid inheritance.

2.1: single inheritance

Add the following test code to the Code in the previous step:

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;};

View the memory layout:

  

For the memory layout of the object itself, the base class is first followed by the class. The virtual pointer is still in the first position.

The part of the virtual function table has undergone great changes.

  • If the subclass overrides the virtual function of the parent class, only the version of the subclass is saved in the virtual table. -- Draw () & dtor ()
  • If a new virtual function is added to the subclass, a slot will be added to the virtual table above to save it. -- Draw3d ()
  • If the subclass does not override the virtual function of the parent class, the original virtual function is retained in the table. -- Draw2d ()
2.2: Multi-Inheritance

The test code is as follows:

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;};

Shows how to view the memory layout:

  

From the perspective of the memory layout of the object itself, the Declaration Order of the base class contains the parts of the two base classes in sequence. Each base class has a virtual pointer pointing to its own virtual table. That is to say, in this case, an object may contain multiple virtual pointers pointing to different virtual tables. Generally, the base class at the top of the Object Memory contains a virtual pointer pointing to the "virtual function main table ". There are several rules as follows:

  • The address of the new virtual function added by the class itself will be added after the "virtual function main table. -- Derived_func ()
  • If the base-class virtual function is rewritten, the primary virtual function table only saves the overwritten version, and other virtual function tables will jump to the primary virtual function table through the thunk mechanism.
  • If the virtual functions of the base class are not overwritten, they are retained as they are. -- Base2: func () and so on
  • Note that there is a thunk at the top of the second virtual function table to call the following two statements: Base2 * pb = new Derived (); delete pb; which can be correctly called to Derived: dtor. In essence, it is an Assembly code.
2.3: Pyramid inheritance

The test function is as follows:

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;};

View the memory layout as follows:

  

It can be seen that there is no significant difference between the memory layout and multi-inheritance. You can first arrange the layout of the Base1 and Base2 classes according to the single inheritance rules, then arrange the layout of the Derived class Object Memory according to the multi-inheritance rules. Note that the memory model of the final Derived object contains two Base grandfather Base classes. In this case, if you want to use the member x in the grandparent base class, you must write: derived_obj.Base1: x, or derived_obj.Base2: x. Otherwise, ambiguity may occur.

3. Consider virtual inheritance 3.1: single inheritance

The test code is as follows:

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;};

Observe the memory layout:

  

It can be seen that the object memory layout is divided into three parts, from top to bottom:

  • This class contains a virtual pointer and the following virtual base class pointer and member variable.Only store the newly added virtual function addresses in this class.. The virtual base class Pointer Points to the virtual base class table. The first item in the table stores the offset of the virtual base class pointer and the starting address of the object, the following items are the offset of each base class part from left to right relative to the starting address of the object.
  • The four-byte blank part, used to split the first part and the third part.
  • The third part is the base class. The first part is the virtual pointer, which pointsThe virtual table stores the inherited and rewritten virtual function addresses.Then it is a member variable of the base class.

Therefore, we can see that the Child class has two tables, and the table pointed to by the virtual pointer in the Child class memory stores the newly added virtual function address, the table pointed to by the virtual pointer of the base class stores the address of the inherited and rewritten virtual functions.

3.2: Multi-Inheritance

The test code is as follows:

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;};

Observe the memory layout:

  

It can be seen that the memory layout is not much different from that of a single virtual inheritance.

  • The first part of this class includes virtual pointers, virtual base class pointers, and member variables. The virtual function table pointed to by the virtual pointer in this class contains the address of the newly added virtual function.
  • Followed by the parts of the two base classes in the declared order, in the first base class, the virtual function table pointed to by the virtual pointer is the master table (store the address of the virtual function after rewriting and the address of the virtual function inherited from the first base class ), other virtual function tables only store the addresses of unrewritten virtual functions inherited from the corresponding base class, if you want to call the rewritten virtual function, the thunk mechanism will be used to jump to the primary table.
  • Each part is separated by a value of 0 in all four bytes.
3.3: Pyramid inheritance

The test code is as follows:

  

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;};

Observe the memory layout:

  

  

The following information can be obtained from the above results:

  • Place the base class at the beginning, and the leftmost base class is arranged first.
  • The grandfather class is placed at the end of the class. In front of the grandfather class, it is a data member of the class.
  • The base class has its own virtual pointer and virtual base class pointer, and the grandfather class also has its own virtual pointer.
  • This class does not have a virtual pointer, but occupies the virtual function table pointed to by the virtual pointer of the leftmost base class, expand it to save the address of the newly added virtual function of this class.
  • The address of the rewritten virtual function is saved in the virtual function table pointed to by the virtual pointer of the grandfather class. The addresses of unoverwritten virtual functions are kept in the virtual function tables corresponding to their base class/grandfather class.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.