C + + object model

Posted by scald on Tue, 25 Jan 2022 17:51:03 +0100

C + + object model

1. What is the C + + object model?

The C + + object model can be summarized into the following two parts:

  • ① The part of language that directly supports object-oriented programming
    • Object oriented programming: such as constructor, destructor, virtual function, inheritance (single inheritance, multiple inheritance, virtual inheritance), polymorphism, etc.
  • ② For various supported underlying implementation mechanisms

There are two data members and three member functions in C + + classes:

  • Two data members: static and nonstatic.
  • Three member functions: static, nonstatic and virtual.

Code example:

//base.h
#pragma once
#include<iostream>
using namespace std;
class Base
{
public:
	Base(int);
	virtual ~Base(void);

	int getIBase() const;
	static int instanceCount();
	virtual void print() const;

protected:
	int iBase;
	static int count;
};

Q: how do we build various member data and member functions for the Base class in the machine?

2. Basic C + + object model

Before introducing the object model used in C + +, two object models are introduced: a simple object model and a table driven object model.

a) Simple object model

All members occupy the same space (regardless of member type), and the object only maintains a table containing member pointers. The address of the member is placed in the table. It is handled in this way regardless of the member variable or function. Object does not directly save the member, but saves the pointer of the member.

b) Table driven object model

This model adds an indirect layer to the simple object model.

The members are divided into functions and data and saved in two tables, and then the object only saves two pointers to the table.

Table driven object model can ensure that all objects have the same size (only two pointers are saved); The class object of the simple object model is also related to the number of members.

  • The data member table contains actual data;
  • The address of the actual function contained in the function member table (one more address than the data member).

c) C + + object model

In the C + + object model:

  • ① nonstatic data members are placed inside the object;
  • ② Static data members, static and nonstatic function members are placed outside the object.
  • ③ Support for virtual functions is divided into two steps:
    • a) Each class will generate a pointer for each virtual function, and these pointers are uniformly placed in the virtual function table (vtbl)
    • b) Each class object adds a pointer (vptr) to the related virtual function table (vtbl).
      • The setting and resetting of vptr are automatically completed by the constructor, destructor and copy assignment operator of each class.
  • ④ In addition, a pointer to type is set in front of the virtual function table address_ Pointer to the info class.
    • C + + provides a type_info class to get object type information.

Advantages and disadvantages of C + + object model:

  • Advantages: its efficiency in space and access time
  • Disadvantages: when the non static data members of the class used are added, deleted or modified, they need to be recompiled.

d) Model validation test

class Base
{
public:
    Base(int i) :baseI(i){};
    virtual void print(void){ cout << "Virtual function called Base::print()"; }
    virtual void setI(){cout<<"Virtual function called Base::setI()";}
    virtual ~Base(){}
private:
    int baseI;
};
  • When a class itself defines a virtual function or its parent class has a virtual function, the compiler will add a virtual function pointer (vptr) to the class in order to support the polymorphism mechanism.

  • Virtual function pointers are generally placed in the first position of the object memory layout to ensure that the virtual function table can be obtained with the highest efficiency in the case of multi-layer inheritance or multi inheritance.

  • When vptr is at the front of the object memory, the address of the object is the virtual function pointer address. We can get the address of the virtual function pointer:

    Base b(1000);
    int *vptrAddr = (int *)(&b);   //Forcibly convert the address of the class object to type int * and obtain the address of the virtual function pointer.
    
  • The virtual function pointer points to the virtual function table. The virtual function table stores the addresses of a series of virtual functions. The order of virtual function addresses is consistent with the order of virtual function declarations in the class.

  • For the virtual function pointer address value, you can get the address of the virtual function table, that is, the address of the first virtual function in the virtual function table:

    typedef void(*Fun)(void);
    Fun vfunc = (Fun)*( (int *)*(int*)(&b));
    /*
    Get the value of virtual function table pointer: * (int *) (& B); It's an address, the address of the virtual function table
     Cast the address of virtual function table into int *: (int *) * (int *) (& B);
    Then convert it to our Fun pointer type: (Fun) * (int *) * (int *) (& B)
    */
    cout << "The address of the first virtual function is:" << (int *)*(int*)(&b) << endl;
    cout << "Virtual functions are called by address Base::print(): ";
    vfunc();
    

3. Adding single inheritance to C + + model

class Base
{
public:
    Base(int i) :baseI(i){};
    virtual void print(void){ cout << "Virtual function called Base::print()"; }
    virtual void setI(){cout<<"Virtual function called Base::setI()";}
    virtual ~Base(){}
private:
    int baseI;
};
class Derive : public Base
{
public:
    Derive(int d) :Base(1000),      DeriveI(d){};
    //overwrite parent virtual function
    virtual void print(void){ cout << "Drive::Drive_print()" ; }
    // New virtual function declared by Derive
    virtual void Drive_print(){ cout << "Drive::Drive_print()" ; }
    virtual ~Derive(){}
private:
    int DeriveI;
};

C + + object model with rewriting under simple inheritance: if there is no rewriting, the parent virtual function will not be overwritten in the subclass virtual function table.

The inheritance class diagram is:

4. Adding multiple inheritance to C + + model

4.1 General multiple inheritance (non diamond inheritance)

From single inheritance, we can know that the derived class only extends the virtual function table of the base class. If it is multi inheritance, how is it extended?

  • ① Each base class has its own virtual table
  • ② The virtual functions of subclasses are placed in the virtual function table of the first base class.
  • ③ In the memory layout, its parent class layout is arranged in the order of declaration.
  • ④ The print() function in the virtual table of each base class is overwritten to print() of the subclass. This is to solve the problem that pointers of different base class types point to the same subclass instance and can call the actual function.
class Base
{
public:
 
    Base(int i) :baseI(i){};
    virtual ~Base(){}
 
    int getI(){ return baseI; }
 
    static void countI(){};
 
    virtual void print(void){ cout << "Base::print()"; }
 
private:
 
    int baseI;
 
    static int baseS;
};
class Base_2
{
public:
    Base_2(int i) :base2I(i){};

    virtual ~Base_2(){}

    int getI(){ return base2I; }

    static void countI(){};

    virtual void print(void){ cout << "Base_2::print()"; }
 
private:
 
    int base2I;
 
    static int base2S;
};
 
class Drive_multyBase :public Base, public Base_2
{
public:

    Drive_multyBase(int d) :Base(1000), Base_2(2000) ,Drive_multyBaseI(d){};
 
    virtual void print(void){ cout << "Drive_multyBase::print" ; }
 
    virtual void Drive_print(){ cout << "Drive_multyBase::Drive_print" ; }
 
private:
    int Drive_multyBaseI;
};

4.2 diamond inheritance

In the memory layout of class D objects:

  • In the figure, cyan represents Class b1 sub object instances, yellow represents class b2 sub object instances, and gray represents class D sub object instances.

  • Because class D indirectly inherits class B twice, class D objects contain two data members ib of class B, one of which belongs to Class B1 and the other to class B2. This not only increases the space, but also causes program ambiguity:

    D d;
    d.ib = 1;    //Ambiguous error. Is B1::ib or B2::ib called?
    
    //Correct calling method
    d.B1::ib = 1;
    d.B2::ib = 1;
    

5. Adding virtual inheritance to C + + model

What is virtual inheritance?

//The content of the class is the same as before
class B{...}
class B1 : virtual public B

Virtual inheritance is to solve the problem of multiple indirect parent classes in repeated inheritance, so you can't use the simple extension above and provide a virtual function pointer for each virtual base class (this will lead to multiple virtual function tables for the base class of repeated inheritance).

The memory structure of the derived class of virtual inheritance is completely different from that of ordinary inheritance:

  • The subclass of virtual inheritance has a separate virtual function table. In addition, a separate virtual function table of the parent class is also saved. A four byte 0x00000000 is used as the boundary between the two parts.
  • In the memory of the derived class, the first is its own virtual function table, then the data member of the derived class, then 0x0, then the virtual function table of the base class, and then the data member of the base class.
  • If the derived class does not have its own virtual function, the derived class will not have a virtual function table, but 0x0 is still required between the derived class data and the base class data.

Summary:

  • Therefore, in virtual inheritance, the data of the derived class and the base class are completely separated. The derived class's own virtual function table and data are stored first, separated by 0x0 in the middle, and finally the virtual function and data of the base class are saved.
  • If the derived class overloads the virtual function of the parent class, the corresponding function of the base class virtual function table in the derived class memory is replaced.
  • In the C + + object model, a hidden virtual base class pointer (vbptr) is generated by the subclass inherited from the virtual base class
  • Therefore, for a class instance, if it has a virtual base class pointer, the virtual base class pointer may be at the 0-byte offset of the instance (when the class has no vptr, vbptr is at the front of the class instance memory layout, otherwise vptr is at the front of the class instance memory layout), or it may be at the 4-byte offset of the class instance.

5.1 simple virtual inheritance

Class diagram is as follows:

The subclass object model is as follows:

5.2 virtual inheritance

Class diagram is as follows:

C + + object model:

Topics: C++ OOP