problems with multiple levels of virtual base classes 
Author Message
 problems with multiple levels of virtual base classes

Given the following simple program:

#include <string>
#include <iostream>

class Base1
{
    public:
        Base1()
        {
            std::cout << "in Base1::Base1()" << std::endl;
        }

        Base1(const std::string &a_rString) : m_String(a_rString)
        {
            std::cout << "in Base1::Base1(const std::string&)" << std::endl;
        }
    private:
        std::string     m_String;

Quote:
};

class Base2 : public virtual Base1
{
    public:
        Base2()
        {
            std::cout << "in Base2::Base2()" << std::endl;
        }

        Base2(const std::string &a_rString) : Base1(a_rString)
        {
            std::cout << "in Base2::Base2(const std::string&)" << std::endl;
        }

Quote:
};

class Middle1 : public virtual Base2
{
    public:
        Middle1()
        {
            std::cout << "in Middle1::Middle1()" << std::endl;
        }

        Middle1(const std::string &a_rString) : Base2(a_rString)
        {
            std::cout << "in Middle1::Middle1(const std::string&)"
                      << std::endl;
        }

Quote:
};

class Middle2 : public virtual Base2
{
    public:
        Middle2()
        {
            std::cout << "in Middle2::Middle2()" << std::endl;
        }

        Middle2(const std::string &a_rString) : Base2(a_rString)
        {
            std::cout << "in Middle2::Middle2(const std::string&)"
                      << std::endl;
        }

Quote:
};

class Derived : public Middle1, public Middle2
{
    public:
        Derived()
        {
            std::cout << "in Middle2::Middle2()" << std::endl;
        }

        Derived(const std::string &a_rString) : Base2(a_rString)
        {
            std::cout << "in Middle2::Middle2(const std::string&)"
                      << std::endl;
        }

Quote:
};

int main()
{
    Derived("Hello");
    return 0;

Quote:
}

I get the following output:
  in Base1::Base1()
  in Base2::Base2(const std::string&)
  in Middle1::Middle1()
  in Middle2::Middle2()
  in Middle2::Middle2(const std::string&)

I expected to get this:
  in Base1::Base1(const std::string&)
  in Base2::Base2(const std::string&)
  in Middle1::Middle1()
  in Middle2::Middle2(const std::string&)

The thing is that the Base1 class default constructor is getting called
instead of the one I specified.  If I remove the Base2 class from the
hierarchy or if I make Base2 derive "non-virtually" from Base1, it works
fine.

Anyone got any ideas as to what is going on?  Any possible work-arounds?

--
Chuck McCorvey
Creative SolutionWare, Inc.



Tue, 17 Oct 2000 03:00:00 GMT  
 problems with multiple levels of virtual base classes

Quote:

>Given the following simple program:

>#include <string>
>#include <iostream>

>class Base1
>{
>    public:
>        Base1()
>        {
>            std::cout << "in Base1::Base1()" << std::endl;
>        }

>        Base1(const std::string &a_rString) : m_String(a_rString)
>        {
>            std::cout << "in Base1::Base1(const std::string&)" << std::endl;
>        }
>    private:
>        std::string     m_String;
>};

>class Base2 : public virtual Base1
>{
>    public:
>        Base2()
>        {
>            std::cout << "in Base2::Base2()" << std::endl;
>        }

>        Base2(const std::string &a_rString) : Base1(a_rString)
>        {
>            std::cout << "in Base2::Base2(const std::string&)" << std::endl;
>        }
>};

>class Middle1 : public virtual Base2
>{
>    public:
>        Middle1()
>        {
>            std::cout << "in Middle1::Middle1()" << std::endl;
>        }

>        Middle1(const std::string &a_rString) : Base2(a_rString)
>        {
>            std::cout << "in Middle1::Middle1(const std::string&)"
>                      << std::endl;
>        }
>};

>class Middle2 : public virtual Base2
>{
>    public:
>        Middle2()
>        {
>            std::cout << "in Middle2::Middle2()" << std::endl;
>        }

>        Middle2(const std::string &a_rString) : Base2(a_rString)
>        {
>            std::cout << "in Middle2::Middle2(const std::string&)"
>                      << std::endl;
>        }
>};

>class Derived : public Middle1, public Middle2
>{
>    public:
>        Derived()
>        {
>            std::cout << "in Middle2::Middle2()" << std::endl;
>        }

>        Derived(const std::string &a_rString) : Base2(a_rString)
>        {
>            std::cout << "in Middle2::Middle2(const std::string&)"

// I think you meant :   "in Derived::Derived(const std::string&)"

- Show quoted text -

Quote:
>                      << std::endl;
>        }
>};

>int main()
>{
>    Derived("Hello");
>    return 0;
>}

>I get the following output:
>  in Base1::Base1()
>  in Base2::Base2(const std::string&)
>  in Middle1::Middle1()
>  in Middle2::Middle2()
>  in Middle2::Middle2(const std::string&)

>I expected to get this:
>  in Base1::Base1(const std::string&)
>  in Base2::Base2(const std::string&)
>  in Middle1::Middle1()
>  in Middle2::Middle2(const std::string&)

>The thing is that the Base1 class default constructor is getting called
>instead of the one I specified.  If I remove the Base2 class from the
>hierarchy or if I make Base2 derive "non-virtually" from Base1, it works
>fine.

>Anyone got any ideas as to what is going on?  Any possible work-arounds?

From CD2 (I've italicized the important parts and summarized below):

12.6.2  Initializing bases and members               [class.base.init]
5 5 Initialization shall proceed in the following order:
  --First,  and  only  for  the constructor of the most derived class as
    described below, virtual base classes shall be  initialized  in  the
    order  they  appear  on a depth-first left-to-right traversal of the
    directed acyclic graph of base classes, where "left-to-right" is the
    order  of  appearance  of  the base class names in the derived class
    base-specifier-list.
  --Then, direct base classes shall be initialized in declaration  order
    as  they  appear in the base-specifier-list (regardless of the order
    of the mem-initializers).
  --Then, nonstatic data members shall be initialized in the order  they
    were declared in the class definition (again regardless of the order
    of the mem-initializers).
  --Finally, the body of the constructor is executed.
  [Note: the declaration order is mandated to ensure that base and  mem-
  ber  subobjects  are destroyed in the reverse order of initialization.  ]
6 All sub-objects representing virtual base classes are  initialized  by
  the  constructor  of  the most derived class (_intro.object_).  If the
  constructor of the most derived class does not specify a  mem-initial-
  izer  for  a  virtual  base  class  V, then V's default constructor is
  called to initialize the virtual base class subobject.  If V does  not
  have  an  accessible  default  constructor, the initialization is ill-
  formed.  A mem-initializer  naming  a  virtual  base  class  shall  be
  ignored  during  execution of the constructor of any class that is not
  the most derived class.  [Example:          class V {          public:
              V();              V(int);              // ...          };
          class A : public virtual V {          public:              A();
              A(int);              // ...          };
          class B : public virtual V {          public:              B();
              B(int);              // ...          };
          class C : public A, public B, private virtual V {          public:
              C();              C(int);              // ...          };
          A::A(int i) : V(i) { /* ... */ }          B::B(int i) { /* ... */ }
          C::C(int i) { /* ... */ }          V v(1); // use V(int)
          A a(2); // use V(int)          B b(3); // use V()
          C c(4); // use V()   --end example]

Summary:

You must specify *all* the constructors for all virtual base classes *in the entire heirarchy* (by not explicitly specifying a virtual base class constructor, your are specifying the default constructor).  The compiler calls virtual base class constructors before all non-virtual base classes.  The compiler orders the calls of the virtual base class constructors in a depth-first, left-right traversal of the class heirachy.  The order *cannot* be changed by the constructor initialization list.  In your example:

        Derived(const std::string &a_rString) : Base2(a_rString)
        { ... }

is the same as:

        Derived(const std::string &a_rString) : Base1(), Base2(a_rString), Middle1(), Middle2()
        { ... }

Note that:

        Derived(const std::string &a_rString) : Base2(a_rString), Base1(), Middle1(), Middle2()
        { ... }

would print out in the exact same order (the order you declare the classes after "class Foo: "
determines the order of construction, *not* the order of the constructor initialization list.  To get
closer to what you want, you would have to change the Derived constructor to:

        Derived(const std::string &a_rString) : Base1(a_rString), Base2(a_rString)
        { ... }



Wed, 18 Oct 2000 03:00:00 GMT  
 problems with multiple levels of virtual base classes

Never mind...  I forgot about the requirement that the most derived class
must explicitly call the constructor of the virtual base classes otherwise
the their default constructors get called...

--
Chuck McCorvey
Creative SolutionWare, Inc.

Quote:

>Given the following simple program:

>#include <string>
>#include <iostream>

[SNIP]

>The thing is that the Base1 class default constructor is getting called
>instead of the one I specified.  If I remove the Base2 class from the
>hierarchy or if I make Base2 derive "non-virtually" from Base1, it works
>fine.

>Anyone got any ideas as to what is going on?  Any possible work-arounds?

>--
>Chuck McCorvey
>Creative SolutionWare, Inc.



Wed, 18 Oct 2000 03:00:00 GMT  
 problems with multiple levels of virtual base classes

        I found your question to be very interesting.  After a bit of fiddling I
changed your program slightly into the following form.  The output is what
you expected though Base2 is virtually inherited.  My conclusion from this
is that virtual inheritance "disconnects" the usual sequence of
constructors in a hierarchy and requires the user of the hierarchy to
specify explicitly all constructors other than the default.  This may be a
bug, I don't know.  One would have to consult the Final Draft International
Standard for C++ to know.  However, consider what you are asking the
compiler to do.
        Virtual inheritance causes the compiler to eliminate redundant images of
base classes from derived classes when base classes appear more than once
in the hierarchy.  So Derived in your example contains only one copy of
Base1 and only one copy of Base2.  It is only because Base1 and Base2 are
declared virtually inherited that the compiler allows me to specify which
constructor to use for them in the Derived constructor below.  Without
virtual inheritance the compiler returns an error that Base1 and Base2 are
not base classes of Derived, because Middle's come inbetween.  But with
virtual inheritance, suddenly the compiler is treating all the base classes
of Derived as if they were all only one level removed.  Now, given this
point of view, what should the compiler do with your constructor for
Middle2?  On the one hand, in the constructor for Derived you haven't
specified how to construct Middle1, Middle2 or Base1.  Therefore, it must
use the default constructors.  The fact that you have specified a
constructor for Base2 in Middle2 has been discounted by the compiler in
accounting for virtual inheritance.
        I think the lesson is that, if you declare virtual inheritance
hierarchies, you should be prepared to specify the constructors for _all_
classes in the hierarchy when constructing objects from it.  Thanks for the
interesting question.

#include <string>
#include <iostream>

class Base1
{
    public:
        Base1()
        {
            std::cout << "in Base1::Base1()" << std::endl;
        }

        Base1(const std::string &a_rString) : m_String(a_rString)
        {
            std::cout << "in Base1::Base1(const std::string&)" <<
std::endl;
        }
    private:
        std::string     m_String;

Quote:
};

class Base2 : public virtual Base1
{
    public:
        Base2()
        {
            std::cout << "in Base2::Base2()" << std::endl;
        }

        Base2(const std::string &a_rString)
        {
            std::cout << "in Base2::Base2(const std::string&)" <<
std::endl;
        }

Quote:
};

class Middle1 : public virtual Base2
{
    public:
        Middle1()
        {
            std::cout << "in Middle1::Middle1()" << std::endl;
        }

        Middle1(const std::string &a_rString)
        {
            std::cout << "in Middle1::Middle1(const std::string&)"
                      << std::endl;
        }

Quote:
};

class Middle2 : public virtual Base2
{
    public:
        Middle2()
        {
            std::cout << "in Middle2::Middle2()" << std::endl;
        }

        Middle2(const std::string &a_rString)
        {
            std::cout << "in Middle2::Middle2(const std::string&)"
                      << std::endl;
        }

Quote:
};

class Derived : public Middle1, public Middle2
{
    public:
        Derived()
        {
            std::cout << "in Derived::Derived()" << std::endl;
        }

        Derived(const std::string &a_rString) : Middle2(a_rString),
Base2(a_rString), Base1(a_rString)
        {
            std::cout << "in Derived::Derived(const std::string&)"
                      << std::endl;
        }

Quote:
};

int main()
{
    Derived("Hello");
    return 0;
Quote:
}



Wed, 18 Oct 2000 03:00:00 GMT  
 problems with multiple levels of virtual base classes

You are most correct - I knew that once, but I use virtual inheritance so
rarely that I got stung until Bjarne's 3rd edition reminded me of my
error...

--
Chuck McCorvey
Creative SolutionWare, Inc.

Quote:

> I found your question to be very interesting.  After a bit of fiddling I
>changed your program slightly into the following form.  The output is what
>you expected though Base2 is virtually inherited.  My conclusion from this
>is that virtual inheritance "disconnects" the usual sequence of
>constructors in a hierarchy and requires the user of the hierarchy to
>specify explicitly all constructors other than the default.  This may be a
>bug, I don't know.  One would have to consult the Final Draft International
>Standard for C++ to know.  However, consider what you are asking the
>compiler to do.
> Virtual inheritance causes the compiler to eliminate redundant images of
>base classes from derived classes when base classes appear more than once
>in the hierarchy.  So Derived in your example contains only one copy of
>Base1 and only one copy of Base2.  It is only because Base1 and Base2 are
>declared virtually inherited that the compiler allows me to specify which
>constructor to use for them in the Derived constructor below.  Without
>virtual inheritance the compiler returns an error that Base1 and Base2 are
>not base classes of Derived, because Middle's come inbetween.  But with
>virtual inheritance, suddenly the compiler is treating all the base classes
>of Derived as if they were all only one level removed.  Now, given this
>point of view, what should the compiler do with your constructor for
>Middle2?  On the one hand, in the constructor for Derived you haven't
>specified how to construct Middle1, Middle2 or Base1.  Therefore, it must
>use the default constructors.  The fact that you have specified a
>constructor for Base2 in Middle2 has been discounted by the compiler in
>accounting for virtual inheritance.
> I think the lesson is that, if you declare virtual inheritance
>hierarchies, you should be prepared to specify the constructors for _all_
>classes in the hierarchy when constructing objects from it.  Thanks for the
>interesting question.

>#include <string>
>#include <iostream>

>class Base1
>{
>    public:
>        Base1()
>        {
>            std::cout << "in Base1::Base1()" << std::endl;
>        }

>        Base1(const std::string &a_rString) : m_String(a_rString)
>        {
>            std::cout << "in Base1::Base1(const std::string&)" <<
>std::endl;
>        }
>    private:
>        std::string     m_String;
>};

>class Base2 : public virtual Base1
>{
>    public:
>        Base2()
>        {
>            std::cout << "in Base2::Base2()" << std::endl;
>        }

>        Base2(const std::string &a_rString)
>        {
>            std::cout << "in Base2::Base2(const std::string&)" <<
>std::endl;
>        }
>};

>class Middle1 : public virtual Base2
>{
>    public:
>        Middle1()
>        {
>            std::cout << "in Middle1::Middle1()" << std::endl;
>        }

>        Middle1(const std::string &a_rString)
>        {
>            std::cout << "in Middle1::Middle1(const std::string&)"
>                      << std::endl;
>        }
>};

>class Middle2 : public virtual Base2
>{
>    public:
>        Middle2()
>        {
>            std::cout << "in Middle2::Middle2()" << std::endl;
>        }

>        Middle2(const std::string &a_rString)
>        {
>            std::cout << "in Middle2::Middle2(const std::string&)"
>                      << std::endl;
>        }
>};

>class Derived : public Middle1, public Middle2
>{
>    public:
>        Derived()
>        {
>            std::cout << "in Derived::Derived()" << std::endl;
>        }

>        Derived(const std::string &a_rString) : Middle2(a_rString),
>Base2(a_rString), Base1(a_rString)
>        {
>            std::cout << "in Derived::Derived(const std::string&)"
>                      << std::endl;
>        }
>};

>int main()
>{
>    Derived("Hello");
>    return 0;
>}



Wed, 18 Oct 2000 03:00:00 GMT  
 
 [ 5 post ] 

 Relevant Pages 

1. Problem with IMPLEMENT_xxx macros and virtual base classes.

2. Problem: CEdit as virtual base class ...

3. Problem with virtual functions in dll-based class?

4. Problem with IMPLEMENT_xxx macros and virtual base classes.

5. Problem with IMPLEMENT_xxx macros and virtual base classes.

6. Serialize class derived from virtual base class

7. Sealed Class in C++ -- private virtual base

8. Calling a base class virtual function

9. Derived class "hiding" virtual Base method

10. Sealed Class in C++ -- private virtual base

11. Virtual Base Class Constructors

12. Virtual function in base class constructor?

 

 
Powered by phpBB® Forum Software