|
Foreword
The article is longer, but the content is relatively dry, and I hope the C ++ object memory layout, the virtual table pointer, the virtual base class pointers, etc. have a good understanding of friends can take your time.
Conclusions have been verified on VS2013. Different compilers may differ in the details of the memory layout.
If the article has poorly explained, inexplicable omissions or place urge said.
What is the C ++ object model?
References "depth exploration of C ++ object model" in the words of this book:
There are two concepts can be explained by the C ++ object model:
Language directly support object-oriented programming part.
For various supported underlying implementation mechanism.
Direct support for object-oriented programming, including constructors, destructors, polymorphism, virtual functions, and so on, these discussions have in many books, but also C ++ best known place (characteristic). The implementation mechanism underlying object model is rarely discussed in books. Implementation mechanism underlying object model not standardized, different compilers have a certain freedom to design and implementation details of the object model. In my opinion, the object model is an object of study on the storage space and better on time, and the C ++ object-oriented technology to support, such as the virtual pointer to the virtual table mechanism to support multi-state properties.
Article Description
This article is mainly to discuss the C ++ object in memory layout, it belongs to the second category of concept studies. The C ++ object-oriented programming direct support part much to say. The article reads as follows:
Virtual function table to resolve. Virtual function or its parent class containing virtual functions contains a compiler would like to add a virtual function table, vptr, to understand constitute virtual function tables, to facilitate understanding of the C ++ object model.
Virtual base class table resolution. Virtual inheritance generate virtual base class table (vbptr), the contents of the virtual base class virtual function table and the table is completely different, we will introduce the virtual function tables to explain the virtual inheritance.
Object Model Overview: The simple object model, table-driven object model, and the C ++ object model non-inherited cases.
Inheritance C ++ object model. Analysis of C ++ class objects in the following scenario memory layout:
Single Inheritance: subclasses inherit from a single parent class, analyze the subclass overrides the parent class virtual function, define a new subclass of the class object virtual function case erupted in memory layout.
Multiple inheritance: a subclass inherits from multiple parent classes, subclasses analyzed virtual function override the parent class, subclass defines a new virtual function situation erupted class object memory layout, and analyzes the non-virtual inheritance under diamond inheritance.
Virtual inheritance: analysis of single inheritance under virtual inheritance, virtual base layer under multiple inheritance, virtual inheritance under repeated inheritance.
Once you understand the memory layout of objects, we can analyze some of the problems:
Distribution costs C ++ package brings is what?
Empty class inheritance hierarchy from the composition, the size of each class of the object is how much?
As for the other memory-related knowledge, I assume that we have a certain understanding, such as memory alignment, pointer manipulation. This article may be obscure at first glance, asking readers to have some C ++, based on the concept of a certain grasp.
Understand the virtual function table
Polymorphism and virtual tables
The role of C ++ virtual functions primarily to achieve polymorphism mechanism. Polymorphism, in simple terms, refers to the inheritance hierarchy, parent class pointer can have a variety of forms - when it is pointing to a subclass object, it can be invoked by a subclass to function, rather than a function of the parent class .
class Base {virtualvoid print (void);}
class Drive1: public Base {virtualvoid print (void);}
class Drive2: public Base {virtualvoid print (void);}
Base * ptr1 = new Base;
Base * ptr2 = new Drive1;
Base * ptr3 = new Drive2;
ptr1-> print (); // call Base :: print ()
prt2-> print (); // call Drive1 :: print ()
prt3-> print (); // call Drive2 :: print ()
This is a run-time polymorphism, that is the parent class pointer only when the program is running in order to know what the real type meant. This run-resolution, through the virtual function table to achieve.
Use pointers to access virtual tables
If we enrich our Base class, it has a plurality of virtual functions:
class Base
{
public:
Base (int i): baseI (i) {};
virtualvoid print (void) {cout << "virtual function call Base :: print ()";}
virtualvoid setI () {cout << "virtual function call Base :: setI ()";}
virtual ~ Base () {}
private:
int baseI;
};
When a class itself defines a virtual function, or its parent class has virtual functions, in order to support multi-state mechanism, the compiler will add a class virtual function pointer (vptr). Virtual function pointer is generally placed in the first position of the object memory layout, this is to ensure that in the case of multilayer inheritance or multiple inheritance with maximum efficiency to get the virtual function table.
When vprt located in 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 * vptrAdree = (int *) (& b);
cout << "virtual function pointer (vprt) The address is: \ t" << vptrAdree << endl;
We run the code the results
We are forced to address the class object converted to type int *, made a virtual function pointer address. Virtual function pointer to the virtual function table, the virtual function table stored in the address is a series of virtual function, virtual function address in the same order that appears in the class virtual function declaration. Virtual function pointer address value, you can get the address of the virtual function table, that is the address of the virtual function table a virtual function:
typedefvoid (* Fun) (void);
Fun vfunc = (Fun) * ((int *) * (int *) (& b));
cout << "address of the first virtual function is:" << (int *) * (int *) (& b) << endl;
cout << "the address, calling the virtual function Base :: print ():";
vfunc ();
We value the virtual table pointer taken out: * (int *) (& b), it is an address, the address of the virtual function table
The address of the virtual function table cast to int *: (int *) * (int *) (& b)
Then convert it to us Fun pointer types: (Fun) * (int *) * (int *) (& b)
Thus, we have made a first class virtual function, we can access it through a function pointer.
Similarly, the second virtual function setI () The address is:
(Int *) (* (int *) (& b) +1)
It can also be accessed through a function pointer, here left to the reader test.
So far, we know that the class virtual table pointer vprt origin, known virtual function table of contents, and how to access the virtual function table pointer. The following article will often use pointers to access objects memory to verify our C ++ object model and to discuss changes in a variety of succession virtual table pointer, put the contents of this part of the digested then went to see the following.
Object Model Overview
In C ++, there are two data members (class data members): static and nonstatic, as well as three types of member functions (class member functions): static, nonstatic and virtual:
Now we have a class Base, which contains the above in this type of data or function 5:
class Base
{
public:
Base (int i): baseI (i) {};
int getI () {return baseI;}
staticvoid countI () {};
virtualvoid print (void) {cout << "Base :: print ()";}
virtual ~ Base () {}
private:
int baseI;
static int baseS;
};
So, this is how the class will be represented in memory? 5 kinds of data are stored in a row it? How the layout to support C ++ polymorphism? How our C ++ compiler will create standards and a variety of data members and member function?
Simple Object Model
Description: At the following figure, the blue border box up content is contiguous in memory.
This model is very simple and crude. In this model, the object consists of a series of pointers, each pointer points to a data member or member function. In other words, each data members and member functions in the class, their size is the same, are size of a pointer. This has the advantage - it is easy to calculate the size of the object, but lose the space and the implementation of efficiency. Imagine if our class is Point3d this model will be a lot more space to store the pointer to a function than struct C language, and data members read each class must go through again addressing - It is time-consuming on.
So this has not been the object model for the actual product.
Table-driven model
This model has added on the basis of a simple object model on a layer of indirection that the data class is divided into two parts: the part of the data portion of the function, and the use of two tables, a data store itself, a storage function address (that is a member of more than one function is addressed), and the class object only contains two pointers, pointing to the above two tables. It would appear that the size of the object's size is fixed at two pointers. This model has not actually applied for the real C ++ compiler.
C ++ object model under the non-inherited
Overview: In this model, nonstatic data member is placed in a class for each object, and the static data member is disposed outside of the class object. static and nonstatic functions are also placed outside the class object, and for virtual function, virtual function through a pointer to a virtual table + support, as follows:
Each class generates a form, called a virtual table (virtual table, referred vtbl). Stored in the virtual table a bunch of pointers, which point to a virtual function for each class. Order virtual table function declaration will address, but when the child class has multiple overloaded function exception, will be discussed later.
Each class object has a virtual table pointer (vptr), its generated by the compiler. Set and reset the copy control rests class virtual table pointer (that is, constructors, destructors, assignment operator) to complete. vptr position is determined by the compiler, which is traditionally placed on all members of the show after the declaration, but now many compiler vptr at the forefront of a class object. Details about the layout of the data members, will analyze in detail later.
In addition, in front of the virtual function table is provided with a pointer pointing type_info to support RTTI (Run Time Type Identification, run-time type identification). RTTI is polymorphic and generated information, including the object inheritance, a description of the object itself, and only the objects that have virtual functions will generate.
In this model, Base object model
The first class of the object layout verification in VS:
Base b (1000);
B visible objects containing a vfptr, namely vprt. And only the nonstatic members are placed in the data object. We started vfprt:
vfptr There are two types of data pointer (address), the first point to the destructor Base class, the second one to the Base virtual function print, the same order as the declaration order.
This is consistent with the above-mentioned C ++ object model. You can also be verified by the code:
void testBase (Base & p)
{
cout << "object memory starting address:" << & p << endl;
cout << "type_info message:" << endl;
RTTICompleteObjectLocator str = * ((RTTICompleteObjectLocator *) * ((int *) * (int *) (& p) - 1));
string classname (str.pTypeDescriptor-> name);
classname = classname.substr (4, classname.find ( "@@") - 4);
cout << "according to information output type_info class name:" << classname << endl;
cout << "virtual function table address:" << (int *) (& p) << endl;
// Verify virtual table
cout << "virtual function table address of a function:" << (int *) * ((int *) (& p)) << endl;
cout << "destructor Address:" << (int *) * (int *) * ((int *) (& p)) << endl;
cout << "virtual function table, namely the second virtual function print () Address:" << ((int *) * (int *) (& p) + 1) << endl;
// Call a virtual function print by address ()
typedefvoid (* Fun) (void);
Fun IsPrint = (Fun) * ((int *) * (int *) (& p) + 1);
cout << endl;
cout << "virtual function call";
IsPrint (); // if the address is correct, then calling the virtual function print Base class ()
cout << endl;
// Enter the static function address
p.countI (); // call the function to create a first instance
cout << "static function countI () Address:" << p.countI << endl;
// Verify nonstatic data members
cout << "presumed nonstatic data members baseI address:" << (int *) (& p) + 1 << endl;
cout << "according inferred address, the output value of the address:" << * ((int *) (& p) + 1) << endl;
cout << "Base :: getI ():" << p.getI () << endl;
}
Base b (1000);
testBase (b);
Result analysis:
Get the address of the virtual function table by (int *) (& p)
type_info information does exist in the location before a virtual table. By ((int) (int *) (& p) - 1) to obtain type_infn information), and successfully obtained the name of the class of Base
The first function is a virtual function table destructor.
The second function is a virtual function table virtual function print (), after obtaining the address to call it by address (instead of through the object), verify that the correct
The next location for the virtual table pointer of nonstatic data member baseI.
You can see, static member functions and virtual table pointer address Dan, baseI address different Dan.
Okay, so far we understand the non-inherited class object under five kinds of data layout in memory, but also know the beginning of each virtual function table has a pointer to type_info, in charge of support for RTTI. Join succession after the class object in memory how to represent it?
C ++ object model is derived under
Single inheritance
If we define a derived class
class Derive: public Base
{
public:
Derive (int d): Base (1000), DeriveI (d) {};
// Overwrite the parent class virtual function
virtualvoid print (void) {cout << "Drive :: Drive_print ()";}
// Derive new virtual function declaration
virtualvoid Drive_print () {cout << "Drive :: Drive_print ()";}
virtual ~ Derive () {}
private:
int DeriveI;
};
How to create a derived class the parent class's instance on the machine level it? In a simple object model, you can assign a pointer to each base class sub-object in the subclass object.
The disadvantage is the result of a simple object model of space caused by indirect access additional burden on time, the advantage is the size of the class is fixed, the base class changes will not affect the size of the child class object.
In the table-driven object model, we can add a third sub-class object pointer: the base class pointer (bptr), point to base class pointer to a base class table (base class table), the same, because the indirect cause of the space and access additional burden on time, the advantage is no need to change the sub-class object itself can change the base class. Table-driven model diagram is not paste out.
In the C ++ object model for general inheritance (usually relative to the virtual inheritance purposes), if a subclass overrides (overwrite) the virtual function of the parent class, the child class virtual function will override the parent class virtual table corresponding function; if subclass does not overwrite the parent class virtual functions, but declared his new virtual function, the virtual address of the function will be expanded to the parent class virtual function table and finally (in vs can not see by monitoring the results of the expansion, but we take the address of the method can be done, a new sub-class virtual function does the child the parent class object virtual function table end). For virtual inheritance, if the sub-class overwrite the parent class virtual functions in the same manner will override the parent child object virtual function table corresponding to the position, and if the child declares his new virtual function, the compiler will increase subclass a new virtual table pointer vptr, which is different from the general inheritance, is considered later.
We use the code to validate the model above
typedefvoid (* Fun) (void);
int main ()
{
Derive d (2000);
// [0]
cout << "[0] Base :: vptr";
cout << "\ t Address:" << (int *) (& d) << endl;
// Vprt [0]
cout << "[0]";
Fun fun1 = (Fun) * ((int *) * ((int *) (& d)));
fun1 ();
cout << "\ t Address: \ t" << * ((int *) * ((int *) (& d))) << endl;
// Vprt [1] destructor can not be called by the address, so the manual output
cout << "[1]" << "Derive :: ~ Derive" << endl;
// Vprt [2]
cout << "[2]";
Fun fun2 = (Fun) * ((int *) * ((int *) (& d)) + 2);
fun2 ();
cout << "\ t Address: \ t" << * ((int *) * ((int *) (& d)) + 2) << endl;
//[1]
cout << "[2] Base :: baseI =" << * (int *) ((int *) (& d) + 1);
cout << "\ t Address:" << (int *) (& d) + 1;
cout << endl;
//[2]
cout << "[2] Derive :: DeriveI =" << * (int *) ((int *) (& d) + 2);
cout << "\ t Address:" << (int *) (& d) + 2;
cout << endl;
getchar ();
}
Multiple Inheritance
Usually multiple inheritance (non-diamond inheritance)
Single inheritance (usually inherited), sub-class extends the parent class virtual function table. In multiple inheritance, the sub-class sub-object containing more than one parent class to the parent class virtual function table which extended it? When a subclass overwrite the parent class function, you need to cover more than one parent class virtual function table do?
Subclass virtual function is declared virtual function table on the first base class.
overwrite, all the base class print () functions quilt class print () function coverage.
Memory layout, the parent class arranged in order of their declaration.
The second point which ensures the parent class pointer pointing to a subclass of objects, always be able to call to the real function.
In order to facilitate viewing, we pasted the code
class Base
{
public:
Base (int i): baseI (i) {};
virtual ~ Base () {}
int getI () {return baseI;}
staticvoid countI () {};
virtualvoid 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;}
staticvoid countI () {};
virtualvoid 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) {};
virtualvoid print (void) {cout << "Drive_multyBase :: print";}
virtualvoid Drive_print () {cout << "Drive_multyBase :: Drive_print";}
private:
int Drive_multyBaseI;
};
We use the code to verify:
typedefvoid (* Fun) (void);
int main ()
{
Drive_multyBase d (3000);
// [0]
cout << "[0] Base :: vptr";
cout << "\ t Address:" << (int *) (& d) << endl;
// Vprt [0] destructor can not be called by the address, so the manual output
cout << "[0]" << "Derive :: ~ Derive" << endl;
// Vprt [1]
cout << "[1]";
Fun fun1 = (Fun) * ((int *) * ((int *) (& d)) + 1);
fun1 ();
cout << "\ t Address: \ t" << * ((int *) * ((int *) (& d)) + 1) << endl;
// Vprt [2]
cout << "[2]";
Fun fun2 = (Fun) * ((int *) * ((int *) (& d)) + 2);
fun2 ();
cout << "\ t Address: \ t" << * ((int *) * ((int *) (& d)) + 2) << endl;
//[1]
cout << "[1] Base :: baseI =" << * (int *) ((int *) (& d) + 1);
cout << "\ t Address:" << (int *) (& d) + 1;
cout << endl;
//[2]
cout << "[2] Base _ :: vptr";
cout << "\ t Address:" << (int *) (& d) +2 << endl;
// Vprt [0] destructor can not be called by the address, so the manual output
cout << "[0]" << "Drive_multyBase :: ~ Derive" << endl;
// Vprt [1]
cout << "[1]";
Fun fun4 = (Fun) * ((int *) * ((int *) (& d)) + 1);
fun4 ();
cout << "\ t Address: \ t" << * ((int *) * ((int *) (& d)) + 1) << endl;
// [3]
cout << "[3] Base_2 :: base2I =" << * (int *) ((int *) (& d) + 3);
cout << "\ t Address:" << (int *) (& d) + 3;
cout << endl;
// [4]
cout << "[4] Drive_multyBase :: Drive_multyBaseI =" << * (int *) ((int *) (& d) + 4);
cout << "\ t Address:" << (int *) (& d) + 4;
cout << endl;
getchar ();
}
Diamond inheritance
Inheritance is also known as diamond or diamond-type inheritance repeated inheritance, which refers to the base class is a derived class inherits Profile repeated many times. Thus, the derived class object has multiple instances of the base class (this can cause problems). In order to facilitate the description, we do not use the code above, while a re-write of repeated inheritance hierarchy:
class B
{
public:
int ib;
public:
B (int i = 1): ib (i) {}
virtualvoid f () {cout << "B :: f ()" << endl;}
virtualvoid Bf () {cout << "B :: Bf ()" << endl;}
};
class B1: public B
{
public:
int ib1;
public:
B1 (int i = 100): ib1 (i) {}
virtualvoid f () {cout << "B1 :: f ()" << endl;}
virtualvoid f1 () {cout << "B1 :: f1 ()" << endl;}
virtualvoid Bf1 () {cout << "B1 :: Bf1 ()" << endl;}
};
class B2: public B
{
public:
int ib2;
public:
B2 (int i = 1000): ib2 (i) {}
virtualvoid f () {cout << "B2 :: f ()" << endl;}
virtualvoid f2 () {cout << "B2 :: f2 ()" << endl;}
virtualvoid Bf2 () {cout << "B2 :: Bf2 ()" << endl;}
};
class D: public B1, public B2
{
public:
int id;
public:
D (int i = 10000): id (i) {}
virtualvoid f () {cout << "D :: f ()" << endl;}
virtualvoid f1 () {cout << "D :: f1 ()" << endl;}
virtualvoid f2 () {cout << "D :: f2 ()" << endl;}
virtualvoid Df () {cout << "D :: Df ()" << endl;}
};
In this case, according to single inheritance, we can analyze the B1, B2 class memory layout inherits from class B when. And according to the general multiple inheritance, we can analyze the class D memory layout.
Class D objects in memory layout, the figure represents a cyan sub-class object instance b1, b2 yellow sub-class object instance, gray D-sub object instance. It can be seen from the figure, since the class D inherits indirectly the class B twice, resulting in an object class D contains two data members of the class B ib, a source belonging to class B1, B2 a source category. This not only increases the space, more importantly, the program is causing ambiguity:
D d;
d.ib = 1; // ambiguity error, the call is B1 or B2 of the ib ib?
d.B1 :: ib = 1; // correct
d.B2 :: ib = 1; // correct
Although we can clear the path specified in the call to eliminate the ambiguity, but the potential for ambiguity has not been eliminated, we can make the class D through virtual inheritance has only a ib entity.
Virtual inheritance
Virtual inheritance to solve the case of the most-derived class diamond inheritance indirect parent has more than one instance of the class. Virtual inheritance derived class memory layout with many different common inheritance, mainly reflected in:
Virtual inheritance subclass, if itself defines a new virtual function, the compiler to generate a virtual function pointer (vptr) and a virtual function tables. The vptr located in front of the object memory.
vs non-virtual inheritance: direct extend the parent class virtual function table.
Virtual inheritance subclass alone retained vprt virtual function table of the parent class. This part connected with the sub-class content to a four-byte boundary to 0.
Virtual inheritance subclass object, containing four bytes of the virtual table pointer offset value.
In order to analyze the final diamond inheritance, we start with a single virtual inheritance succession began.
Virtual base class table parsing
In the C ++ object model, virtual inherited subclass will generate a hidden virtual base class pointer (vbptr), in Microsoft Visual C ++, a virtual base class table pointer is always after the virtual function table pointer, therefore, for a instances of the class, if it has a virtual base class pointer, then the virtual base class pointer in the 0 byte offset possible instance when (the class is not vptr, vbptr is in front of the class instance memory layout, otherwise in vptr examples of top class memory layout), may also be a 4-byte offset class instance.
Virtual base class pointer pointing to a class of virtual base class table, and virtual function tables, virtual base class table is also composed of a plurality of entries, the entry is stored in the offset value. The first entry is stored virtual base class table pointer (vbptr) where the value of such memory address to the first address offset by the analysis of the first paragraph we know, this offset value is 0 (no class vptr) or 4 ( class has virtual functions, then there vptr). We will be better understood by a map.
Second and third virtual base class table entries were ... kind of virtual inheritance left-most parent class, second class ... inherited his father left virtual memory address with respect to the virtual base class table pointer offset value , which we will verify below.
Simple virtual inheritance
If our virtual B1 class inherits from class B:
// Class contents and the same as before
class B {...}
class B1: virtual public B
We B1 class object pointer accesses memory in order to verify the above C ++ object model:
int main ()
{
B1 a;
cout << "B1 Objects Memory size:" << sizeof (a) << endl;
// Get the virtual function table B1
cout << "[0] B1 :: vptr";
cout << "\ t Address:" << (int *) (& a) << endl;
// Output virtual table B1 :: vptr the function
for (int i = 0; i <2; ++ i)
{
cout << "[" << i << "]";
Fun fun1 = (Fun) * ((int *) * (int *) (& a) + i);
fun1 ();
cout << "\ t Address: \ t" << * ((int *) * (int *) (& a) + i) << endl;
}
//[1]
cout << "[1] vbptr";
Address // virtual table pointer; << (int *) (& a) + 1 << endl: cout << "\ t Address"
// Output content virtual base class pointer entry within the meaning of
for (int i = 0; i <2; i ++)
{
cout << "[" << i << "]";
cout << * (int *) ((int *) * ((int *) (& a) + 1) + i);
cout << endl;
}
//[2]
cout << "[2] B1 :: ib1 =" << * (int *) ((int *) (& a) + 2);
cout << "\ t Address:" << (int *) (& a) + 2;
cout << endl;
// [3]
cout << "[3] value =" << * (int *) ((int *) (& a) + 3);
cout << "\ t \ t Address:" << (int *) (& a) + 3;
cout << endl;
// [4]
cout << "[4] B :: vptr";
cout << "\ t Address:" << (int *) (& a) +3 << endl;
// Output B :: vptr virtual functions
for (int i = 0; i <2; ++ i)
{
cout << "[" << i << "]";
Fun fun1 = (Fun) * ((int *) * ((int *) (& a) + 4) + i);
fun1 ();
cout << "\ t Address: \ t" << * ((int *) * ((int *) (& a) + 4) + i) << endl;
}
// [5]
cout << "[5] B :: ib =" << * (int *) ((int *) (& a) + 5);
cout << "\ t Address:" << (int *) (& a) + 5;
cout << endl;
The results of our C ++ object model diagram in full compliance. Then we can analyze the virtual table pointer of the second entry of the value of the specific source 12, memories mentioned above:
Second, third ... entry followed by the left-most class virtual inheritance parent class, second class ... inherited his father left virtual memory address with respect to the virtual base class table pointer offset value.
In our case, that is, instances of class B with respect vbptr memory address offset value, that is: [4] - [1] of the offset value, the result is 12, from the address can be calculated: decimal 007CFDFC-007CFDF4 results exactly 12. Now, we have virtual base class table configuration should have a better understanding.
Virtual diamond inheritance
If we have the following inheritance hierarchy:
class B {...}
class B1: virtual public B {...}
class B2: virtual public B {...}
class D: public B1, public B2 {...}
Diamond virtual inheritance, the most-derived class class D object models have different make up. On the D class object memory structure, the following points:
Class D objects in memory, the order of the base class is emerging: first B1 (left-most parent class), then B2 (second left parent class), and finally the B (imaginary grandfather category)
Data members of a class D object id on the front of the class B, two pieces of data still to 0 to separate.
The compiler does not generate a class D of its own vptr, but covers and extends virtual base class table leftmost parent class, inheriting the same simple object model.
Content super class B into Class D final memory layout of objects.
Using the code below to verify:
int main ()
{
D d;
cout << "D Objects Memory size:" << sizeof (d) << endl;
// Get the virtual function table B1
cout << "[0] B1 :: vptr";
cout << "\ t Address:" << (int *) (& d) << endl;
// Output virtual table B1 :: vptr the function
for (int i = 0; i <3; ++ i)
{
cout << "[" << i << "]";
Fun fun1 = (Fun) * ((int *) * (int *) (& d) + i);
fun1 ();
cout << "\ t Address: \ t" << * ((int *) * (int *) (& d) + i) << endl;
}
//[1]
cout << "[1] B1 :: vbptr";
Address // virtual table pointer; << (int *) (& d) + 1 << endl: cout << "\ t Address"
// Output content virtual base class pointer entry within the meaning of
for (int i = 0; i <2; i ++)
{
cout << "[" << i << "]";
cout << * (int *) ((int *) * ((int *) (& d) + 1) + i);
cout << endl;
}
//[2]
cout << "[2] B1 :: ib1 =" << * (int *) ((int *) (& d) + 2);
cout << "\ t Address:" << (int *) (& d) + 2;
cout << endl;
// [3]
cout << "[3] B2 :: vptr";
cout << "\ t Address:" << (int *) (& d) + 3 << endl;
// Output B2 :: vptr virtual functions
for (int i = 0; i <2; ++ i)
{
cout << "[" << i << "]";
Fun fun1 = (Fun) * ((int *) * ((int *) (& d) + 3) + i);
fun1 ();
cout << "\ t Address: \ t" << * ((int *) * ((int *) (& d) + 3) + i) << endl;
}
// [4]
cout << "[4] B2 :: vbptr";
Address // virtual table pointer; << (int *) (& d) + 4 << endl: cout << "\ t Address"
// Output content virtual base class pointer entry within the meaning of
for (int i = 0; i <2; i ++)
{
cout << "[" << i << "]";
cout << * (int *) ((int *) * ((int *) (& d) + 4) + i);
cout << endl;
}
// [5]
cout << "[5] B2 :: ib2 =" << * (int *) ((int *) (& d) + 5);
cout << "\ t Address:" << (int *) (& d) + 5;
cout << endl;
// [6]
cout << "[6] D :: id =" << * (int *) ((int *) (& d) + 6);
cout << "\ t Address:" << (int *) (& d) + 6;
cout << endl;
// [7]
cout << "[7] value =" << * (int *) ((int *) (& d) + 7);
cout << "\ t \ t Address:" << (int *) (& d) + 7;
cout << endl;
// Indirect parent
//[8]
cout << "[8] B :: vptr";
cout << "\ t Address:" << (int *) (& d) + 8 << endl;
// Output B :: vptr virtual functions
for (int i = 0; i <2; ++ i)
{
cout << "[" << i << "]";
Fun fun1 = (Fun) * ((int *) * ((int *) (& d) + 8) + i);
fun1 ();
cout << "\ t Address: \ t" << * ((int *) * ((int *) (& d) + 8) + i) << endl;
}
//[9]
cout << "[9] B :: id =" << * (int *) ((int *) (& d) + 9);
cout << "\ t Address:" << (int *) (& d) +9;
cout << endl;
getchar ();
}
Some questions
Distribution costs C ++ package brings is what?
In the C language, "Operating data processing (function)", "data" and is separate statements, that is, the language itself does not support the relevance "of data and functions" between.
In C ++, we will come through the class attributes and operations bundled together, known as ADT, abstract data structures.
C language used in the struct (structure) to encapsulate data, use the function to process the data. For example, if we define a struct Point3 as follows:
typedef struct Point3
{
float x;
float y;
float z;
} Point3;
To print this Point3d, we can define a function:
void Point3d_print (const Point3d * pd)
{
printf ( "(% f,% f,% f)", pd-> x, pd-> y, pd_z);
}
In C ++, we prefer to define a Point3d class to ADT to implement the above actions:
class Point3d
{
public:
point3d (float x = 0.0, float y = 0.0, float z = 0.0)
: _x (X), _y (y), _z (z) {}
float x () const {return _x;}
float y () const {return _y;}
float z () const {return _z;}
private:
float _x;
float _y;
float _z;
};
inline ostream &
operator << (ostream & os, const Point3d & pt)
{
os << "(" << pr.x () << ","
<< Pt.y () << "," << pt.z () << ")";
}
See this code, a lot of people the first question might be: added packaging, distribution costs increased much? The answer is that class Point3d did not increase costs. Learned the C ++ object model, we know that memory Point3d class object, only three data members.
The above class declaration, the three members of the data contained in each Point3d direct object, and member functions declared in the class though, does not appear in the class object (object), and those functions (non-inline) belong to the class not part of the class object, only the class produces a unique function instance.
So, Point3d package did not bring any space or affect the efficiency of the implementation period. And in the following situation, C ++ package additional costs will be displayed:
Virtual function mechanism (virtual function), to support the implementation of binding, to achieve polymorphism.
Virtual base class (virtual base class), virtual inheritance relations virtual base class for multiple inheritance to ensure that the base class has a unique instance of the subclass.
Moreover, the memory layout and c language Point3d class data members of the struct member Point3d memory layout is the same. C ++, at the same access identifier (refer to public, private, protected) under the data members declared in memory must ensure that its declaration order. The members of different access identifier is declared under no such requirement. For Point3 class, its members are in the next three data private, declared the order they appear together in memory. We can do the next experiment:
void TestPoint3Member (const Point3d & p)
{
cout << "speculate _x address is:" << (float *) (& p) << endl;
cout << "speculate _y address is:" << (float *) (& p) + 1 << endl;
cout << "speculate _z address is:" << (float *) (& p) + 2 << endl;
cout << "according inferred address output value of _x:" << * ((float *) (& p)) << endl;
cout << "according inferred address output value of the _y:" << * ((float *) (& p) +1) << endl;
cout << "according inferred address output value of _z:" << * ((float *) (& p) +2) << endl;
}
// Test code
Point3d a (1,2,3);
TestPoint3Member (a);
Can be seen from the results, _x, _y, _z three data members immediately in memory.
in conclusion:
Does not consider virtual functions and virtual inheritance, when the data are in the same access identifier under, C ++ struct Class and C language on the object size and memory layout is the same, C ++ package did not bring on the space time influences.
The following empty class inheritance hierarchy configuration, the size of each class is how much?
Today there are classes are as follows:
class B {};
class B1: public virtual B {};
class B2: public virtual B {};
class D: public B1, public B2 {};
int main ()
{
B b;
B1 b1;
B2 b2;
D d;
cout << "sizeof (b) =" << sizeof (b) << endl;
cout << "sizeof (b1) =" << sizeof (b1) << endl;
cout << "sizeof (b2) =" << sizeof (b2) << endl;
cout << "sizeof (d) =" << sizeof (d) << endl;
getchar ();
}
Resolution:
The compiler is empty class placement 1 byte char, so the class object to configure an address in memory.
b1 virtual inheritance in b, the compiler for the placement of a 4-byte virtual base class table pointer (32 machines), then b1 is already empty, the compiler no longer placed their 1-byte char (optimization).
b2 empathy.
d containing from two virtual base table pointer b1 and b2 two parent class. Size is 8 bytes. |
|
|
|