Where is the “virtual” keyword necessary in a comp

2019-03-08 09:14发布

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. For example, suppose I have the following classes:

            A
           / \
          B   C
         / \ / \
        D   E   F
         \ / \ /
          G   H
           \ /
            I

If I want to ensure that none of the classes appear more than once in any of the subclasses, which base classes need to be marked virtual? All of them? Or is it sufficient to use it only on those classes that derive directly from a class that may otherwise have multiple instances (i.e. B, C, D, E and F; and G and H (but only with the base class E, not with the base classes D and F))?

7条回答
聊天终结者
2楼-- · 2019-03-08 09:46

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 <stdio.h>
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: alt text http://i34.tinypic.com/2ns6pt4.png

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!!)

alt text http://i33.tinypic.com/xpa2l5.png

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:

alt text http://i33.tinypic.com/110dlj8.png

Have fun!

查看更多
登录 后发表回答