C + + language learning (13)--c++ object Model Analysis I. C + + object Model analysis 1. Memory layout of class object model
Class is a special kind of struct,class that follows the same principle of memory alignment as a struct, where member functions in class are stored separately from member variables, and each object has independent member variables, and all objects share member functions in the class.
At run time, the class object is degraded to the form of a struct:
A, all member variables are sequentially arranged in memory
B. There may be memory gaps between member variables due to memory alignment
C. Member variables can be accessed via memory address
D. Access keys expire at run time
#include <iostream>using namespace std;class A{ int i; int j; char c; double d;public: void print() { cout << "i = " << i << ", " << "j = " << j << ", " << "c = " << c << ", " << "d = " << d << endl; }};struct B{ int i; int j; char c; double d;};int main(int argc, char *argv[]){ A a; //64 bit machine cout << "sizeof(A) = " << sizeof(A) << endl; // 24 cout << "sizeof(a) = " << sizeof(a) << endl; // 24 cout << "sizeof(B) = " << sizeof(B) << endl; // 24 a.print(); B* p = reinterpret_cast<B*>(&a); p->i = 1; p->j = 2; p->c = ‘c‘; p->d = 3.14; a.print(); return 0;}
In the code above, the Class A object is in the same arrangement as the struct B object in memory.
2. Derived class class object model
Subclasses are obtained by overlapping child class members of the parent class member.
#include <iostream>using namespace Std;class parent{protected:int m_i; int m_j;}; Class Child:public Parent{public:child (int i, int J, double d) {m_i = i; M_j = j; M_d = D; } void Print () {cout << "m_i =" << m_i << Endl; cout << "M_j =" << m_j << Endl; cout << "m_d =" << m_d << Endl; }private:double m_d;}; struct test{int i; Int J; Double D;}; int main (int argc, char *argv[]) {cout << sizeof (Parent) << endl;//8 cout << sizeof (child) << ; ENDL;//16 Child Child (1,2,3.14); Child.print (); test* test = reinterpret_cast<test*> (&child); cout << "i =" << test->i << Endl; cout << "j =" << test->j << Endl; cout << "d =" << test->d << Endl; Test->i = 100; Test->j = 200; Test->d = 3.1415; Child.print (); return 0;}
II. Implementation Mechanism of C + + polymorphism 1, C + + polymorphic implementation Introduction
When a virtual function is declared in a class, the C + + compiler generates a virtual function table in the class. A virtual function table is a data structure used to store virtual member function addresses. Virtual function tables are automatically generated and maintained by the compiler, and virtual member functions are placed in the virtual function table by the compiler. When a virtual function exists, each object has a pointer to the virtual function table of the class.
Because the virtual function table is queried by the object when it calls the virtual function, the call efficiency of the virtual function is lower than the normal member function.
When creating a class object, if a virtual function exists in the class, the compiler adds a pointer to the virtual function table in the class object. The virtual function table in the parent class object stores the virtual function of the parent class, and the virtual function table in the subclass object stores the virtual function of the child class object. The virtual function table pointer stores the first 4 (8) bytes of the start of the class object storage space.
2. virtual function table
If a class contains a virtual function, its class contains a virtual function table.
If a base class contains virtual functions, the base class contains a virtual function table, and its derived class also contains a table of its own virtual functions.
A virtual function table is an array of function pointers whose array elements are function pointers to virtual functions, and each element corresponds to a function pointer of a virtual function. Calls to non-virtual member functions do not need to pass through a virtual function table, so the elements of a virtual function table do not include function pointers for non-virtual member functions.
The assignment of virtual function pointers in virtual function tables occurs at compiler stage, that is, the virtual function table is generated during code compilation.
#include <iostream>using namespace Std;class parent{public:parent (int i, int j) {m_i = i; M_j = j; virtual void print () {cout << "Parent::" << __func__<< Endl; cout << "m_i =" << m_i << Endl; cout << "M_j =" << m_j << Endl; } virtual double sum () {cout << "Parent::" << __func__<< Endl; DOUBLE ret = m_i + m_j; cout <<ret << Endl; return ret; } virtual void display () {cout << "Parent::d isplay ()" << Endl; }protected:int m_i; int m_j;}; Class Child:public Parent{public:child (int i, int J, double D):P arent (i, j) {m_d = D; } virtual void print () {cout << "Child::" << __func__<< Endl; cout << "m_i =" << m_i << Endl; cout << "M_j =" << m_j << Endl; cout << "m_d ="<< m_d << Endl; } virtual double sum () {cout << "Child::" << __func__<< Endl; DOUBLE ret = m_i + M_j + m_d; cout << ret << Endl; return ret; }private:void display () {cout << "Child::d isplay ()" << Endl; }private:double m_d;}; struct test{void* vptr; int i; Int J; Double D;}; int main (int argc, char *argv[]) {cout << sizeof (Parent) << endl;//12 cout << sizeof (child) <&L T ENDL;//24 Child Child (1,2,3.14); test* test = reinterpret_cast<test*> (&child); cout << "virtual Function Table Pointer:" << Endl; cout << "vptr =" << test->vptr << Endl; The virtual function table pointer is located in the first 4 bytes of the class object cout << "child object address:" << &child << Endl; cout << "Member Variables Address:" << Endl; cout << "&vptr =" << &test->vptr << Endl; cout ≪< "&i =" << &test->i << Endl; cout << "&j =" << &test->j << Endl; cout << "&d =" << &test->d << Endl; function pointer access to the virtual function of the class cout << "virtual function Table:" << Endl; cout << "Virtual print Function Address:" << Endl; cout << (long*) (* (LONG *) (* ((long *) &child) + 0)) <<endl; cout << "Virtual sum Function Address:" << Endl; cout << (long*) (* (LONG *) (* ((long *) &child) + 1)) <<endl; cout << "Virtual display Function Address:" << Endl; cout << (long*) (* (LONG *) (* ((long *) &child) + 2)) <<endl; typedef void (*pprint) (); Pprint print = (pprint) (* (LONG *) (* ((long *) &child) + 0)); Print (); typedef double (*psum) (void); PSum sum = (pSum) (* (LONG *) (* ((long *) &child) + 1)); SUM (); typedef void (*pdisplay) (void); Pdisplay display = (Pdisplay) (* (LONG *) (* ((long *) &child)) + 2); Display (); return 0;}
In the code above, the virtual function table of the class can be accessed through the virtual function table pointer of the class object, the virtual function table order stores the function address of the virtual function of the class, and the virtual function of the class can be called through the function pointer, including the virtual function declared as private. However, because the virtual function of a class is accessed using a function pointer, the value of the member variable of the class object to which it is accessed is a garbage value, as it executes during the execution of the object to which the this pointer points is indeterminate.
3. virtual function Table pointer
A virtual function table belongs to a class, not to a specific class object, and a class requires only a single virtual function table. All objects of the same class use the unique virtual function table of the class. In order to specify a virtual function table for a class object, the class object contains a pointer to a virtual function table, pointing to the virtual function table of the class. To have a virtual function table pointer for each class object, the compiler adds a pointer to the *__vptr
virtual function table in the class. When a class object is created with a __vptr
pointer, and __vptr
the value of the pointer is automatically set to a virtual function table that points to the class.
class Parent{public: Parent(int i, int j) { m_i = i; m_j = j; } virtual void print() { cout << "Parent::" << __func__<< endl; cout << "m_i = "<< m_i << endl; cout << "m_j = "<< m_j << endl; } virtual double sum() { cout << "Parent::" << __func__<< endl; double ret = m_i + m_j; cout <<ret << endl; return ret; } virtual void display() { cout << "Parent::display()" << endl; } int add(int value) { return m_i + m_j + value; }protected: void func() { }protected: int m_i; int m_j;};
In the above code, the virtual function table of the class is as follows:
In the memory layout of a class parent object, the virtual function table pointer is located at the beginning of the class object storage space whose value 0x409004 is the first address of the virtual function table of the class parent, and the first array element in the virtual function table is the virtual function parent: The address of the:p rint, The second array element is the virtual function parent::sum, and the third array element is the virtual function parent::d Isplay, the non-virtual function is not in the virtual function table.
4. Memory layout of Class objects
For classes that contain virtual functions, the virtual function table pointer is at the beginning of the memory layout of the class object, then arranges the class to inherit the member variables from the parent class, and finally arranges the non-static member variables of the class itself.
#include <iostream>using namespace Std;class parent{public:parent (int i, int j) {m_i = i; M_j = j; virtual void print () {cout << "Parent::" << __func__<< Endl; cout << "m_i =" << m_i << Endl; cout << "M_j =" << m_j << Endl; } virtual double sum () {cout << "Parent::" << __func__<< Endl; DOUBLE ret = m_i + m_j; cout <<ret << Endl; return ret; } virtual void display () {cout << "Parent::d isplay ()" << Endl; } int Add (int value) {return m_i + M_j + value; }protected:void func () {}protected:int m_i; int m_j; static int m_count;}; int parent::m_count = 0;class childa:public parent{public:childa (int i, int J, double D):P arent (i, j) {m _d = D; } virtual void print () {cout << "Childa::" << __FUNC__<< Endl cout << "m_i =" << m_i << Endl; cout << "M_j =" << m_j << Endl; cout << "m_d =" << m_d << Endl; } virtual double sum () {cout << "Childa::" << __func__<< Endl; DOUBLE ret = m_i + M_j + m_d; cout << ret << Endl; return ret; }private:void display () {cout << "Childa::d isplay ()" << Endl; }private:double m_d;}; Class Childb:public Parent{public:childb (int i, int J, double D):P arent (i, j) {m_d = D; } virtual void print () {cout << "childb::" << __func__<< Endl; cout << "m_i =" << m_i << Endl; cout << "M_j =" << m_j << Endl; cout << "m_d =" << m_d << Endl; } virtual double sum () {cout << "childb::" << __func__<< Endl; DOUBLE ret = m_i + M_j + M_d; cout << ret << Endl; return ret; }private:void display () {cout << "childb::d isplay ()" << Endl; }private:double m_d;}; struct parenttest{void* vptr; int i; int J;}; struct childtest{void* vptr; int i; Int J; Double D;}; int main (int argc, char *argv[]) {cout << sizeof (Parent) << endl;//12 cout << sizeof (Childa) <& Lt ENDL;//24 cout << Endl; cout << "Parent ..." <<endl; Parent parent (from); parenttest* parenttest = reinterpret_cast<parenttest*> (&parent); cout << "Member Variable Value:" << Endl; The first address of the virtual function table cout << parenttest->vptr << endl;//Compile-time determination cout << parenttest->i << endl;//1 cout << parenttest->j << endl;//2 cout << "Member Variable Address:" << Endl; cout << &parenttest->vptr << Endl; cout << &parenttest->i << Endl; cout << &parenttest->j << Endl; cout << Endl; cout << "Child ..." << Endl; Childa Child (1,2,3.14); childtest* childtest = reinterpret_cast<childtest*> (&child); cout << "Member Variable Value:" << Endl; The first address of the virtual function table cout << childtest->vptr << endl;//Compile-time determination cout << childtest->i << endl;//1 cout << childtest->j << endl;//2 cout << childtest->d << endl;//3.14 cout << "M Ember Variable Address: "<< Endl; cout << &childtest->vptr << Endl; cout << &childtest->i << Endl; cout << &childtest->j << Endl; cout << &childtest->d << Endl; return 0;}
5, the implementation of dynamic binding
The parent, Childa, and childb three classes have virtual functions, and the C + + compiler compiles a virtual function table for each class, that is, the virtual function table of the parent class (parent VTBL), the virtual function table of the class Childa (Childa vtbl), The virtual Table of class Childb (Childb vtbl). The objects of the class parent, Childa, and childb all have a virtual function table pointer *vptr, which points to the virtual function table of the class to which it belongs.
The class parent includes three virtual functions, and the virtual function table of the parent class contains three pointers, pointing to the parent::p rint (), Parent::sum (), Parent::d Isplay () three virtual function functions.
The class Childa inherits from the class parent, so the class Childa can call the parent's function, but the class Childa overrides the parent::p rint (), Parent::sum (), Parent::d Isplay () three virtual functions, Thus, the three function pointers of the class Childa virtual function table point to Childa::p rint (), Childa::sum (), Childa::d isplay ().
Class Childb inherits from the class parent, so class CHILDB can call functions of the class parent, but because the class Childb overrides the parent::p the Rint (), parent::sum () function, the class CHILDB virtual function table has three function pointers, The first function pointer points to the parent::d isplay () virtual function, and the second third points to Childb::p rint (), childb::sum () virtual function.
ChildA childA;Parent* p = &childA;
When you define an object Childa for a Childa class, the Childa object contains a virtual function table pointer to the virtual function table of the Childa class.
When a pointer p that defines a parent class points to a Childa object, the P pointer can only point to the parent part of the Childa object, but because the virtual function table pointer is at the beginning of the object's storage space, the P pointer accesses the virtual function table pointer of the Childa object. Because the virtual function table pointer of the Childa object points to the virtual function table of the Childa class, the P pointer can access the virtual function table of the class Childa.
When you call the print function with a pointer, the program executes P->print () and finds that P is a pointer and that the function being called is a virtual function.
First, the virtual function table of the object Childa is accessed according to the virtual function sheet pointer p->vptr.
Then, in the virtual function table, look for the entry for the virtual function that was called. Because the virtual function table is generated at compile time, it is possible to navigate to the corresponding entry in the virtual function table based on the function being called. For P->print () calls, the first item of the Class Childa virtual function table is the entry corresponding to the print function pointer.
Finally, according to the function pointer found in the virtual function table, call the function Childa::p rint ().
Parent base;Parent* p = &base;p->print();
When the base object is created, the virtual function table pointer vptr of the base object is set to point to the virtual function table of the parent class, and p->vptr points to the parent virtual function table. Print the corresponding entry in the parent virtual function table points to the parent::p the Rint () function, so P->print () calls the parent::p the Rint () function.
The three steps of a virtual function call can be summed up with an expression (* (P->vptr) [n]) (p).
The classical problem of virtual function 1, the constructor cannot be a virtual function
The virtual function table pointers for class objects are initialized correctly after the constructor finishes executing. Therefore, the constructor cannot be a virtual function. A virtual function table pointer in a class object is initialized when the constructor is called. Therefore, the virtual function table pointer has not completed initialization before the constructor call, and the virtual constructor cannot be called.
before the constructor enters the function body, the virtual function table pointer is initialized, the virtual function table pointer is initialized to the virtual function table address of the current class, that is, when the base class calls the constructor, the virtual function table address of the base class is assigned to the virtual function table pointer, and if it is executed into the subclass constructor, The virtual function table address of the handle class is assigned to the virtual function table pointer. Therefore, when the derived class object is constructed, the virtual function table pointer points to the virtual function table address that is dynamically changing.
#include <iostream>using namespace Std;class parent{public:parent (int i, int j) {m_i = i; M_j = j; cout << "Parent (int i, int j):" << this << Endl; Virtual function table pointer int* vptr = (int*) * ((int*) this); cout << "vptr:" << vptr << Endl; virtual void print () {cout << "Parent::" << __func__<< Endl; cout << "m_i =" << m_i << Endl; cout << "M_j =" << m_j << Endl; } virtual ~parent () {cout << "~parent ():" << this << Endl; }protected:int m_i; int m_j;}; Class Child:public Parent{public:child (int i, int J, double D):P arent (i, j) {m_d = D; cout << "Child (int i, Int. J, double D):" << this << Endl; Virtual function table pointer int* vptr = (int*) * ((int*) this); cout << "vptr:" << vptr << Endl; } virtual void print () { cout << "Child::" << __func__<< Endl; cout << "m_i =" << m_i << Endl; cout << "M_j =" << m_j << Endl; cout << "m_d =" << m_d << Endl; } ~child () {cout << "~child ():" << this <<endl; }private:double m_d;}; int main (int argc, char *argv[]) {parent* p = new Child (1,2,3.14); P->print (); Delete p; return 0;}
2, can be virtual function in the destructor
A destructor can be a virtual function, and polymorphism can occur. In engineering practice, if there are virtual member functions in the base class, it is recommended to declare the destructor as a virtual function to ensure that the correct destructor call is triggered when the object is destroyed and that the resource is properly recycled.
#include <iostream>using namespace Std;class parent{public:parent (int i, int j) {m_i = i; M_j = j; cout << "Parent (int i, int j)" << Endl; virtual void print () {cout << "Parent::" << __func__<< Endl; cout << "m_i =" << m_i << Endl; cout << "M_j =" << m_j << Endl; } virtual ~parent () {cout << "~parent ()" << Endl; }protected:int m_i; int m_j;}; Class Child:public Parent{public:child (int i, int J, double D):P arent (i, j) {m_d = D; cout << "Child (int i, int J, double D)" << Endl; } virtual void print () {cout << "Child::" << __func__<< Endl; cout << "m_i =" << m_i << Endl; cout << "M_j =" << m_j << Endl; cout << "m_d =" << m_d << Endl; } ~child () {cout <<~child () "<<endl; }private:double m_d;}; int main (int argc, char *argv[]) {parent* p = new Child (1,2,3.14); P->print (); Delete p; return 0;}
3. No polymorphic behavior can occur within the constructor function
When a constructor for a base class is called, its virtual function table pointer points to the virtual function table of the base class, and when the constructor of the derived class is called, its virtual function table pointer points to the virtual function table of the derived class. Therefore, there is no polymorphic behavior within the constructor.
4. Polymorphic behavior cannot occur within the destructor
When a destructor for a derived class is called, its virtual function table pointer points to the virtual function table of the derived class, and when the destructor of the base class is called, its virtual function table pointer points to the virtual function table of the base class, and the virtual function table of the derived class has been destroyed.
C + + language learning (13)--c++ Object Model analysis