C++ virtual inheritance

http://www.cnblogs.com/biyeymyhjob/archive/2012/11/12/2766277.html

concept:

C++ uses virtual inheritance (Virtual Inheritance) to solve the problem of data inconsistency caused by different copies of data members of the same name inherited from different ways in memory, and sets the common base class as a virtual base class. At this time, data members with the same name inherited from different paths have only one copy in memory, and there is only one mapping for the same function name.

It solves the problem of ambiguity, saves memory, and avoids the problem of data inconsistency.
 

二义性:
#include <iostream>

using namespace std;  

int gFlag = 0;  

class Base  
{    
public:      
    Base(){cout << "Base called : " << gFlag++ << endl;}      
    void print(){cout << "Base print" <<endl;}  
    
};

class Mid1 : virtual public Base  
{    
public:      
    Mid1(){cout << "Mid1 called" << endl;}  
    void print(){cout << "1  print" <<endl;}     
private:      
};
class Mid2 : virtual public Base  
{    
public:      
    Mid2(){cout << "Mid2 called" << endl;}  
    void print(){cout << "2  print" <<endl;}
};
class Child:public Mid1, public Mid2  
{    
public:      
    Child(){cout << "Child called" << endl;}       
};

int main()  
{    
    Child d;
    //You can't use  
    d.print();
    // can also be used like this  
    
// d.Mid1::print();
    
// d.Mid2::print();    
    system("pause");  
    return 0;  
    
}


output:
--------------------Configuration: t1 - Win32 Debug--------------------
Compiling...
t1.cpp
Q:\VC\MyProjects\t1.cpp(38) : error C2385: 'Child::print' is ambiguous
Q:\VC\MyProjects\t1.cpp(38) : warning C4385: could be the 'print' in base 'Mid1' of class 'Child'
Q:\VC\MyProjects\t1.cpp(38) : warning C4385: or the 'print' in base 'Mid2' of class 'Child'
执行 cl.exe 时出错.

t1.exe - 1 error(s), 0 warning(s)


The definitions of virtual inheritance and virtual base classes are very simple, and it is also very easy to judge whether an inheritance is virtual inheritance. Although the definitions of these two concepts are very simple and clear, in the C++ language virtual inheritance is used as a A relatively rare but absolutely necessary component exists, and its behavior and model show huge differences from the general inheritance system (including differences in access performance), and now we will thoroughly The virtual inheritance and virtual base classes are studied from the aspects of language, model, performance and application.
    First, the definitions of virtual inheritance and virtual base classes are given.

    Virtual inheritance: the inheritance relationship of the virtual keyword is included in the inheritance definition;
    virtual base class: the base class inherited from virtual in the virtual inheritance system, it should be noted that:
            class CSubClass : public virtual CBase {}; CBase calls it the virtual base class of CSubClass, not to say that CBase is a virtual base class, because CBase may not be the base class in the virtual inheritance system.

With the above definition, we can start to study the essence of virtual inheritance and virtual base classes. The following is a comprehensive description of five aspects: syntax, semantics, model, performance and application.

1. Grammar
       The grammar is determined by the definition of the language itself. Generally speaking, it is very simple, as follows:
           class CSubClass : public virtual CBaseClass {};

       Among them, three different inheritance keywords, public, protected and private, can be used for modification. Just make sure to include virtual. In this way, a virtual inheritance system is formed, and CBaseClass becomes the virtual base class of CSubClass.

        In fact, it is not that simple. What will happen if there is further inheritance of the virtual inheritance system?

        As follows:

copy code
/*
  * base class with data members
 */
class CBaseClass1
{
public:
        CBaseClass1( size_t i ) : m_val( i ) {}
private:
         size_t m_val;
};

/*
 * Virtual inheritance system
 */
class CSubClassV1 : public virtual CBaseClass1
{
public:
          CSubClassV1( size_t i ) : CBaseClass1( i ) {}
};           

class CSubClassV2 : public virtual CBaseClass1
{
public:
          CSubClassV2( size_t i ) : CBaseClass1( i ) {}
};  
        
class CDiamondClass1 : public CSubClassV1, public CSubClassV2
{
public:
           CDiamondClass1( size_t i ) : CBaseClass1( i ), CSubClassV1( i ), CSubClassV2( i ) {}
};   
        
class CDiamondSubClass1 : public CDiamondClass1
{
public:
           CDiamondSubClass1( size_t i ) : CBaseClass1( i ), CDiamondClass1( i ) {}
};
copy code


Note the contents of the constructor initialization lists of the CDiamondClass1 and CDiamondSubClass1 classes in the code above. It can be found that the initialization work of the virtual base class CBaseClass1 is included. If there is no such initialization statement, it will cause a compile-time error. Why is this? In general, isn't it ok to include initialization in CSubClassV1 and CSubClassV2? To explain this problem, you must understand the semantic characteristics of virtual inheritance, so see the explanation of the semantics section below.

 

2. Semantics
       What are virtual inheritance and virtual base classes semantically? The above is just from how to write legal virtual inheritance class definition in C++ language. First of all, let's understand the public meaning of the virtual keyword in C++. There are only two places in the C++ language where the virtual keyword can be used. One is the class member virtual function and the virtual inheritance discussed here. Don't look at these two application situations as if they have nothing to do with each other. In fact, they have the common meaning represented by the word virtual in the background semantics, so the same keywords are used in these two situations. So what is the meaning of the word virtual?
       Virtual is defined in the "American Heritage Dictionary [Double Explanation]" as follows:
           adj. (adjective)
           1. Existing or resulting in essence or effect though not in actual fact, form, or name:
              in essence, in fact: 2. Existing in the
           mind, especially as a product of the imagination. Used in literary criticism of text.
              Virtual, inner: Existing in the mind, especially the product of intention. used in literary criticism.

We adopt the first definition, that is to say, the thing or phenomenon modified by virtual exists in essence, but it has no intuitive form, and cannot be directly described or defined. It needs to be reflected by other indirect methods or means. its actual effect.

Then in C++, the meaning of this word is adopted, which cannot be directly called or reflected in the language model, but there is indeed something that can be called or reflected in an indirect way. For example, virtual functions must be activated (called) through an indirect runtime (rather than compile-time) mechanism, and virtual inheritance is also a system that can only be accessed at runtime . exists, but indirectly. The key lies in the three characteristics of existence, indirection and sharing.

For virtual functions, these three characteristics are well understood. Indirectness indicates that he must complete the function addressing according to the actual object at runtime. The shared representation in the base class will share the virtual function that is overloaded by the subclass. The function actually points to the same function entry.

For virtual inheritance, how to understand these three characteristics? Existence means that the virtual inheritance system and the virtual base class do exist . Indirectness means that accessing members of the virtual base class must also be done through some indirect mechanism (described in the following model), and the shared appearance is in the virtual base class. Classes are shared in virtual inheritance without multiple copies . That can now explain the question left in the syntax section, "Why once a virtual base class appears, it must contain the initialization statement of the virtual base class in none of the inherited classes". From the above analysis, we can know that the virtual base class is shared, that is, no matter how many times it is inherited in the inheritance system, only one sub-object of the virtual base class will appear in the object memory model (this is completely different from multiple inheritance. ), so that since it is shared, each subclass will not be exclusive, but there must always be a class to complete the initialization process of the base class (because all objects must be initialized, even by default) , and at the same time, it is not possible to repeat the initialization, so who should be responsible for completing the initialization? The C++ standard (and naturally) chooses to write initialization statements in every inherited subclass (because every inherited subclass may be used to define objects), and to actually perform the initialization process in the lowest-level inherited subclass. Therefore, the initialization statement must be written in each inherited class above, but when the object is created, the initialization statement will only be actually executed in the class constructor used to create the object, and other initialization statements will be suppressed and not called.

 

3. Model

       In order to achieve the three semantic meanings mentioned above, it is natural to consider the implementation model of objects (that is, the memory model). In C++, an object is actually a semantic representation of a continuous address space. Let's analyze the memory model under virtual inheritance.

       3.1. Existence

           That is to say, the complete sub-object of the virtual base class must be included in the object memory, so that the identification of the object can be completed by address. Then, as for the location (head, middle, tail) where the sub-object of the virtual base class will be stored in the object, it is selected by each compiler, and there is no difference. (In VC8, no matter where the virtual base class is declared, the sub-objects of the virtual base class will be placed at the end of the object memory)

       3.2. Indirect

           Indirectness indicates that some kind of pointer (offset or table) must be included in the direct virtual base subclass to complete the indirect means of accessing the virtual base subobject (or member) through the subclass (because the virtual base subobject is shared, there is no definite relationship), as to which method to use is chosen by the compiler. (In VC8, a virtual base class pointer vbc is placed in the subclass, which points to a slot in the virtual function table, and the negative value of the offset of the virtual base class subobject is stored in the slot, which is actually a When calculating the first address of the virtual base class sub-object, the value of int type represented by complement code needs to be added to the absolute value of the offset. This is mainly to meet the requirement that only virtual function addresses can be stored in the virtual table. The difference is because the address is the value of the unsigned int type represented by the original code)

      3.3. Sharing

           Sharing indicates that only one sub-object of the virtual base class can be included in the object's memory space, and the shared reference relationship is completed through some indirect mechanism. After the introduction of the complete content, the test code will be attached to reflect these content.

4. Performance

       Due to the two characteristics of indirection and sharing, it is determined that the objects under the virtual inheritance system must be greatly different from the general situation in time and space when they are accessed.

       4.1. Time

       When accessing members (including data members and function members) in a virtual base class object through an inherited class object, it must be done through some indirect reference, which will increase the reference addressing time (just like virtual functions). Adjust the this pointer to point to the virtual base class object, but this adjustment is done at runtime. (In VC8, by opening the assembly output, you can view the content in the *.cod file. When accessing members of the virtual base class object, three mov indirect addressing statements will be formed, and when accessing the general inheritance class object, there is only one mov constant directly. addressing statement)

       4.2. Space

           Due to sharing, multiple copies of virtual base class sub-objects are not saved in the object memory , which saves space compared to multiple inheritance.

5. Application

           After talking about so many language features and content, under what circumstances should virtual inheritance be used, and how should it be used in general? In fact, it is difficult to answer this question. Generally speaking, if you are sure that multiple inheritance is unnecessary, and you must share the base class sub-object, you can consider adopting the virtual inheritance relationship (this is the case in the C++ standard ios system). Since each inherited class must contain an initialization statement and only call it in the bottom subclass, it may make the state of the virtual base class subobject obtained by some upper subclasses not what they expect (because their own The initialization statement is suppressed), so it is generally recommended not to include any data members (not stateful) in the virtual base class, and only provide it as an interface class.

 Code example:

copy code
#include <iostream>
#include <ostream>
#include <cstring>

using namespace std;

class CBase
{
public:
    virtual void fun ()
    {
        cout << "CBase:fun" << endl;
    }
private:
    char a[3];
};

class Derived1:public virtual CBase
{
public:
    void fun()
    {
        cout << "Derived1:fun" << endl;
    }
};

class Derived2:public virtual CBase
{
public:
    void fun()
    {
        cout << "Derived2:fun" << endl;
    }
};

class Derived: public  virtual Derived1, public  virtual Derived2 // Compare with the same effect with or without virtual 
{
 public :
     void fun()
    {
        cout << "Derived:fun" << endl;
    }
};

class Derived_:public  Derived1, public  Derived2
{
public:
    void fun()
    {
        cout << "Derived_:fun" << endl;
    }
};


intmain ()
{
    CBase b;
    Derived1 d1;
    Derived2 d2;
    Derived d;
    Derived_ d_;

    cout << "CBase " << sizeof(b) << endl;
    cout << "Derived1 " << sizeof(d1) << endl;
    cout << "Derived2 " << sizeof(d2) << endl;
    cout << "Derived " << sizeof(d) << endl;

    cout << "Derived_ " << sizeof(d_) << endl;

    return 0;
}
copy code


Execution effect:




Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325936158&siteId=291194637