Topic: Init of virtual bases


Author: doug@badger.ads.com (Doug Morgan)
Date: 3 Sep 94 16:17:05
Raw View
In article <CvJ1wI.43z@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>In article <DOUG.94Sep1123433@monet.ads.com> doug@monet.ads.com (Doug Morgan) writes:
>>...
>>"Should never," should never be taken too seriously (especially with
>>C++).  C++ does support the notion of a virtual base holding data
>>members to be shared between sibling subobjects.
>
>C++ does not support it very well. It merely "allows" it,
>and any porr programmer that uses this feature and derives a few
>times form the class virtually will wish they hadn't.

Eventhough I rail against the C++ specification for virtual base
classes, I have seen and used what I consider reasonable virtual bases
that do define data.  Classes derived from these formed wide and deep
lattices.  Putting in all the mem-initializers was a pain, but no more
so than would have been putting in all the data-carrying mixin
classes.  (However, the generality needed to allow multiple ways of
storing the data was never an issue for the library's domain.)

>...
>That was the point I wished to make: if you use
>the feature and find its a pain in the arse, you have only
>yourself to blame: I've explained why it is not necessary
>to get yourself tied up into knots with the problem of
>multiple ctor-initialisers.

Can't argue with that.  I just never found mem-initializers for
virtual bases to be a particularly difficult knot to deal with
consistently.  (Annoying, yes.)

>As for using inheritance for other than the representation
>of subtyping -- which  of course you can -- on your own head be it.

I would say it more like: be sure that in every instance your notion
of subtyping exactly matches the C++ mechanisms you've chosen to use.
Also, choose the best combination of notion of subtyping and C++
mechanisms for your application.  Make sure they meet your needs.  The
general drift seems similar to what you said.

>>>>> It is therefore my feeling that IF your code seems
>>>>>to require any initialisation of a virtual base, you code
>>>>>is exhibiting a design fault.
>>
>>Again, it is only a design fault under a notion of design that rejects
>>the idea that sharing data members is ever a sensible thing to do.
>
>Rubbish. :-) Sharing data is a sensible thing to do.
>But you should NOT do it by putting the data in a shared base class,
>you do it by putting FUNCTIONS that access the data in that shared base.

Yeah, I said that wrong.  However, since I did mean to say "... only a
design flaw under a notion of design that rejects the idea that
putting the data in a shared base class is ever a sensible thing to
do" we still disagree.  I agree that getting at the data (wherever it
might be) through functions is generally good.  However, for many
applications (e.g., a large subset of those where you know a data
member is the one-and-only "right" way to ever access your
information), putting data members in the shared base can also be
good.

If specifying data members violates the notion of subtyping that is
currently guiding the system design, then data members in virtual
bases should not be used.  However, some notions of subtyping have no
trouble dealing with object memory layout, specification of inline,
virtual, non-virtual function implementations, or anything else C++
might throw in.  There just is no overriding theoretical justification
for always keeping data out of virtual bases.  Practical and
completely correct designs can go either way.

>...
>... But "in general" and in particular,
>when writing reusable object oriented library code from
>which you intend the user to derived new classes, its likely
>that using the mixin technique is the best way.

Certainly using mixins will often be the best way.  Perhaps I
originally was just splitting hairs in responding to the use of
"should never."

Doug
--------------------------------------------------------------------
Doug Morgan, doug@ads.com, (415) 960-7444
Advanced Decision Systems (a division of Booz-Allen & Hamilton Inc.)
1500 Plymouth St., Mountain View, CA 94043-1230
--------------------------------------------------------------------




Author: jdp@polstra.com (John Polstra)
Date: 5 Sep 1994 21:15:33 -0700
Raw View
In article <CvJ1wI.43z@ucc.su.oz.au>,
John Max Skaller <maxtal@physics.su.OZ.AU> wrote:
> ... initialising bases from a "join" class is not correct
> in theory and wont work in practice either.

This is the second time I've seen you make that assertion in this
thread.  Could you please justify it somehow, preferably with an
example?

(Just to make sure we are talking about the same thing, here's a diagram:

   VirtualBase
      /   \
     /     \
    A       B
     \     /
      \   /
       \ /
        C
        |
        D
        |
        E

E is the most derived class, and I assume that by your terminology C is
the join class.)

To me, it makes a lot more sense to require the virtual base class to be
initialized by C than by E.  Why is it that that "is not correct in theory
and won't work in practice either" ?
--
   John Polstra                                       jdp@polstra.com
   John D. Polstra & Co., Inc.                   Phone (206) 932-6482
   Seattle, Washington USA                         Fax (206) 935-1262
   "Self-knowledge is always bad news."                 -- John Barth




Author: doug@monet.ads.com (Doug Morgan)
Date: 1 Sep 94 12:34:33
Raw View
In article <CvCyzp.61v@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>In article <33r0j8$5r5@st-james.comp.vuw.ac.nz> (Rey Crisostomo) writes:
>>In article <Cv6p4I.HMo@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>>> .......
>>> However, my tentative conclusion is that virtual
>>>bases should never need any more than a default constructor
>>>for initialisation.  Indeed, they should probably have no
>>>data members.

"Should never," should never be taken too seriously (especially with
C++).  C++ does support the notion of a virtual base holding data
members to be shared between sibling subobjects.  It isn't likely that
much agreement will materialize that not using this support is almost
always a good thing.

>>> For this reason I am drawn to the conclusion that
>>>shared bases are shared globally and not accessed by ANY path,
>>>but always directly. They have no local behaviour, they
>>>cant be localised in the context of a so called "join" class.

Given the premise, the conclusion is reasonable.  The reasonableness
of the premise is questionable.
>...

>>> It is therefore my feeling that IF your code seems
>>>to require any initialisation of a virtual base, you code
>>>is exhibiting a design fault.

Again, it is only a design fault under a notion of design that rejects
the idea that sharing data members is ever a sensible thing to do.
Certainly, it is always possible to take any design containing a
virtual base with data members and show that certain expansions of the
class lattice then become less reasonable.  However, if those forms of
expansion are of no interest to the knowledgable system designer, then
the original virtual base with data is not a design flaw.  Rather, it
is a design decision that, depending on particulars, can well be the
best one.

Doug
--------------------------------------------------------------------
Doug Morgan, doug@ads.com, (415) 960-7444
Advanced Decision Systems (a division of Booz-Allen & Hamilton Inc.)
1500 Plymouth St., Mountain View, CA 94043-1230
--------------------------------------------------------------------




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sat, 3 Sep 1994 00:12:17 GMT
Raw View
In article <DOUG.94Sep1123433@monet.ads.com> doug@monet.ads.com (Doug Morgan) writes:
>In article <CvCyzp.61v@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>>In article <33r0j8$5r5@st-james.comp.vuw.ac.nz> (Rey Crisostomo) writes:
>>>In article <Cv6p4I.HMo@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>>>> .......
>>>> However, my tentative conclusion is that virtual
>>>>bases should never need any more than a default constructor
>>>>for initialisation.  Indeed, they should probably have no
>>>>data members.
>
>"Should never," should never be taken too seriously (especially with
>C++).  C++ does support the notion of a virtual base holding data
>members to be shared between sibling subobjects.

 C++ does not support it very well. It merely "allows" it,
and any porr programmer that uses this feature and derives a few
times form the class virtually will wish they hadn't.

>It isn't likely that
>much agreement will materialize that not using this support is almost
>always a good thing.

 I dont care if you agree or not. I just stated tentative
conclusions from my own analysis. In particular, as is often the
case, there is nothing wrong with C++ as it stands, you just
have to know how to use it. Thats sometimes hard (it's taken
me a few years to reach this stage of understanding)

 That is, I think that Bjarne Stroustrup did it right,
and initialising bases from a "join" class is not correct
in theory and wont work in practice either.

 That was the point I wished to make: if you use
the feature and find its a pain in the arse, you have only
yourself to blame: I've explained why it is not necessary
to get yourself tied up into knots with the problem of
multiple ctor-initialisers.

 As for using inheritance for other than the representation
of subtyping -- which  of course you can -- on your own head be it.

>>>> It is therefore my feeling that IF your code seems
>>>>to require any initialisation of a virtual base, you code
>>>>is exhibiting a design fault.
>
>Again, it is only a design fault under a notion of design that rejects
>the idea that sharing data members is ever a sensible thing to do.

 Rubbish. :-) Sharing data is a sensible thing to do.
But you should NOT do it by putting the data in a shared base class,
you do it by putting FUNCTIONS that access the data in that shared base.

 This is called "Object Oriented Programming"
by most people -- using methods to access data.

>Certainly, it is always possible to take any design containing a
>virtual base with data members and show that certain expansions of the
>class lattice then become less reasonable.  However, if those forms of
>expansion are of no interest to the knowledgable system designer, then
>the original virtual base with data is not a design flaw.  Rather, it
>is a design decision that, depending on particulars, can well be the
>best one.

 Yes it might be. But "in general" and in particular,
when writing reusable object oriented library code from
which you intend the user to derived new classes, its likely
that using the mixin technique is the best way.

 The "other" cases might well be for experts and for hacking one
off appplications (when reusability is not important).
--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189




Author: (Rey Crisostomo)
Date: 28 Aug 1994 21:46:15 GMT
Raw View
In article <Cv6p4I.HMo@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
> .......
> However, my tentative conclusion is that virtual
>bases should never need any more than a default constructor
>for initialisation.  Indeed, they should probably have no
>data members.
> .......
> For this reason I am drawn to the conclusion that
>shared bases are shared globally and not accessed by ANY path,
>but always directly. They have no local behaviour, they
>cant be localised in the context of a so called "join" class.

I disagree. My use of virtual bases are usually via public
inheritance. This is because the design of my classes exhibit
true is-a relationships between base and derived classes.

Besides, my virtual bases are not necessarily at the root of
the inheritance tree. Some of them are in the middle and have
already inherited some members that have to be initialised in
its parent's constructor.

Maybe I should give a more concrete example:

class Window {
public:
   Window(Window *parent) : parent(parent) { ... }
   ...

private:
   Window *parent;
};

class ComboBox : public Window {
public:
   ComboBox(Window *parent) : Window(parent) { ... }
   ...
};

class DropdownComboBox : public virtual ComboBox {
public:
   DropdownComboBox(Window *parent) : ComboBox(parent) { ... }
   ...
};

class EditableComboBox : public virtual ComboBox {
public:
   EditableComboBox(Window *parent) : ComboBox(parent) { ... }
   ...
};

class FullComboBox : public DropdownComboBox, public EditableComboBox {
public:
   FullComboBox(Window *parent)
      : ComboBox(parent), DropdownComboBox(parent), EditableComboBox(parent)
   { ... }
};

class OwnerdrawFullCB : public FullComboBox {
public:
   OwnerdrawFullCB(Window *parent) : FullComboBox(parent) { ... }
};

Note that other classes inherit from Window, DropdownComboBox, and
EditableComboBox.

Here, in the constructor to OwnerdrawFullCB, I don't want to be required
to initialize ComboBox. It should be done in FullComboBox unambiguously!

I tried putting a default constructor and an Init(Window *parent) member
function in class Window, but it doesn't work. This is because the body
of class Window's constructor need the true value of the parent pointer
to be able to be constructed properly. I have been able to find workarounds
but they are complex, unintuitive and definitely ugly.

Your solution simply does not apply to my problem. I don't use virtual
bases and multiple inheritance just as a clever way of "sharing data".
The concepts my classes represent clearly reflect the way I structured
my inheritance trees.

> It is therefore my feeling that IF your code seems
>to require any initialisation of a virtual base, you code
>is exhibiting a design fault.

As illustrated above, I don't think there is a fault in the design of my
class structure.


Rey Crisostomo
branzrpc@branz.org.nz
CompuServe:100237,2164
Wellington, New Zealand




Author: branzrpc@branz.org.nz (Rey Crisostomo)
Date: 29 Aug 1994 21:48:33 GMT
Raw View
This is a repost. I think I screwed up the 'From:' line in my previous
post, so here we go again...

As you all know, virtual base classes are always initialized in the
most derived class. I understand the reasoning behind this.

The problem is, this can become a little tedious if you have a virtual
base class without a default constructor. All derived classes will have
to explicitly invoke the constructor of the virtual base class in their
own constructors.

Is it possible to relax the rule so that only derived classes that
multiply inherit from several classes be required to initialize the
virtual base class. This means that the responsibility of initializing
virtual base class(es) be put only where the inheritance hierarchy
"converge" to a single line of inheritance.

For example,

class A {
public:
   A(int x) {}     // no default ctor here.
};

class B : public virtual A {
public:
   B() : A(1) {}
};

class C : public virtual A {
public:
   C() : A(2) {}
};

class D : public B, public C { // inheritance hierarchy "converges" here.
public:
   D() : A(3), B(), C() { }
};

class E : public D {
public:
   E() : D() { }  // <-- _not_ required to initialize ancestor A
                  //   A will be initialized in D() unambigously.
};

In fact, any class that singly inherits from B or C do not have to
initialize A, ie. A will be initialized in the ctors of B and C.

Does this make sense? Or am I being too simplistic?

Rey Crisostomo
branzrpc@branz.org.nz
CompuServe:100237,2164
Wellington, New Zealand




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Tue, 30 Aug 1994 17:23:47 GMT
Raw View
In article <33r0j8$5r5@st-james.comp.vuw.ac.nz> (Rey Crisostomo) writes:
>In article <Cv6p4I.HMo@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>> .......
>> However, my tentative conclusion is that virtual
>>bases should never need any more than a default constructor
>>for initialisation.  Indeed, they should probably have no
>>data members.
>> .......
>> For this reason I am drawn to the conclusion that
>>shared bases are shared globally and not accessed by ANY path,
>>but always directly. They have no local behaviour, they
>>cant be localised in the context of a so called "join" class.
>
>I disagree. My use of virtual bases are usually via public
>inheritance. This is because the design of my classes exhibit
>true is-a relationships between base and derived classes.

 I have no idea what you are disagreeing with.
>
>Besides, my virtual bases are not necessarily at the root of
>the inheritance tree.

 Now you have no idea what you are talking about.
The "root" of a class lattice is the SOURCE of all the arrows,
namely the most derived class. A virtual base necessarily
cannot EVER be the root.

>Maybe I should give a more concrete example:

 You are free to do that but it will prove
nothing just because you can write code that does not
follow a set of design rules that reflect an underlying
theory. What would prove something is if you could
come up with an example which CANNOT be sensibly
implemented without breaking the design rules.

>I tried putting a default constructor and an Init(Window *parent) member
>function in class Window, but it doesn't work. This is because the body
>of class Window's constructor need the true value of the parent pointer
>to be able to be constructed properly. I have been able to find workarounds
>but they are complex, unintuitive and definitely ugly.
>
>Your solution simply does not apply to my problem.

 Yes it does. My solution, as far as I know, is
GUARRANTEED TO ALWAYS WORK. That is, it ALWAYS solves the
problem.

>I don't use virtual
>bases and multiple inheritance just as a clever way of "sharing data".
>The concepts my classes represent clearly reflect the way I structured
>my inheritance trees.

 Yes, but you made a bad mistake. You should NOT have
put the "parent" variable in the Window class. Instead
you should have made Window a virtual base as well, and
supplied a pure virtual function to FETCH the parent variable.

 Then you just mixin the parent subobject which
contains the variable.

>
>> It is therefore my feeling that IF your code seems
>>to require any initialisation of a virtual base, you code
>>is exhibiting a design fault.
>
>As illustrated above, I don't think there is a fault in the design of my
>class structure.
>

 As commented, there is.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Tue, 30 Aug 1994 18:19:34 GMT
Raw View
In article <33r0j8$5r5@st-james.comp.vuw.ac.nz> (Rey Crisostomo) writes:
>In article <Cv6p4I.HMo@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>class Window {
>public:
>   Window(Window *parent) : parent(parent) { ... }
>private:
>   Window *parent;
>};
>
>class ComboBox : public Window {
>public:
>   ComboBox(Window *parent) : Window(parent) { ... }
>   ...
>};
>
>class DropdownComboBox : public virtual ComboBox {
>public:
>   DropdownComboBox(Window *parent) : ComboBox(parent) { ... }
>   ...
>};
>
>class EditableComboBox : public virtual ComboBox {
>public:
>   EditableComboBox(Window *parent) : ComboBox(parent) { ... }
>   ...
>};
>
>class FullComboBox : public DropdownComboBox, public EditableComboBox {
>public:
>   FullComboBox(Window *parent)
>      : ComboBox(parent), DropdownComboBox(parent), EditableComboBox(parent)
>   { ... }
>};
>
>class OwnerdrawFullCB : public FullComboBox {
>public:
>   OwnerdrawFullCB(Window *parent) : FullComboBox(parent) { ... }
>};
>
>Your solution simply does not apply to my problem.

 Yes it does. It always works.  And I know all about the
problem you are having because I had exactly the same problem
with dialog boxes myself.  Here's the solution:

class Window {
public:
//   Window(Window *parent) : parent(parent) { ... }
   Window() : parent() { ... } // no pointer
//private:
//   Window *parent;
    Window *getParent()const=0;  // replace data with method
};

//class ComboBox : public Window {
class ComboBox : public virtual Window { // use virtual derivation
public:
//   ComboBox(Window *parent) : Window(parent) { ... }
   ComboBox() : Window() { ... } // no pointer to pass
};

class DropdownComboBox : public virtual ComboBox {
public:
//   DropdownComboBox(Window *parent) : ComboBox(parent) { ... }
   DropdownComboBox() : ComboBox() { ... } // no pointer
};

class EditableComboBox : public virtual ComboBox {
 ... etc ...

Now, you see there is no longer any pointer to pass to each
virtual base, so that eliminates the problem. The method

 Window::getparent()const=0

is used to fetch the parent pointer, so you still have access to it.
The solution is MUCH BETTER CODE as well because the parent pointer
is obtained polymorphically: you might fetch it out of
the Window data via a Windows call based on the Window handle,
rather than storing it: who knows what methods you might dream up?

Oh, you may be wondering where the pointer is stored for
your particular implementation. Easy:

 class Parent : virtual Window {
  Window *parent;
  Window *getParent()const{return parent; }
 public:
  Parent(Window *p) :parent(p) {}
 };

Now you know how the parent pointer is obtained.
Notice that Window, parent and getParent are all private.
Thats fine: ONLY the constructor needs to be accessible
because you will NOT access any of this stuff after
construction. Doing so would mean "getting at" representation
details. The point of abstraction and encapsulation is
to avoid that.

But, you cry, an EditableComboBox is an ABSTRACT class,
it doesnt have a Parent base subobject!

Thats right. You have to "mix one in" WHEN you have
a concrete class for a particular kind of EditableDialogBox.
After all, an EditableDialogBox is just an abstraction:
its a virtual base so it OUGHT to be abstract.

 class  EDBox : public EditableDialogBox, Parent
 {
  EDBox(Window *p) : Parent(p) {}
 };
 EditableDialogBox * = new EDBox(parnt);
  // NOTICE we upcast IMMEDIATELY so there is NO WAY
  // to access EDBox or Parent: this guarrantees
  // representation independence if you dont
  // use any RTTI (GAK!)

And there you have the mixin: a concrete class from which
you do not derive but instantiate. IT and it alone
initialises the parent pointer into the Parent subobject
which IT and it alone knows exists.

Everything else uses polymorphism to access the parent pointer.
Which means you can change the representation WITHOUT
recoding any of your abstractions. Just mixin

 Parent2

instead of Parent:

 class Parent2 : virtual Window {
  HWND hwnd; // parent window handle
  Window *getParent()const { return lookup(hwnd); }
 public:
  Parent2(HWND p) : hwnd(p) {}
 };

There. Thats ANOTHER possible representation. You need
to choose:

 class  EDBox2 : public EditableDialogBox, Parent2
 {
  EDBox2(HWND p) : Parent2(p) {}
 };
 EditableDialogBox * = new EDBox2(parnt);
  // NOTICE we upcast IMMEDIATELY so there is NO WAY
  // to access EDBox or Parent2: this guarrantees
  // representation independence if you dont
  // use any RTTI (GAK!)

Perhaps you can begin to see the point of mixin technology.
It DOESNT mean just using Multiple Inheritance. It means
designing ABSTRACT lattices with virtual derivation,
creating a jigsaw like "circuit" board of modular
interfaces into which you can plug the "chips" of the implementations
of the various modules INDEPENDENTLY and without affecting
the operation of the system -- other than to obtain
the optimium speed/memory trade off for your application --
and of course providing appropriate specialisations.
(Burn your own chips where the available ones will not
do the job you want).

Note that using this technique does create a lot of classes.
Taken to its extreme, you get one class for every function.

Dont take it to that extreme (C++ is not designed for it).
The model above is a _guideline_: naturally in practice
you may compromise, kludge, hack etc etc. (I sure do :-)

The thing to notice is:

 a) the abstract design is HARD.

 b) once you've done the abstract design,
 implementing the "chips" is trivial and rigorously
 specified by the interface. and you can just change
 the constructors for RUNNING CODE and it will all
 keep working

 c) All the work (after the design is done) is in
 the constructors.

BTW: My dialog box code managed every keystroke and performed
ARBITRARY verification on each field, as well are loading
and storing the verified dialog fields into an arbitrary object.

The code I had to write was a routine for each field
to verify a string, parse the string into the object,
or create a string from the object.

I used a switch on a field number, and polymorphic
translation of that into the corresponding Control ID numbers:
I usually mixed in an implementation using a lookup table.
The whole thing was statically verified by the compiler
(except the field number switching, which I had
to implement by hand each time -- I should have used
an array of pointers to members to fix that)

I made the MISTAKE of putting the count of the number of
fields in a virtual base. Now I know better :-)

[I hope all this helps]


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Tue, 30 Aug 1994 18:51:03 GMT
Raw View
In article <CvD1Kn.CL2@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>And there you have the mixin: a concrete class from which
>you do not derive but instantiate. IT and it alone
>initialises the parent pointer into the Parent subobject
>which IT and it alone knows exists.
>
>Everything else uses polymorphism to access the parent pointer.
>Which means you can change the representation WITHOUT
>recoding any of your abstractions.

 And what I forgot to add: without recompiling them
either: the members are not just private but invisible.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189




Author: (Rey Crisostomo)
Date: 26 Aug 1994 05:00:38 GMT
Raw View
As you all know, virtual base classes are always initialized in the
most derived class. I understand the reasoning behind this.

The problem is, this can become a little tedious if you have a virtual
base class without a default constructor. All derived classes will have
to explicitly invoke the constructor of the virtual base class in their
own constructors.

Is it possible to relax the rule so that only derived classes that
multiply inherit from several classes be required to initialize the
virtual base class. This means that the responsibility of initializing
virtual base class(es) be put only where the inheritance hierarchy
"converge" to a single line of inheritance.

For example,

class A {
public:
   A(int x) {}     // no default ctor here.
};

class B : public virtual A {
public:
   B() : A(1) {}
};

class C : public virtual A {
public:
   C() : A(2) {}
};

class D : public B, public C { // inheritance hierarchy "converges" here.
public:
   D() : A(3), B(), C() { }
};

class E : public D {
public:
   E() : D() { }  // <-- _not_ required to initialize ancestor A
                  //   A will be initialized in D() unambigously.
};

In fact, any class that singly inherits from B or C do not have to
initialize A, ie. A will be initialized in the ctors of B and C.

Does this make sense? Or am I being too simplistic?

Rey Crisostomo
branzrpc@branz.org.nz
CompuServe:100237,2164
Wellington, New Zealand




Author: bkr@watson.ibm.com (Barry Rosen)
Date: 26 Aug 1994 20:50:33 GMT
Raw View
In article <33jstm$24g@st-james.comp.vuw.ac.nz>, (Rey Crisostomo) writes:

|> As you all know, virtual base classes are always initialized in the
|> most derived class. I understand the reasoning behind this.
|>
|> ...
|>
|> Is it possible to relax the rule so that only derived classes that
|> multiply inherit from several classes be required to initialize the
|> virtual base class. This means that the responsibility of initializing
|> virtual base class(es) be put only where the inheritance hierarchy
|> "converge" to a single line of inheritance.

Sounds good to me.  Under the present rules, deriving from a class that is
derived from a virtual base class is quite tricky and unintuitive, even when
there is no multiple inheritance involved.  Your change would make the rules
only a little more complex.  In return for this complexity, we would get
more intuitive behavior in common cases, and people using a given class as a
base would be less often burned if they forgot to check whether the given
class has any virtual ancestors.

Two possible hitches:

1.  Forgetful would-be class definers would still be burned at convergence
points.  Perhaps it is better to burn them in simple cases, while they may
still be playing around rather than coding something important and behind
schedule?

2.  In a messy hierarchy, the convergence point for virtual base class Foo
may differ from the convergence point for virtual base class Bar.  This is
not a show-stopper, but it does indicate that the proposed change is somewhat
more complex than it might look at first glance.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Barry Rosen at IBM Research (Yorktown) will receive e-mail addressed to
               bkr@watson.ibm.com

               phone : 914-945-2144    fax : 914-945-2141
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sat, 27 Aug 1994 08:05:05 GMT
Raw View
In article <33jstm$24g@st-james.comp.vuw.ac.nz> (Rey Crisostomo) writes:
>As you all know, virtual base classes are always initialized in the
>most derived class. I understand the reasoning behind this.
>
>The problem is, this can become a little tedious if you have a virtual
>base class without a default constructor. All derived classes will have
>to explicitly invoke the constructor of the virtual base class in their
>own constructors.

 This is a theoretical issue with which I have wrestled
for some time. I dont have any conclusive answers.

 However, my tentative conclusion is that virtual
bases should never need any more than a default constructor
for initialisation.  Indeed, they should probably have no
data members.

>Is it possible to relax the rule so that only derived classes that
>multiply inherit from several classes be required to initialize the
>virtual base class.

 Markku Sakkinen and others (including myself) looked
at this. (There is a summary in C++ Report). However, it really
doesnt work.

 For this reason I am drawn to the conclusion that
shared bases are shared globally and not accessed by ANY path,
but always directly. They have no local behaviour, they
cant be localised in the context of a so called "join" class.

 In particular, the following DAG is impossible to
build in C++:

  V  V
        / \        / \
       A   B      E     F
        \ /        \  /
         C         G
   \  /
   H


 It is therefore my feeling that IF your code seems
to require any initialisation of a virtual base, you code
is exhibiting a design fault.

 I want to tell you how to "get around this".
The technique is called "mixin programming".

 First, your virtual base should be an abstraction
and have no data that requires initialisation.

 Second, if you want to share some data, you
do it with OBJECT ORIENTED PROGRAMMING. <grin>

 That is, you use member functions of the virtual
base to provide access.

 Third, you provide the data as a class derived
from the virtual base and instantiated as a base of
the "mixin" class, which is the most derived class,
and is constructed "on the fly" just before you need to
create the object. This class is never accessed,
its just a temporary used to do the "mixing".

 All the other subobjects access the data THROUGH
the virtual base.  (by what Stroustrup called "sibling call")

 OK, now for the example you need to understand my waffle.

 class V  // abstract access to shared data
 {
 public:
  virtual int getInt()const=0;
 };

 class Data : private virtual V {
  int x;
  int getInt()const { return x;}
 protected:
  Data(int xx) : x(xx) {}
 };

 class User : public virtual V {
  ...
  void f() { cout <<  getInt(); }
 };

 void User *factory(int xxxx) {
  class __mixin : Data, User
  {
   __mixin(int xxx) : Data(xxx) { }
  };
  return new __mixin(xxxx);
 }


Now some comments. The "private virtual" derivation of Data,
and the "private virtual" overriding function is deliberate.
You are not MEANT to access the Data class OTHER THAN

 a) via the virtual base V
 b) directly during construction of __mixin

Note in this case that private does NOT mean "implemented
in terms of" <Barton/Nackman> but means "provides
the hidden implementation of". (I should write a book :-)

The class Data is NOT intended to be accessed by the user.
Neither, in fact, is V usually: the derivation:

 class User : protected virtual V { .. }

might well be more correct if you dont want the public getting
at the low level details of the representation.
(Even though in this cases the representation is "functional"
it might still be a representation)

These ideas might be anathema to you, and I'm sure they're
controversial but they seem to me to represent the consequences
of advanced use of the C++ virtual function and protection
system to implement notions in a maximally representation
independent (and thus reusable) way.

I suspect this shows our understanding of inheritance is
far from complete.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189