The previous simple discussion of C + + object model, always feel not deep enough, is near idle to further mining C + + object memory layout. Main discussion: Single inheritance, multiple inheritance, virtual function of diamond inheritance and the case of dummy inheritance. The test procedure is posted, and the test conclusion and the corresponding class object size calculation are given. (PS: The memory layout of class objects depends on the compiler, where tests are based on visual Studio)
a single Class object
A single Class object mainly considers the case of virtual functions, which are described in the previous blog. The virtual function is defined in the class, and a virtual function table is created (essentially a function pointer array, the virtual function table is not in the class, vs compilation environment, the virtual function table is in the constant segment, the virtual table pointer is in the class object), the class defines an object, A virtual table pointer is placed at the top of the object, pointing to the virtual function table, so that the class defines more than 4 Byte (32 bits) of the object.
You can see the C + + memory layout directly in Visual Studio C + + compilation output: Project--Right "properties"--Configure Properties--c/c++--command line--add "/d1reportallclasslayout" to other options You can view the memory layout of a defined class in the compiled output, which outputs all the defined class objects, and you search for your own defined objects. (It's best not to set the name as base, or a lot of it.)
using namespace std;
Class parent
{
//public:
int b;
static int A;
virtual void fun1 ()
{
cout << "parent:fun1" << Endl;
}
/*virtual void Fun2 ()
{
cout << "parent:fun2" << Endl;
} */
};
int main ()
{
parent obj_b;
return 0;
}
The following is a defined class object memory layout: Vfptr represents a virtual table pointer, and the static member is not in the class object
1> class Parent size (8):
1> +---
1> 0 | {VFPTR}
1> 4 | b
1> +---
If the class is not defined as a virtual function, the size of the class object is 4, as follows:
1> class Parent size (4):
1> +---
1> 0 | b
1> +---
Here we consider the case of class inheritance:
Single Inheritance
1) General inheritance, no virtual function
1.1 Inheriting a base class
Class parent
{
//public:
int b;
/*virtual void Fun1 ()
{
cout << "parent:fun1" << Endl;
} */
};
Class Child:public parent
{
int A;
};
int main ()
{child
obj_b;
return 0;
}
Memory layout:
1> class child size (8):
1> +---
1> | +---(base class parent)
1> 0 | | b
1> | +---
1> 4 | A
1> +---
1.2 Again consider the case of inheriting two base classes
Class parent
{
//public:
int b;
/*virtual void Fun1 ()
{
cout << "parent:fun1" << Endl;
} */
};
Class Parent1
{
int c;
};
Class Child:public Parent, public parent1
{
int A;
};
int main ()
{child
obj_b;
return 0;
}
Memory layout:
1> class child size:
1> +---
1> | +---(base class parent)
1> 0 | | b
1> | +---
1> | +---(base class parent1)
1> 4 | | c
1> | +- --
1> 8 | A
1> +---
It can be seen that in the case of general inheritance without virtual function, the memory layout of the derived class object is to store the base class member and then store its own member variable, whose size is the sum of the simple base class object size and the size of its own member. When multiple base classes are inherited, data members are placed in the order of the inheritance Declaration.
2 The existence of virtual functions
2.1 The base class defines a virtual function, and the derived class itself has no virtual functions
Class parent
{
//public:
int b;
virtual void fun1 ()
{
cout << "parent:fun1" << Endl;
}
;
Class Parent1
{
int c;
};
Class Child:public parent/*, public parent1*/
{
int A;
};
int main ()
{child
obj_b;
return 0;
}
Memory layout:
1> class child size:
1> +---
1> | +---(base class parent)
1> 0 | | {VFPTR}
1> 4 | | b
1> | +---
1> 8 | A
1> +---
This is no different from the general inheritance above, except that the virtual table pointer is defined in the base class and then inherited by a derived class.
2.2 Inheriting multiple base classes
Class parent
{
int b;
virtual void fun1 ()
{
cout << "parent:fun1" << Endl;
}
;
Class Parent1
{
int c;
virtual void fun2 ()
{
cout << "Parent1" << Endl;
}
;
Class Child:public Parent, public parent1
{
int A;
};
int main ()
{child
obj_b;
return 0;
}
Memory layout
1> class child size:
1> +---
1> | +---(base class parent)
1> 0 | | {VFPTR}
1> 4 | | b
1> | +---
1> | +---(base class parent1)
1> 8 | | {VFPTR}
1> | | c
1> | +---
1> | A
1> +---
As you can see, inheriting multiple base classes is also the case with the absence of a virtual function, and the difference here and the inheritance is that the derived class produces two virtual table pointers, which we will use to verify that the two virtual table pointers point to different two virtual function tables, and by the way, the derived class inherits several base classes containing virtual functions under non-virtual inheritance. Then the instance object of the derived class will have several virtual table pointers.
2.3 containing virtual functions
1. The base class has no virtual function, the derived class itself defines a virtual function
Class parent
{public
:
int b;
/*virtual void Fun2 ()
{
cout << "parent" << Endl;
} */
};
Class Child:public Parent
{public
:
int A;
virtual void fun2 ()
{
cout << "Child" << Endl;
}
};
int main ()
{return
0;
}
Memory layout
1> class child size:
1> +---
1> 0 | {VFPTR}
1> | +---(base class parent)
1> 4 | | b
1> | +---
1> 8 | A
1> +---
As you can see, the virtual table pointer is always placed at the top of the object, even if it is defined in a derived class.
The base class has virtual functions, derived classes have no virtual functions, this simple, not to repeat the
2. When the base class, the derived class itself defines the case of the virtual function
Class parent
{public
:
int b;
virtual void fun2 ()
{
cout << "parent" << Endl;
}
};
Class Child:public Parent
{public
:
int A;
virtual void fun1 ()
{
cout << "Child" << Endl;
}
};
int main ()
{return
0;
}
Memory layout:
1> class child size:
1> +---
1> | +---(base class parent)
1> 0 | | {VFPTR}
1> 4 | | b
1> | +---
1> 8 | A
1> +---
This first thought that the derived class would also occupy one of its own virtual table pointer space, a look at the wrong. With a closer look at the layout, you will find that the virtual table pointer in the subclass object is an inherited parent object, so the subclass object itself does not produce a virtual function pointer. The answer is yes. So where is the virtual function pointer of the subclass object? is in the same virtual function table. Let's verify this by program.
typedef void (*fun) (void);
int main ()
{child
obj;
Fun Pfun = NULL;
Pfun = (Fun) * (int*) * (int*) (&obj));
Pfun ();
Pfun = (Fun) * (int*) * (int*) (&obj) +1);
Pfun ();
return 0;
}
Output
Parent Child
The above statement Pfun = (fun) * (int*) * (int*) (&obj)); The virtual function table is essentially an array of function pointers in which the virtual function pointers are stored. Object only holds a virtual table pointer to a virtual function table, and is at the very front of the object, which means that the virtual table pointer is the same address as the object. Look back at this program: (int*) (&obj) The object address is also a virtual table pointer (the address of the virtual function table) cast, (int*) * (int*) (&obj) dereference the virtual table pointer and navigates to the virtual function table (array of function pointers), which points to the array (the first position). (Fun) * (int*) * (int*) (&obj) The first element of the array gets the virtual function pointer, then the cast.
As you can see from the test output above, there is only one virtual table pointer in the derived class object, the virtual function pointer of the derived class is in the same virtual function table, and it is behind the base class virtual function pointer.
1> child :: $vftable @:
1> | &child_meta
1> | 0
1> 0 | &parent::fun2
1> 1
If a virtual function in a derived class is named fun2, it naturally overwrites the base class with the same name.
2.3 Multiple Inheritance situation
1 Inheriting multiple base classes
Class parent
{public
:
int A;
virtual void Fun_p ()
{
cout << "parent" << Endl;
}
};
Class adopter
{public
:
int b;
virtual void fun_a ()
{
cout << "adopter" << Endl;
}
;
Class Child:public Parent, public adopter
{public
:
int C;
virtual void Fun_c ()
{
cout << "Child" << Endl;
}
};
int main ()
{return
0;
}
Memory layout:
1> class child size:
1> +---
1> | +---(base class parent)
1> 0 | | {VFPTR}
1> 4 | | A
1> | +---
1> | +---(base class adopter)
1> 8 | | {VFPTR}
1> | | b
1> | +---
1> | c
1> +---
Since all two base classes have virtual functions, this inherits the subclass object with two virtual table pointers, which means the subclass has two virtual function tables. The virtual function pointer in the subclass object is stored in the virtual function table of the inherited first parent class
1> child :: $vftable @parent@:
1> | &child_meta
1> | 0
1> 0 | &parent::fun_p
1> 1 | &child::fun_c
1>
1> Child :: $vftable @adopter@:
1> |-8
1> 0
What if there is only one virtual function in the parent class?
Class parent
{public
:
int A;
/*virtual*/void Fun_p ()
{
cout << "parent" << Endl;
}
;
Class adopter
{public
:
int b;
virtual void fun_a ()
{
cout << "adopter" << Endl;
}
;
Class Child:public Parent, public adopter
{public
:
int C;
virtual void Fun_c ()
{
cout << "Child" << Endl;
}
};
Memory layout
1> class child size:
1> +---
1> | +---(base class adopter)
1> 0 | | {VFPTR}
1> 4 | | b
1> | +---
1> | +---(base class parent)
1> 8 | | a< c45/>1> | +---
1> | c
1> +---
This intentionally does not define a virtual function in the first parent class, looking at the memory layout, the parent class adopter is at the front of the subclass object because it defines a virtual function, and the Visual Studio compiler guarantees that the virtual table pointer is at the top of the object, so that if the inherited multiple parent classes have virtual functions, Then the location is based on the inheritance order, if there is a virtual function not defined, then regardless of the order of inheritance, according to the principle of virtual function precedence, there are virtual functions placed in the front.
2) Cumulative inheritance
class parent {int A;
virtual void Fun_p () {cout << "parent" << Endl;
}
};
Class Child:public Parent {int B;
virtual void Fun_c () {cout << "child" << Endl;
}
};
Class Grandchild:public Child {int C;
virtual void Fun_g () {cout << "grandchild" << Endl;
}
};
int main () {grandchild obj_b;
return 0; }//Memory layout 1> class grandchild size: 1> +---1> | +---(base class child) 1> | | +---(base class Parent) 1> 0 | | | {vfptr} 1> 4 | | | A 1> | | +---1> 8 | | b 1> | +---1> 12 | C 1> +---//virtual function table 1> grandchild:: $vftable @: 1> | &grandchild_meta 1> | 0 1> 0 | &parent::fun_p 1> 1 | &child::fun_c 1> 2 | &grandchild::fun_g
From the above, as well as the combination of the preceding base class with virtual functions, it can be seen that the virtual table pointers in the subclass object depend on the number of parent classes that have the inherited definition of virtual functions (Non-virtual inheritance), and of course if the parent class does not have a virtual function, it depends on whether there If the above subclass (should be called a grandchild), and then inherit parent, then it will be more than one virtual table pointer. The subclass, if it also defines a virtual function, does not produce a virtual table pointer (in the case of an inherited parent class with a virtual table pointer). Instead, it puts its virtual function pointer in the virtual function table of the inherited parent class, which is for the purpose of using virtual function to realize polymorphism, here we discuss the memory layout, the virtual function inheritance and polymorphism, and the following.
Before introducing diamond-type inheritance, we consider the memory layout of class objects under virtual inheritance for several of the scenarios discussed above:
virtual Inheritance (imaginary base class)
Let's take this cumulative inheritance as an example and expand to illustrate how virtual inheritance affects the memory layout of class objects.
Class parent
{
int A;
virtual void Fun_p ()
{
cout << "parent" << Endl;
}
};
Class Child:virtual Public parent
{
int b;
virtual void Fun_c ()
{
cout << "Child" << Endl;
}
};
Class Grandchild:virtual public child
{
int c;
virtual void Fun_g ()
{
cout << "grandchild" << Endl;
}
;
Looking at the memory layout, it's not a little microsecond change.
1> class child size:
1> +---
1> 0 | {VFPTR}
1> 4 | {VBPTR}
1> 8 | b
1> +---
1> +---(virtual base parent)
1> | {VFPTR}
1> | A
1> +---
1> class grandchild size:
1> +---
1> 0 | {VFPTR}
1> 4 | {VBPTR}
1> 8 | c
1> +---
1> +---(virtual base parent)
1> | {VFPTR}
1> | A
1> +---
1> +---(virtual base child)
1> | {VFPTR}
1> | {VBPTR}
1> | b
1> +---
First look at the class child, contrast 2.3.2, general inheritance, the base class derived classes all contain virtual functions (sizeof (CHILD_OBJ) = 12), where the virtual inheritance, instantaneous more than 8 Byte,
First, virtual inheritance, the subclass object will naturally have a virtual base class pointer Vbptr, but the virtual table pointer is still at the top of the object, and one of the biggest changes is that the subclass produces two virtual function tables, so the instantiated object has two virtual table pointers, which subverts the previous conclusion It cannot be simply determined by the number of inherited parent classes that contain virtual functions.
The situation described here is the situation of virtual inheritance, for the former face class containing virtual table pointer number and the number of virtual function is limited to the relationship between the virtual inheritance. For virtual inheritance, you can see that it inherits a subclass in its entirety.
can see that
1> child :: $vftable @child@:
1> | &child_meta
1> | 0
1> 0 | &child::fun_c
1>
1> child :: $vbtable @:
1> 0 |- 4
1> 1 | 8 (CHILDD (child+4) parent)
1>
1> child :: $vftable @parent@:
1 > | -12
1> 0
The middle is a virtual base class pointer. The above two virtual table pointers point to different two virtual function tables, which we can verify by program:
typedef void (*fun) (void);
int main ()
{child
obj;
Fun Pfun = NULL;
Pfun = (Fun) * (int*) * (int*) (&obj));
Pfun ();
Pfun = (Fun) * (int*) * ((int*) (&obj) +3));
Pfun ();
return 0;
}
Output
Child
Parent
For the above pointer, notice the difference from the previous mentioned: the address displacement of the virtual function table and the address displacement within the object.
3. Diamond Type Inheritance
The so-called diamond-type inheritance is the inheritance way like the following "diamond" structure
A
/\
B C
\/
D
The following shows a situation in which:
Class Class_a
{public
:
int A;
void Fun_a ()
{
cout << "a" << Endl;
}
};
Class Class_b:public class_a
{public
:
int B;
void Fun_b ()
{
cout << "B" << Endl;
}
};
Class Class_c:public class_a
{public
:
int C;
void Fun_c ()
{
cout << "C" << Endl;
}
};
Class Class_d:public Class_b, public class_c
{public
:
int D;
void Fun_d ()
{
cout << "D" << Endl;
}
};
int main ()
{
class_d obj;
return 0;
}
The memory layout is as follows
1> class class_d size:
1> +---
1> | +---(base class class_b)
1> | | +---(base class class_a)
1> 0 | | | A
1> | | +---
1> 4 | | b
1> | +---
1> | +---(base class Class_c)
1> | | +---(base class class_a)
1& gt; 8 | | | a
1> | | +---
1> | | c
1> | +---
1> | D
1> +---
The above program has a problem, from the memory layout can be seen, if the invocation of the base class member A, it will produce two semantic, which is called, because the BC has inherited a member variable, the final subclass object has a two copies. This time the virtual base class turned out to be a virtual inheritance. If the above takes a virtual inheritance, then the instance of the last subclass D will only have one, look below
Class Class_a
{public
:
int A;
virtual void fun_a ()
{
cout << "a" << Endl;
}
};
Class Class_b:virtual public class_a
{public
:
int B;
virtual void Fun_b ()
{
cout << "B" << Endl;
}
};
Class Class_c:virtual public class_a
{public
:
int C;
virtual void Fun_c ()
{
cout << "C" << Endl;
}
};
Class Class_d:public Class_b, public class_c
{public
:
int D;
virtual void Fun_d ()
{
cout << "D" << Endl;
}
};
int main ()
{
class_d obj;
return 0;
}
Before you look at the memory layout, analyze how much memory the instance of this derived class class_d will consume. According to the previous summary, Class_d inherits two base classes that contain virtual functions, then there are two virtual table pointers in their objects (their own virtual function pointers are placed in the first virtual function table), one brain inherits and no virtual table pointers are generated by themselves. Can look directly at the inherited Class_b, virtual inheritance A, then Class_b will occupy the Bytes (virtual table pointers, virtual base class pointers, and int member variables), the same class_c will occupy a Bytes (virtual inheritance itself will produce redundant virtual table pointers), and then consider the total inherited a , it will have a virtual table pointer, the member variable is then placed at the end of the derived class, and the members of a will only have a copy (* bytes) in the derived class D, which is the purpose of the virtual base class, avoids ambiguity, and then class_d, since it is a general inheritance, is an int type 4 Byte. The total down is 12+12+8+4 = Bytes.
1> class class_d size:
1> +---
1> | +---(base class class_b)
1> 0 | | {VFPTR}
1> 4 | | {VBPTR}
1> 8 | | b
1> | +---
1> | +---(base class Class_c)
1> | | {VFPTR}
1> | | {VBPTR}
1> | | c
1> | +---
1> | d
1> +---
1> + ---(virtual base class_a)
1> | {VFPTR}
1> | A
1> +---
If the last class_d also virtual inheritance B and C (two are virtual inheritance only line), then the final size will be how much. Virtual inheritance will be more than the virtual base class pointers, and then itself will be redundant to produce a virtual table pointer, then more than 8Bytes, the final size is Bytes, very large.
If just Class_d is just a virtual inheritance of one of the base classes, well, that means it's messy, in visual Studio, although the size doesn't change, there's no more virtual table pointers and virtual base class pointers, but the memory layout changes,
The code is not pasted, look directly at the memory layout will also know the inheritance relationship:
1> class class_d size:
1> +---
1> | +---(base class Class_c)
1> 0 | | {VFPTR}
1> 4 | | {VBPTR}
1> 8 | | c
1> | +---
1> | d
1> +---
1> +- --(virtual base class_a
) 1> | {VFPTR}
1> | A
1> +---
1> +---(virtual base class_b)
1> | {VFPTR}
1> | {VBPTR}
1> | b
1> +---
Combined with the preceding virtual inheritance memory layout, you will find that the virtual inheritance is left behind. There is no point in further scrutiny.
The following procedure tells you that a virtual table pointer does not always precede the object:
Class Class_a
{public
:
int A;
virtual void fun_a ()
{
cout << "a" << Endl;
}
};
Class Class_b:virtual public class_a
{public
:
int B;
void Fun_b ()
{
cout << "B" << Endl;
}
};
typedef void (*fun) (void);
int main ()
{
class_b obj;
Fun Pfun = NULL;
Pfun = (Fun) * (int*) * ((int*) (&obj) +2));
Pfun ();
return 0;
}
Memory Layout
1> class class_b size:
1> +---
1> 0 | {VBPTR}
1> 4 | b
1> +---
1> +---(virtual base class_a)
1> 8 | {VFPTR}
1 > 12 | A
1 > +---
A big chunk of the front, we can draw the following points: under General inheritance (no virtual function), the memory layout of the derived class is to place the base class members first, the multiple base classes in the order of inheritance, and finally the member variables of the derived class itself, and the functions and static variables are not in the class object. The excrement of a derived class object is the sum of the size of the inherited base class and the size of its own variable. Under General inheritance (including virtual functions), the difference with the above is to consider the virtual table pointer. 1. In cases where a derived class inherits only a base class, whether the base class defines a virtual function or a derived class itself has a virtual function, or if two have virtual functions, the last derived class object will only have a virtual table pointer and is at the front of the object. If all two have virtual functions, the virtual function pointer of the derived class is stored behind the virtual function pointer of the base class in the Virtual function table (array). 2, when a derived class inherits more than one base class, if only one base class in multiple base classes has a virtual function, it is the same as before. When multiple base classes have virtual functions, a derived class object produces multiple virtual table pointers, and the number of virtual table pointers is the same as the number of inherited base classes with virtual functions, and the virtual function pointers for derived classes are placed in the first virtual function table. Virtual inheritance (with virtual functions), virtual inheritance in addition to a virtual base class pointer, the entire object's memory layout will also change. As long as it is virtual inheritance, the corresponding derived class produces a virtual base class pointer. In addition, as long as it is virtual inheritance, the base class will and only have a copy in the derived class object, and the virtual inheritance is placed behind, even if it has a virtual table pointer (The virtual table pointer is not necessarily at the front of the object). To further summarize the virtual table pointer condition under virtual inheritance, the derived class will have a full copy of the base class because of virtual inheritance, so that the derived class will produce superfluous own virtual table pointers.
When calculating the size of a class object, it is easy to consider the above points. Incidentally, the tests and summaries above are based on the visual Studio compiler.