Virtual inheritance

Discussion in 'C++' started by Massimo, Apr 30, 2007.

  1. Massimo

    Massimo Guest

    Hi to all, I'm facing a problem in a particularly complex inheritance
    hierarchy, and I'd like to know what the standard says about it and if my
    compiler is correct in what it does.

    I have two consecutive layers of diamond-shaped inheritance; the first layer
    is declared as using virtual inheritance, while the second one is declared
    as *not* using it.

    This is what I'd like to have:

    Base Base
    / \ / \
    D1Base D2Base D1Base D2Base
    \ / \ /
    Middle Middle
    || ||
    D1Middle D2Middle
    \ /
    \ /
    \ /
    Last

    This is what the compiler actually does:

    Base
    / \
    D1Base D2Base
    \ /
    Middle
    / \
    D1Middle D2Middle
    \ /
    Last

    If I remove all virtual inheritance, everything goes just fine; if I use it
    in the first layer, there seems to be no way of disabling it afterwards.

    Does the standard saying anything about this, or is it
    implementation-dependant? My compiler is Visual Studio 2005.

    Code sample attached.


    Thanks


    Massimo
     
    Massimo, Apr 30, 2007
    #1
    1. Advertising

  2. Massimo

    jg Guest

    On Apr 29, 6:53 pm, "Massimo" <> wrote:
    > Hi to all, I'm facing a problem in a particularly complex inheritance
    > hierarchy, and I'd like to know what the standard says about it and if my
    > compiler is correct in what it does.
    >
    > I have two consecutive layers of diamond-shaped inheritance; the first layer
    > is declared as using virtual inheritance, while the second one is declared
    > as *not* using it.
    >
    > This is what I'd like to have:
    >
    > Base Base
    > / \ / \
    > D1Base D2Base D1Base D2Base
    > \ / \ /
    > Middle Middle
    > || ||
    > D1Middle D2Middle
    > \ /
    > \ /
    > \ /
    > Last
    >
    > This is what the compiler actually does:
    >
    > Base
    > / \
    > D1Base D2Base
    > \ /
    > Middle
    > / \
    > D1Middle D2Middle
    > \ /
    > Last


    I think the object of Last will have a single subobject of Base,
    not two. The other subobjects are not shared (i.e. there are
    two subobjects each for D1Base, D2Base, Middle).

    Base
    / / \ \
    / / \ \
    / / \ \
    D1Base D2Base D1Base D2Base
    \ / \ /
    Middle Middle
    | |/
    D1Middle D2Middle
    \ /
    \ /
    \ /
    Last

    JG
     
    jg, Apr 30, 2007
    #2
    1. Advertising

  3. Massimo

    Massimo Guest

    "jg" <> ha scritto nel messaggio
    news:...

    > I think the object of Last will have a single subobject of Base,
    > not two. The other subobjects are not shared (i.e. there are
    > two subobjects each for D1Base, D2Base, Middle).
    >
    > Base
    > / / \ \
    > / / \ \
    > / / \ \
    > D1Base D2Base D1Base D2Base
    > \ / \ /
    > Middle Middle
    > | |/
    > D1Middle D2Middle
    > \ /
    > \ /
    > \ /
    > Last


    I really don't know, but don't think so, since each and every one of these
    classes is (somewhat) a subclass of Base, and so inherits (or seems to
    inherit) the virtual inheritance setting.

    I'm actually quite sure there's only one Middle object, because the compiler
    doesn't complain about method names resolution issues... while it *does*, if
    inheritance is made non-virtual.

    Anyway, any clue on how to obtain the desidered behaviour (if this is at all
    possible)?


    Massimo
     
    Massimo, Apr 30, 2007
    #3
  4. Massimo

    Greg Herlihy Guest

    On Apr 29, 9:25 pm, "Massimo" <> wrote:
    > "jg" <> ha scritto nel messaggionews:...
    >
    > > I think the object of Last will have a single subobject of Base,
    > > not two. The other subobjects are not shared (i.e. there are
    > > two subobjects each for D1Base, D2Base, Middle).

    >
    > > Base
    > > / / \ \
    > > / / \ \
    > > / / \ \
    > > D1Base D2Base D1Base D2Base
    > > \ / \ /
    > > Middle Middle
    > > | |/
    > > D1Middle D2Middle
    > > \ /
    > > \ /
    > > \ /
    > > Last

    >
    > I really don't know, but don't think so, since each and every one of these
    > classes is (somewhat) a subclass of Base, and so inherits (or seems to
    > inherit) the virtual inheritance setting.
    >
    > I'm actually quite sure there's only one Middle object, because the compiler
    > doesn't complain about method names resolution issues... while it *does*, if
    > inheritance is made non-virtual.
    >
    > Anyway, any clue on how to obtain the desidered behaviour (if this is at all
    > possible)?


    The problem is that every class that declares a virtual base class
    shares that base class object with every other class that has declared
    the same virtual base class. Therefore it is not possible to declare
    D1Base and D2Base in such a way that the two pairs of D1Base and
    D2Base objects share the same Base object as one another - without
    also having them share the same Base object as every other pair of
    D1Base and D2Base objects in the hierarchy. In other words, it is
    possible to reduce the top of the current inheritance graph to a
    single Base object - or three or four Base class objects - but not two
    - as the graph is currently structured.

    One way to remedy this problem - and to have the two Base objects at
    the top of the hierarchy as desired - would be to insert a pair of
    "buffer" classes, Base1 and Base2, between the Base and the D1Base and
    D2Base objects. D1Base and D2Base would then each inherit from both
    Base1 and Base2 as virtual base classes. The rest of the current
    inheritance hierarchy would remain the same.

    To illustrate. With this change the top level of the inheritance graph
    would like this:

    Base Base
    | |
    | |
    Base1 Base2
    | | | |
    | \ / |
    | \ / |
    | \/ |
    | /\ |
    | / \ |
    | / \ |
    | | | |
    D1Base D2Base

    And the revised class declarations at the top of hierarchy would look
    like:

    class Base
    {
    int n;
    };

    class Base1 : public Base
    {
    };

    class Base2 : public Base
    {
    };

    class D1Base : public virtual Base1, public virtual Base2
    {
    };

    class D2Base : public virtual Base1, public virtual Base2
    {
    };

    The rest of the inheritance hierarchy would remain the same.

    Greg
     
    Greg Herlihy, Apr 30, 2007
    #4
  5. jg wrote:
    > Base
    > / / \ \
    > / / \ \
    > / / \ \
    > D1Base D2Base D1Base D2Base
    > \ / \ /
    > Middle Middle
    > | |/
    > D1Middle D2Middle
    > \ /
    > \ /
    > \ /
    > Last


    If you are going to draw ascii art, it would be a good
    idea to use fixed-width font, wouldn't you think? That looks
    like gibberish when looked with a fixed-width font.
    Using variable-width font to draw ascii art is insane because
    it assumes everyone else is using the exact same font as you are.
     
    Juha Nieminen, Apr 30, 2007
    #5
  6. Massimo

    James Kanze Guest

    On Apr 30, 3:53 am, "Massimo" <> wrote:
    > Hi to all, I'm facing a problem in a particularly complex inheritance
    > hierarchy, and I'd like to know what the standard says about it and if my
    > compiler is correct in what it does.


    > I have two consecutive layers of diamond-shaped inheritance; the first layer
    > is declared as using virtual inheritance, while the second one is declared
    > as *not* using it.


    > This is what I'd like to have:


    > Base Base
    > / \ / \
    > D1Base D2Base D1Base D2Base
    > \ / \ /
    > Middle Middle
    > || ||
    > D1Middle D2Middle
    > \ /
    > \ /
    > \ /
    > Last


    Which is impossible in C++.

    > This is what the compiler actually does:


    > Base
    > / \
    > D1Base D2Base
    > \ /
    > Middle
    > / \
    > D1Middle D2Middle
    > \ /
    > Last


    That's not what it does given the code you posted. What you're
    actually getting is more like.

    ------Base------
    / / \ \
    / / \ \
    D1Base D2Base D1Base D2Base
    \ / \ /
    Middle Middle
    || ||
    D1Middle D2Middle
    \ /
    \ /
    \ /
    Last

    > If I remove all virtual inheritance, everything goes just
    > fine; if I use it in the first layer, there seems to be no way
    > of disabling it afterwards.


    Virtual inheritance only affects the classes which derive using
    it. On the other hand, all instances of all classes which
    derive from Base will share a common instance. You can't group,
    with two or three different groups of classes sharing a common
    instance.

    What you might try is making Base, the two DnBase and Middle
    templates (without changing anything else), and instantiating
    them on eithre D1Middle or D2Middle. By doing this, the 2
    Middle, each of the DnBase, and the two Base are formally
    different types. This could result in significant code
    duplication, of course.

    > Does the standard saying anything about this, or is it
    > implementation-dependant? My compiler is Visual Studio 2005.


    The standard specifies very precisely what abstract graph you
    should end up with. All of the compilers I have access to
    (VC++, Sun CC and g++) are conform in this respect, and
    generate to diagram I've given with your code.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Apr 30, 2007
    #6
  7. Massimo

    jg Guest

    On Apr 30, 1:39 am, Juha Nieminen <> wrote:
    > jg wrote:
    > > Base
    > > / / \ \
    > > / / \ \
    > > / / \ \
    > > D1Base D2Base D1Base D2Base
    > > \ / \ /
    > > Middle Middle
    > > | |/
    > > D1Middle D2Middle
    > > \ /
    > > \ /
    > > \ /
    > > Last

    >
    > If you are going to draw ascii art, it would be a good
    > idea to use fixed-width font, wouldn't you think? That looks
    > like gibberish when looked with a fixed-width font.
    > Using variable-width font to draw ascii art is insane because
    > it assumes everyone else is using the exact same font as you are.


    Aha, didn't know this would have made you unhappy. Knowing
    that I did not choose to do so could probably make you happy
    again. :) Yeah, your point taken !
     
    jg, Apr 30, 2007
    #7
  8. Massimo

    Massimo Guest

    "Greg Herlihy" <> ha scritto nel messaggio
    news:...

    > To illustrate. With this change the top level of the inheritance graph
    > would like this:
    >
    > Base Base
    > | |
    > | |
    > Base1 Base2
    > | | | |
    > | \ / |
    > | \ / |
    > | \/ |
    > | /\ |
    > | / \ |
    > | / \ |
    > | | | |
    > D1Base D2Base
    >
    > And the revised class declarations at the top of hierarchy would look
    > like:
    >
    > class Base
    > {
    > int n;
    > };
    >
    > class Base1 : public Base
    > {
    > };
    >
    > class Base2 : public Base
    > {
    > };
    >
    > class D1Base : public virtual Base1, public virtual Base2
    > {
    > };
    >
    > class D2Base : public virtual Base1, public virtual Base2
    > {
    > };


    This way, there would be two different "n" in Base1 and Base2, and so D1Base
    would have *two* n's, as would D2Base. This is not what i'm looking for... I
    want D1Base to have *only one* n (here the virtual inheritance need), and
    D2Base to also have *only one* n; but I need those two n's to be two
    different ones when deriving from both D1Base and D2Base.

    I think the only way to obtain this is to *not* derive subsequent classes
    from both, but to put a couple of Middle objects inside Last as members;
    this is not exactly what I needed, but it will at least work...

    But I'm still curious on why the "virtual" setting for the inheritance is...
    inherited, even if I've specified "virtual" only when deriving from Base,
    and not when deriving subsequent classes. It just seems to have no effect on
    subsequent derivations, making it actually redundant.


    Massimo
     
    Massimo, Apr 30, 2007
    #8
  9. Massimo

    Massimo Guest

    "James Kanze" <> ha scritto nel messaggio
    news:...
    On Apr 30, 3:53 am, "Massimo" <> wrote:

    >Virtual inheritance only affects the classes which derive using
    >it.


    Why is it in effect when I'm sub-deriving in a non-virtual manner, then?

    > On the other hand, all instances of all classes which
    > derive from Base will share a common instance.
    > You can't group, with two or three different groups of classes
    > sharing a common instance.


    Yes, I've come to understand this.
    Is the "virtual" keyword on sub-derivations redundant, then?

    >What you might try is making Base, the two DnBase and Middle
    >templates (without changing anything else), and instantiating
    >them on eithre D1Middle or D2Middle. By doing this, the 2
    >Middle, each of the DnBase, and the two Base are formally
    >different types.


    This is a good suggestion; can you elaborate a bit? Specifically, what do
    you mean by "instantiating"? Making DxMiddle a derived class of a
    specialized Middle<T>? I think this wouldn't work; actually, before
    semplifying it for posting, the real code involved Base, D1Base, D2Base and
    Middle being template classes, and D1Middle being derived from
    Middle<double>. D2Middle was derived from Middle<double> too, and the
    purpose of D1Middle and D2Middle being different classes was exactly to be
    able to derive (non-virtually!) Last from both of them, so having two
    different Middle<double> inside a Last. But I'm stuck with undesidered
    virtual inheritance, so members of D1Middle and D2Middle end un accessing
    the same data.


    Massimo
     
    Massimo, Apr 30, 2007
    #9
  10. Massimo

    James Kanze Guest

    On Apr 30, 10:33 pm, "Massimo" <> wrote:
    > "James Kanze" <> ha scritto nel messaggionews:...
    > On Apr 30, 3:53 am, "Massimo" <> wrote:


    > >Virtual inheritance only affects the classes which derive using
    > >it.


    > Why is it in effect when I'm sub-deriving in a non-virtual manner, then?


    I don't get what you're trying to say. If you write something
    like:

    class A : public B {} ;

    B is NOT a virtual base. Period, and regardless of anything
    else in the program. There will be one instance of B for every
    instance of A, always.

    > > On the other hand, all instances of all classes which
    > > derive from Base will share a common instance.
    > > You can't group, with two or three different groups of classes
    > > sharing a common instance.


    > Yes, I've come to understand this.
    > Is the "virtual" keyword on sub-derivations redundant, then?


    No. Either you inherit virtually, and the keyword is necessary,
    or you don't inherit virtually, and the keyword cannot be used.

    > >What you might try is making Base, the two DnBase and Middle
    > >templates (without changing anything else), and instantiating
    > >them on eithre D1Middle or D2Middle. By doing this, the 2
    > >Middle, each of the DnBase, and the two Base are formally
    > >different types.


    > This is a good suggestion; can you elaborate a bit? Specifically, what do
    > you mean by "instantiating"?


    Do you know what a template is? Do you know what it means to
    instantiate it.

    > Making DxMiddle a derived class of a
    > specialized Middle<T>? I think this wouldn't work; actually, before
    > semplifying it for posting, the real code involved Base, D1Base, D2Base and
    > Middle being template classes, and D1Middle being derived from
    > Middle<double>. D2Middle was derived from Middle<double> too, and the
    > purpose of D1Middle and D2Middle being different classes was exactly to be
    > able to derive (non-virtually!) Last from both of them, so having two
    > different Middle<double> inside a Last. But I'm stuck with undesidered
    > virtual inheritance, so members of D1Middle and D2Middle end un accessing
    > the same data.


    The trick is to instantiate one side on D1Middle, and the other
    on D2Middle.

    Note that there's nothing in the rules which says that a
    template has to use the instantiation type. Something like:

    template< typename T >
    class Base
    {
    // as before...
    } ;

    is perfectly legal. The class will behave exactly the same no
    matter what type it is instantiated on, *but* it will have a
    different type. Once you do this, you can then define the other
    classes as:

    template< typename T >
    class D1Base : public virtual Base< T >
    {
    // as before...
    } ;

    template< typename T >
    class D2Base : public virtual Base< T >
    {
    // as before...
    } ;

    template< typname T >
    class Middle : public D1Base< T >, public D2Base< T >
    {
    // as before...
    } ;

    class D1Middle : public Middle< D1Middle >
    {
    // as before...
    } ;

    class D2Middle : public Middle< D2Middle >
    {
    // as before...
    } ;

    class Last : public D1Middle, public D2Middle
    {
    // as before...
    } ;

    This will result in the hierarchy you said you wanted, with the
    exception that all of the classes on the left side will in fact
    be instantiations on D1Middle, and all on the right side will in
    fact be instantiations on D2Middle. (Note that this may affect
    your external interface. There's no longer any such class a
    Base, for example. And it typically means hoisting a lot of
    code up into the headers---not necessarily something you want to
    do either.)

    --
    James Kanze (Gabi Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, May 1, 2007
    #10
  11. Massimo

    Massimo Guest

    "James Kanze" <> ha scritto nel messaggio
    news:...

    >I don't get what you're trying to say. If you write something
    >like:
    >
    > class A : public B {} ;
    >
    >B is NOT a virtual base. Period, and regardless of anything
    >else in the program. There will be one instance of B for every
    >instance of A, always.


    That's exaclty what I would expect.
    But then, if B has a virtual base C, and I do

    class A1 : public B {};
    class A2 : public B {};
    class D: public A1,public A2 {};

    As you said, B, is not a virtual base, and I should have two instances of B:
    one in A1 and the other in A2. Each of these *should* contain its own base
    classes, up to and including C. Why is it, then, that when I re-derive from
    both of them the instance of C becomes unique? This is what is confusing me.

    >Note that there's nothing in the rules which says that a
    >template has to use the instantiation type. Something like:
    >
    > template< typename T >
    > class Base
    > {
    > // as before...
    > } ;
    >
    >is perfectly legal. The class will behave exactly the same no
    >matter what type it is instantiated on, *but* it will have a
    >different type.


    That's great, I'll give it a try. Thanks.


    Massimo
     
    Massimo, May 1, 2007
    #11
  12. Massimo

    James Kanze Guest

    On May 1, 2:31 pm, "Massimo" <> wrote:
    > "James Kanze" <> ha scritto nel messaggionews:...


    > >I don't get what you're trying to say. If you write something
    > >like:


    > > class A : public B {} ;


    > >B is NOT a virtual base. Period, and regardless of anything
    > >else in the program. There will be one instance of B for every
    > >instance of A, always.


    > That's exaclty what I would expect.
    > But then, if B has a virtual base C, and I do


    > class A1 : public B {};
    > class A2 : public B {};
    > class D: public A1,public A2 {};


    > As you said, B, is not a virtual base, and I should have two instances of B:
    > one in A1 and the other in A2.


    You do.

    > Each of these *should* contain its own base
    > classes, up to and including C.


    No. When you defined B, you said that you want all instances of
    B in a single hierarchy to share the same C. That's what the
    virtual means in inheritance. More generally, it means that
    *all* classes which inherit virtually from C share the same
    instance, regardless of where they might be in the hierarchy.

    > Why is it, then, that when I re-derive from
    > both of them the instance of C becomes unique?


    Because you told the compiler that that's what you wanted.

    One way of looking at it is that the virtual base class is in
    fact provided by the most derived class. It is the most
    derived class which will call its constructor, for example, and
    it is the most derived class which will determine where it is
    physically located. Even if the author of the most derived
    class doesn't even know it is there. So when you inherit
    virtually, you are more or less saying I want a single instance
    to be provided by the most derived class.

    > This is what is confusing me.


    Just curious, but what would you expect if in addition, D
    derived virtually from C, i.e.:

    class B : public virtual C {} ;
    class A1 : public B {} ;
    class A2 : public B {} ;
    class D : public A1, public A2, public virtual C {} ;

    Note that you can have addional instances of C:

    class B : public virtual C {} ;
    class A1 : public B, public C {} ;
    class A2 : public B {} ;
    class D : public A1, public A2, public virtual C {} ;

    This hierarchy contains two instances of C, one for all of the
    virtual inheritance, and one for A1.

    If you want a somewhat clearer view, try starting with:

    class C
    {
    public:
    C( std::string const& i )
    {
    std::cout << "C initialized by " << i << std::endl ;
    }
    } ;

    and experimenting with different hierarchies, each time adding
    an initializer for C with the class name, e.g.:

    B::B() : C( "B" ) {}
    A1::A1() : C( "A1" ) {}
    // ...

    You'll find that:

    -- there's no problem specifying the initializer, as long as
    there is a virtual inheritance somewhere below the class in
    the hierarchy -- you don't have to specify it in the class
    doing the initialization --, and

    -- the constructor for C will always (without exception) be
    called from the most derived class.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, May 2, 2007
    #12
  13. Massimo

    Massimo Guest

    "James Kanze" <> ha scritto nel messaggio
    news:...

    >One way of looking at it is that the virtual base class is in
    >fact provided by the most derived class. It is the most
    >derived class which will call its constructor, for example, and
    >it is the most derived class which will determine where it is
    >physically located. Even if the author of the most derived
    >class doesn't even know it is there. So when you inherit
    >virtually, you are more or less saying I want a single instance
    >to be provided by the most derived class.


    That's a lot clearer, thanks.


    Massimo
     
    Massimo, May 2, 2007
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. maxw_cc
    Replies:
    1
    Views:
    3,197
    Martijn van Steenbergen
    Dec 21, 2003
  2. qazmlp
    Replies:
    19
    Views:
    826
    Daniel T.
    Feb 4, 2004
  3. cppsks
    Replies:
    0
    Views:
    853
    cppsks
    Oct 27, 2004
  4. Ashwin
    Replies:
    2
    Views:
    367
    Pierre Barbier de Reuille
    Aug 1, 2006
  5. Replies:
    1
    Views:
    485
    Alf P. Steinbach
    Mar 14, 2009
Loading...

Share This Page