Postscript
Based on the previous discussion, we can see that as long as virtual inheritance is involved, the code generated when accessing the member variables of the parent class is quite inefficient, the address of the member variable needs to be located through many indirect calculations. During pointer type conversion, dynamic transformation, and virtual function calling, you also need to generate a lot of additional code to adjust this pointer. Like the obj. Foo () and obj. f170 () of the c170 object in the previous article, the this pointer passed to the two functions is actually different.
We have encountered many other strange behaviors, such as the four-byte pointer pointing to the address and the semantics of the Four-byte 0 values in the c150 and c170 objects, when calling the foo function for c150 and c170, The this Pointer Points to not the starting position of the Child class, but the starting position of the grandfather class, and so on. It is not of great significance to thoroughly explore these issues. The implementation of virtual inheritance is the behavior of the compiler vendor. For different considerations, the implementation methods are quite different.
Traditional C programmers may think that C ++ is less efficient. In fact, the low efficiency is lower in the polymorphism part, because it is necessary to determine the function address through the virtual table at runtime. But for design, polymorphism is a very powerful weapon. Polymorphism is also one of the core technologies of object-oriented design. Although there is a loss in execution efficiency, for large-scale program design, for the ing of problem domains to models, the use of Object-Oriented Design Technology with polymorphism as the core can improve the efficiency of design, implementation and maintenance. For most applications, the overall loss is greater than the loss.
However, for virtual inheritance, I personally feel that it is only a mechanism designed to solve the problem of diamond inheritance and more complex inheritance, and there is no perfect solution, which not only greatly reduces the efficiency, it brings huge complexity, making the inheritance structure obscure. We recommend that you do not use it if you know all the consequences. In particular, do not declare non-static member variables in the base class inherited by virtual objects.
C ++ supports a variety of programming models, including process-oriented, data abstraction and encapsulation, object-oriented, and now a generic programming based on Template technology. Take an excellent open-source C ++ programming environment ACE (called the programming environment because it provides multi-level support from the class library to the Framework) as an example, let's take a look at the measurement during design and the use of various programming models. Ace is divided into several layers: OS adaptation layer, wrapper facade layer, framework layer, and service component layer. The OS adaptation layer and wrapper facade layer mainly use data abstraction and encapsulation, and do not use polymorphism and virtual mechanism. Because the key to the two layers is efficiency. In addition, the member methods of the classes in these two layers should be inline as much as possible. In fact, the efficiency of C ++ is similar to that of C without using polymorphism and virtual mechanisms, but object encapsulation leads to a large number of trivial methods (such as access encapsulation of member variables, that is, set and get methods), and the cost of method calling is also very high (you need to save and restore the context of the current execution environment, parameters pass and return values may generate many temporary variables and objects ). Therefore, the two layers reduce the overhead of function calling through inline and improve the execution efficiency. At the framework layer, a large number of design patterns are used, and many polymorphism mechanisms and generic technologies are used. The main focus of this layer is the clarity of the structure and the semantics of the implementation design. At this time, the loss of the polymorphism mechanism in execution efficiency is negligible.
Finally, I used Lippman to end this series of articles describing virtual member functions in his classic book "inside the C ++ object model. "Although I have a folder full of examples worked out and more than one algorithm for determining the proper offsets and adjustments, the material is simply too esoteric to warrant discussion in this text. my recommendation is not to declare nonstatic data members within a virtual base class. doing that goes a long way in taming the complexity. there are many feasible algorithms to determine the offset address and adjust the this pointer during virtual inheritance, and most of them are very strange (we have already seen this) :)). At the same time, it is recommended that you do not declare non-static member variables in the base class inherited by virtual resources. This is purely an annoyance.
In addition, I would like to thank Zhang shuisong and Zhang Jianye, both of whom, for some useful discussions they have written in these articles. In the end, they reminded me not to go further, so as not to get lost.
(Full text)
Brief description of classes defined in the Code
C000: Empty class.
C010: top-level class with common member functions and/or variables.
C011: top-level class with common member functions and/or variables.
C012: top-level class with static member functions and variables.
C013: The member variables inherited from c010 and the classes that overwrite common member functions of the parent class.
C014: An empty class inherited from c011.
C015: An empty class inherited from c010 and c011.
C016: inherits classes with member functions and/or variables from c015.
C020: A class inherited from the c010 Virtual Machine with its own member variables.
C021: A class inherited from the c010 Virtual Machine with its own member variables.
C030: Class inherited from c020, c021.
C040: top-level class with virtual functions.
C041: top-level class with virtual functions and variables.
C042: top-level class with virtual functions and variables.
C043: top-level class with two virtual functions.
C050: An empty class inherited from c041.
C051: An empty class inherited from c041 and c042.
C071: class that inherits from c043 and overrides the first virtual function of the parent class.
C082: Inherit from c041 and c042 the class with the virtual function defined by yourself and override the parent class.
C100: class with member variables inherited from c041 virtual.
C101: class with member variables inherited from c041 virtual.
C110: Class inherited from C100 and c101 with member variables.
C140: a class that inherits from the c041 Virtual Machine and overrides the virtual function of the parent class.
C141: the class that inherits and overrides the virtual function of the parent class from the c041 virtual.
C150: Class inherited from c140 and c141.
C160: inherit a class with a custom virtual function and override the virtual function of the parent class from c041 virtual.
C161: the class that inherits virtual functions with custom virtual functions and overrides the parent class from the c041 virtual class.
C170: A class inherited from C160 and c161.
Source code
The attachment is the package source code and the vc7.1 project file. After unzipping, you can schedule the operation on your own.
If you do not download the attachment, copy all the code to a CPP file. Note that you must use vc7.1 for compilation (I have verified this code only under vc7.1) and set the structure member alignment option when the code is generated to 1 byte. The default value is 8 bytes. Otherwise, a pointer exception occurs during running. See the description in the first section.
(Download the attachment here at the bottom of the page .)
# Include
# Include
Using namespace STD;
Struct c000
{
};
Struct c010
{
C010 (): C _ (0x01 ){}
Void Foo () {C _ = 0x02 ;}
Char C _;
};
Struct c011
{
C011 (): C1 _ (0x02), C2 _ (0x03 ){}
Char C1 _;
Char C2 _;
};
Struct c012
{
Static int sfoo () {return 1 ;}
Int Foo () {return 1 ;}
Char C _;
Static int I _;
};
Int c012: I _ = 1;
Struct c013: Public c010
{
C013 (): C1 _ (0x01 ){}
Void Foo () {C1 _ = 0x02 ;}
Char C1 _;
};
Struct c014: Private c011
{
};
Struct c015: Public c010, private c011
{
};
Struct c016: c015
{
C016 (): I _ (1 ){}
Int I _;
};
Struct c020: Public Virtual c010
{
C020 (): C _ (0x02 ){}
Char C _;
};
Struct c021: Public Virtual c010
{
C021 (): C _ (0x03 ){}
Char C _;
};
Struct c030: Public c020, public c021
{
C030 (): C _ (0x04 ){}
Char C _;
};
Struct c040
{
Virtual void Foo (){}
};
Struct c041
{
C041 (): C _ (0x01 ){}
Virtual void Foo () {C _ = 0x02 ;}
Char C _;
};
Struct c042
{
C042 (): C _ (0x02 ){}
Virtual void foo2 (){}
Char C _;
};
Struct c043
{
Virtual void foo1 (){}
Virtual void foo2 (){}
};
Struct c050: c040
{
};
Struct c051: Public c041, public c042
{
};
Struct c071: c043
{
Virtual void foo1 (){}
};
Struct c082: Public c041, public c042
{
C082 (): C _ (0x03 ){}
Virtual void Foo (){}
Virtual void foo2 (){}
Virtual void foo3 (){}
Char C _;
};
Struct C100: Public Virtual c041
{
C100 (): C _ (0x02 ){}
Char C _;
};
Struct c101: Public Virtual c041
{
C101 (): C _ (0x03 ){}
Char C _;
};
Struct c110: Public C100, public c101
{
C110 (): C _ (0x04 ){}
Char C _;
};
Struct c140: Public Virtual c041
{
C140 (): C _ (0x02 ){}
Virtual void Foo () {C _ = 0x11 ;}
Char C _;
};
Struct c141: Public Virtual c041
{
C141 (): C _ (0x03 ){}
Virtual void Foo () {C _ = 0x12 ;}
Char C _;
};
Struct c150: Public c140, public c141
{
C150 (): C _ (0x04 ){}
Virtual void Foo () {C _ = 0x21 ;}
Char C _;
};
Struct C160: Public Virtual c041
{
C160 (): C _ (0x02 ){}
Virtual void Foo () {C _ = 0x12 ;}
Virtual void f160 () {C _ = 0x12 ;}
Char C _;
};
Struct c161: Public Virtual c041
{
C161 (): C _ (0x03 ){}
Virtual void Foo () {C _ = 0x13 ;}
Virtual void f161 () {C _ = 0x13 ;}
Char C _;
};
Struct c170: Public C160, public c161
{
C170 (): C _ (0x04 ){}
Virtual void Foo () {C _ = 0x14 ;}
Virtual void f170 () {C _ = 0x14 ;}
Char C _;
};
# Define print_size (class_name )/
Cout <"the size of" <# class_name <"is" <sizeof (class_name) <Endl;
# Define print_detail (class_name, object_name )/
Cout <"the detail of" <# class_name <"is ";/
For (INT I = 0; I <sizeof (object_name); ++ I ){/
Cout. Fill ('0 ');/
Cout <SETW (2) Cout <'';}/
Cout <SETW (0) <dec <Endl;
# Define print_size_detail (class_name )/
Print_size (class_name )/
Class_name _ # class_name ;/
Print_detail (class_name, _ ## class_name)
# Define LF/
Cout <Endl;
Template
Void *
Get_obj_addr (const T & OBJ)
{
Return (void *) & OBJ;
}
Void *
Get_vp_addr (void * Start, int offset)
{
Return (void *) (char *) Start + offset );
}
Void *
Get_vt_addr (void * vp)
{
Return (void *) * (void **) vp );
}
Void *
Get_vti_val (void * Vt, int idx)
{
Return (void *) * (void **) (int *) vt + idx ));
}
# Define print_vtable_item (OBJ, vpoffset, index )/
{/
Cout. Fill ('');/
Cout <SETW (8) <left ;/
Cout <# OBJ <":";/
Void * obj_addr = get_obj_addr (OBJ );/
Cout Void * vp_addr = get_vp_addr (obj_addr, vpoffset );/
Cout <"vpadr:" <vp_addr ;/
Void * vt_addr = get_vt_addr (vp_addr );/
Cout <"vtadr:" <vt_addr ;/
Void * vti_val = get_vti_val (vt_addr, index );/
Cout <"vtival (" <index <"):" <vti_val <dec <right <Endl ;/
}
# Define print_obj_adr (OBJ )/
Cout <# OBJ <"'s address is:"
# Define print_pt (PT )/
Cout <# Pt <"'s value is:"
Struct _ declspec (novtable) c180
// Struct c180
{
C180 (){
Foo ();
This-> Foo ();
}
Virtual Foo (){
Cout <"<c180.foo this:" <This <"vtadr:" <* (void **) This <Endl;
}
};
Struct c190: Public c180
{
C190 (){}
Virtual Foo (){
Cout <"<c190.foo this:" <This <"vtadr:" <* (void **) This <Endl;
}
};
Int
Main (INT argc, char * argv [])
{
Print_size_detail (c000)
Lf
Print_size_detail (c010)
Print_size_detail (c011)
Lf
Print_size_detail (c012)
Lf
Print_size_detail (c013)
Lf
Print_size_detail (c014)
Print_size_detail (c015)
Lf
Print_size_detail (c016)
Lf
Print_size_detail (c040)
Lf
Print_size_detail (c050)
Lf
Print_size_detail (c041)
{
Lf
C040 obj1, obj2;
Print_vtable_item (obj1, 0, 0)
Print_vtable_item (obj2, 0, 0)
Lf
C040 c040;
C050 c050;
Print_vtable_item (c040, 0, 0)
Print_vtable_item (c050, 0, 0)
}
{
Lf
C043 c043;
C071 c071;
Print_size_detail (c071)
Print_vtable_item (c043, 0, 0)
Print_vtable_item (c071, 0, 0)
Print_vtable_item (c043, 0, 1)
Print_vtable_item (c071, 0, 1)
}
{
Lf
Print_size_detail (c041)
Print_size_detail (c042)
Print_size_detail (c051)
C041 c041;
C042 c042;
C051 c051;
Print_vtable_item (c041, 0, 0)
Print_vtable_item (c042, 0, 0)
Print_vtable_item (c051, 0, 0)
Print_vtable_item (c051, 5, 0)
}
{
Lf
Print_size_detail (c082)
C041 c041;
C042 c042;
C082 c082;
Print_vtable_item (c041, 0, 0)
Print_vtable_item (c042, 0, 0)
Print_vtable_item (c082, 0, 0)
Print_vtable_item (c082, 5, 0)
Print_vtable_item (c082, 0, 1)
Print_vtable_item (c082, 5, 1)
C082.foo3 ();
C082.c041: C _ = 0x05;
Print_vtable_item (c041, 0, 0)
Print_detail (c041, (c041) c082 ))
Print_vtable_item (c041) c082), 0, 0)
Lf
Print_vtable_item (c082, 5, 0)
C042 * PT = dynamic_cast (& C082 );
Print_vtable_item (* PT, 0, 0)
}
// PK change object's type by force
{
Lf
C013 OBJ;
OBJ. Foo ();
(C010) OBJ). Foo ();
OBJ. c010: Foo ();
}
{
Lf
C010 OBJ;
Print_obj_adr (OBJ)
OBJ. Foo ();
C012: sfoo ();
C010 * PT = & OBJ;
Pt-> Foo ();
}
{
Lf
Cout <"< > "<Endl;
C041 OBJ;
Print_detail (c041, OBJ)
Print_vtable_item (OBJ, 0, 0)
OBJ. Foo ();
C041 * PT = & OBJ;
Pt-> Foo ();
}
{
Lf
C051 OBJ;
C041 * pt1 = dynamic_cast (& OBJ );
C042 * pt2 = dynamic_cast (& OBJ );
Pt1-> Foo ();
Pt2-> foo2 ();
}
{
Lf
C190 OBJ;
OBJ. Foo ();
Print_detail (c190, OBJ );
Print_obj_adr (OBJ );
}
{
Lf
Print_size_detail (c020)
Print_size_detail (c030)
Print_size_detail (C100)
Print_size_detail (c110)
C020 c020;
C0109c010: C _ = 0x04;
Print_obj_adr (c020 );
C010 * PT = & c020;
Print_pt (PT );
Pt-> C _ = 0x03;
C010 * pt1 = 0;
C010 * pt2 = pt1;
C110 c110;
C110.c _ = 0x51;
C110.c100: C _ = 0x52;
C110.c101: C _ = 0x52;
C110.c041: C _ = 0x53;
C110.foo ();
}
{
Lf
Print_size_detail (c041)
Print_size_detail (c140)
Print_size_detail (c141)
Print_size_detail (c150)
C150 OBJ;
Print_obj_adr (OBJ)
OBJ. Foo ();
C150 * PT = & OBJ;
Pt-> Foo ();
C141 * pt1 = dynamic_cast (PT );
Pt1-> Foo ();
}
{
Lf
Print_size_detail (c041)
Print_size_detail (C160)
Print_size_detail (c161)
Print_size_detail (c170)
C170 OBJ;
Print_obj_adr (OBJ );
OBJ. Foo ();
OBJ. f170 ();
C170 * PT = & OBJ;
Pt-> f170 ();
Pt-> c041: C _ = 0x33;
C161 * pt2 = dynamic_cast (PT );
Pt2-> C _ = 0x34;
Pt2-> Foo ();
Pt2-> f161 ();
}
Return 0;
}