Where is the “virtual” keyword necessary in a complex multiple inheritance hierarchy?

后端 未结 7 1710
死守一世寂寞
死守一世寂寞 2021-01-30 19:00

I understand the basics of C++ virtual inheritance. However, I\'m confused about where exactly I need to use the virtual keyword with a complex class hierarchy. F

7条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2021-01-30 19:38

    I toyed a program together which could help you to study the intricacies of virtual bases. It prints the class hierarchy under I as a digraph suitable for graphiviz ( http://www.graphviz.org/ ). There's a counter for each instance which helps you to understand the construction order as well. Here's the programm:

    #include 
    int counter=0; 
    
    
    
    #define CONN2(N,X,Y)\
        int id; N() { id=counter++; }\
        void conn() \
        {\
            printf("%s_%d->%s_%d\n",#N,this->id,#X,((X*)this)->id); \
            printf("%s_%d->%s_%d\n",#N,this->id,#Y,((Y*)this)->id); \
            X::conn(); \
            Y::conn();\
        }
    #define CONN1(N,X)\
        int id; N() { id=counter++; }\
        void conn() \
        {\
            printf("%s_%d->%s_%d\n",#N,this->id,#X,((X*)this)->id); \
            X::conn(); \
        }
    
    struct A { int id; A() { id=counter++; } void conn() {} };
    struct B : A { CONN1(B,A) };
    struct C : A { CONN1(C,A)  };
    struct D : B { CONN1(D,B) };
    struct E : B,C { CONN2(E,B,C) };
    struct F : C { CONN1(F,C) };
    struct G : D,E { CONN2(G,D,E) };
    struct H : E,F { CONN2(H,E,F) };
    struct I : G,H { CONN2(I,G,H) };
    int main()
    {
        printf("digraph inh {\n");
        I i; 
        i.conn(); 
        printf("}\n");
    }
    

    If I run this (g++ base.cc ; ./a.out >h.dot ; dot -Tpng -o o.png h.dot ; display o.png), I get the typical non-virtual base tree:

    Adding enough virtualness...

    struct B : virtual A { CONN1(B,A) };
    struct C : virtual A { CONN1(C,A)  };
    struct D : virtual B { CONN1(D,B) };
    struct E : virtual B, virtual C { CONN2(E,B,C) };
    struct F : virtual C { CONN1(F,C) };
    struct G : D, virtual E { CONN2(G,D,E) };
    struct H : virtual E,F { CONN2(H,E,F) };
    struct I : G,H { CONN2(I,G,H) };
    

    ..results in the diamond shape (look at the numbers to learn the construction order!!)

    But if you make all bases virtual:

    struct A { int id; A() { id=counter++; } void conn() {} };
    struct B : virtual A { CONN1(B,A) };
    struct C : virtual A { CONN1(C,A)  };
    struct D : virtual B { CONN1(D,B) };
    struct E : virtual B, virtual C { CONN2(E,B,C) };
    struct F : virtual C { CONN1(F,C) };
    struct G : virtual D, virtual E { CONN2(G,D,E) };
    struct H : virtual E, virtual F { CONN2(H,E,F) };
    struct I : virtual G,virtual H { CONN2(I,G,H) };
    

    You get a diamond with a different initialization order:

    Have fun!

提交回复
热议问题