Topic: Consequence: Virtual calls in constructors does not work


Author: Colin Rafferty <craffert@ml.com>
Date: 1997/04/29
Raw View
Roger L Cauvin writes:
> Colin Rafferty <craffert@ml.com> wrote:

>> As I wrote (and you quoted), there is no way for the writer of the base
>> class to know what the writer of the derived class will do, since they
>> may not be the same people.
>>
>> There is also no way to specify that a virtual function may not access
>> its non-static member variables.
>>
>> Therefore, there is no way that calling the derived class's function
>> from the base constructor can be safe.

> The absurdity of your argument is revealed when it is applied to ordinary
> virtual functions, called outside of constructors and destructors:

I disagree.  I feel that the analogy you make is misplaced.

> [begin ridiculous argument]

> An ordinary virtual function, called outside of a constructor or destructor,
> can be overridden by derived classes.

This is correct.

> There is no way for the writer of the base class to know what the writer of
> the derived class will do, since they may not be the same people.

This is correct.

> There is also no way to specify that a virtual function may not access or
> modify member variables that the base class did not intend it to modify
> safely.

This is correct.  This is why having non-private member variables in the
base class is a bad idea.

My argument, however, is about the derived class referencing its own
member variables in the virtual functions.

> In fact, there is no way to specify that a virtual function will not
> simply do something disastrous like dereferencing a null pointer.

You are correct; it is the derived class's responsibility to maintain
the integrity of its own member variables, and to not do simething
ridiculous.

However, this argument is not a valid comparison because, before the
constructor for the derived class is called, its member variables have
not been initialized.  There is no way, given invalid data, for the
virtual function to work correctly.

Calling a virtual member function from a base class constructor is the
exact equivalent of the following code:

    Class* c = (Class*) new char[sizeof(Class)];
    c->mfn();

If Class::mfn does not access its member variables, then this is
safe[1].

If Class::mfn uses its member variables at all, then this will break
horribly.

> Therefore, there is no way that calling the derived class's function from
> *any* base class member function can be safe.

> [end ridiculous argument]

I am glad that we both agree this is ridiculous.  For different reasons.

> The premise that polymorphic dispatch is only valid when the base class can
> guarantee the safety of derived class overrides would entail that *all*
> virtual function calls should be short-circuited, whether or not they reside
> in a constructor or destructor.

Not at all.  The only thing that the base class needs to ensure is that
the derived class actually exists.  Until its constructor is called,
this is not the case.

By the way, as I mentioned in an earlier reply[2] to an earlier article
of yours[3], Java is able to deal with this because it initializes the
member variables of the derived class to null references, and throws an
exception if the null reference is dereferenced.  Since C++ has direct
objects and not references, there is no way for C++ to do this.

--
Colin

Footnotes:
[1]  Actually, it is undefined, but I won't quibble.

[2]  Message-ID: <ocrbu7mhemm.fsf@spssunp.spspme.ml.com>

[3]  Message-ID: <01bc44ed$a2d5f4c0$e60ca482@tecra.natinst.com>
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/04/30
Raw View
Colin Rafferty <craffert@ml.com> writes:

 |>  Roger L Cauvin writes:
 |>  > Colin Rafferty <craffert@ml.com> wrote:
 |>
 |>  >> As I wrote (and you quoted), there is no way for the writer of the base
 |>  >> class to know what the writer of the derived class will do, since they
 |>  >> may not be the same people.
 |>  >>
 |>  >> There is also no way to specify that a virtual function may not access
 |>  >> its non-static member variables.
 |>  >>
 |>  >> Therefore, there is no way that calling the derived class's function
 |>  >> from the base constructor can be safe.
 |>
 |>  > The absurdity of your argument is revealed when it is applied to ordinary
 |>  > virtual functions, called outside of constructors and destructors:
 |>
 |>  I disagree.  I feel that the analogy you make is misplaced.
 |>
 |>  > [begin ridiculous argument]
 |>
 |>  > An ordinary virtual function, called outside of a constructor or
 |>  > destructor, can be overridden by derived classes.
 |>
 |>  This is correct.
 |>
 |>  > There is no way for the writer of the base class to know what the writer
 |>  > of the derived class will do, since they may not be the same people.
 |>
 |>  This is correct.
 |>
 |>  > There is also no way to specify that a virtual function may not access or
 |>  > modify member variables that the base class did not intend it to modify
 |>  > safely.
 |>
 |>  This is correct.  This is why having non-private member variables in the
 |>  base class is a bad idea.
 |>
 |>  My argument, however, is about the derived class referencing its own
 |>  member variables in the virtual functions.
 |>
 |>  > In fact, there is no way to specify that a virtual function will not
 |>  > simply do something disastrous like dereferencing a null pointer.
 |>
 |>  You are correct; it is the derived class's responsibility to maintain
 |>  the integrity of its own member variables, and to not do simething
 |>  ridiculous.

The key to the argument is that the derived class will know whether it
can call f from its constructor or not, according to how far it has
gotten in its initialization.  And f can consider that all base classes
and member variables have been "constructed".  If the base class can
call f from its constructor, however, it has no guarantees, and no way
of knowing whether the variables are initialized or not.

The difference is particularly flagrant with regards to base classes: if
D derives from M which derives from B, D can (today) assume that M has
been fully constructed in all of its functions.  Allowing the
constructor of B to call a function in D breaks this guarantee.

And of course, D cannot know a priori which virtual functions B will
call in its constructor.

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Roger L. Cauvin" <rcauvin@homemail.com>
Date: 1997/04/30
Raw View
Colin Rafferty <craffert@ml.com> wrote in article
<ocriv15pz84.fsf@spssunp.spspme.ml.com>...
> Roger L Cauvin writes:
> > Colin Rafferty <craffert@ml.com> wrote:
>
> >> As I wrote (and you quoted), there is no way for the writer of the base
> >> class to know what the writer of the derived class will do, since they
> >> may not be the same people.
> >>
> >> There is also no way to specify that a virtual function may not access
> >> its non-static member variables.
> >>
> >> Therefore, there is no way that calling the derived class's function
> >> from the base constructor can be safe.
>
> > The absurdity of your argument is revealed when it is applied to ordinary
> > virtual functions, called outside of constructors and destructors:
>
> I disagree.  I feel that the analogy you make is misplaced.

You argued that allowing dynamic dispatch of virtual function calls during
construction of an object is a bad idea because "there is no way that calling
the derived class's function from the base constructor can be safe."  The
analogy showed that this exact same line of reasoning would entail that
dynamic binding of virtual function calls should *never* be allowed, even
outside of constructors and destructors.  Therefore, your argument is
incomplete or based on flawed premises.  If you would like to amend your
argument, please do so.

> > [begin ridiculous argument]
>
> > An ordinary virtual function, called outside of a constructor or
destructor,
> > can be overridden by derived classes.
>
> This is correct.
>
> > There is no way for the writer of the base class to know what the writer
of
> > the derived class will do, since they may not be the same people.
>
> This is correct.
>
> > There is also no way to specify that a virtual function may not access or
> > modify member variables that the base class did not intend it to modify
> > safely.
>
> This is correct.  This is why having non-private member variables in the
> base class is a bad idea.

Ah, but you've forgotten about protected accessor functions that may be
provided by the base class.

> My argument, however, is about the derived class referencing its own
> member variables in the virtual functions.

Your argument was that the derived class referencing member variables was
unsafe, and that it was this lack of safety that justifies the lack of
inherent polymorphism during object construction.  I showed that derived class
overrides of virtual functions can never be guaranteed to be safe, which, by
your reasoning, would justify removing inherent polymorphism entirely from the
C++ language.

> > In fact, there is no way to specify that a virtual function will not
> > simply do something disastrous like dereferencing a null pointer.
>
> You are correct; it is the derived class's responsibility to maintain
> the integrity of its own member variables, and to not do simething
> ridiculous.

It is also the derived class function implementation's responsibility to
conform to the expectations of the base class function that calls it.

> However, this argument is not a valid comparison because, before the
> constructor for the derived class is called, its member variables have
> not been initialized.  There is no way, given invalid data, for the
> virtual function to work correctly.

Yes, but the derived class's implementation of a virtual function need not
access any member variables.  Indeed, just as "it is the derived class's
responsibility to maintain the integrity of its own member variables" during
an ordinary function call, we can stipulate that it is the derived class's
responsibility not to reference its own member variables in virtual functions
called by the base class constructor.  Both in the ordinary case of a virtual
function call (outside of a constructor or destructor), and the case in which
a virtual function is called from within a base class constructor, the derived
class is subject to certain restrictions.  The restrictions are simply more
stringent in the case of the constructor virtual function call.

[ snip ]

> > Therefore, there is no way that calling the derived class's function from
> > *any* base class member function can be safe.
>
> > [end ridiculous argument]
>
> I am glad that we both agree this is ridiculous.  For different reasons.
>
> > The premise that polymorphic dispatch is only valid when the base class
can
> > guarantee the safety of derived class overrides would entail that *all*
> > virtual function calls should be short-circuited, whether or not they
reside
> > in a constructor or destructor.
>
> Not at all.  The only thing that the base class needs to ensure is that
> the derived class actually exists.  Until its constructor is called,
> this is not the case.

Incorrect.  In the general case of virtual functions being called (outside of
constructors and destructors), derived class overrides must conform to the
expectations of the base class function that calls them.  Hence it is *always*
theoretically possible that a virtual function call will be unsafe.

> By the way, as I mentioned in an earlier reply[2] to an earlier article
> of yours[3], Java is able to deal with this because it initializes the
> member variables of the derived class to null references, and throws an
> exception if the null reference is dereferenced.  Since C++ has direct
> objects and not references, there is no way for C++ to do this.

The best solution, in my view, would be for construction to occur as follows:

1.  Initialize virtual tables and members from the bottom up.
2.  Execute constructor bodies from the bottom up.

The first step ensures that all members are initialized before they are
accessed.  The second step ensures that virtual function calls behave
polymorphically during object construction.

---

Roger L. Cauvin
rcauvin@homemail.com
Software Engineer
National Instruments
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Paul Campbell <mti.mti.sgi.com!odin.corp.sgi.com!cardboard.mti.sgi.com!sgi.com!lucent.com!remove_this_bit!Paul_Campbell__Mr>
Date: 1997/04/23
Raw View
Colin Rafferty wrote:
>
> Roger L Cauvin writes:
> > Bjorn Fahller <Bjorn.Fahller@ebc.ericsson.se> wrote in article <334B454B.3D5E@ebc.ericsson.se>...
> >> Cesar Crusius wrote:
> >> >
> >> > First, thanks everyone who replied my question. The answers revealed what
> >> > I think is a weakness of C++ (constructors do not call virtual
> >> > methods). To see this imagine this simple example:
>
> >> I don't see a simple way around this. Constructing from the
> >> specialisations towards the base is definitely not a good
> >> idea.
>
> > You gave an example of a polymorphic virtual function call from a constructor
> > that wasn't a good idea.  Cesar gave an example of one that *was* a good idea.
> >  Shouldn't it be the programmer's choice?
>
> > Yes, it is possible to get into trouble by calling derived class member
> > functions that rely on uninitialized data.  It would be nice, however, if C++
> > gave the programmer the freedom during object construction to call derived
> > class member functions that do *not* rely on uninitialized data.
>
> However, there is no way that the programmer of the base class can know
> what the programmer of the derived class will do.
>
> More importantly, there is no way for the programmer of the base class
> to force the programmer of the derived class to *not* access its member
> variables.

I seems to me that that there is one clear cut case where calling an
overwridden virtual function within a ctor *should* be perfectly
legit: where the derived class function accesses static members only.

How many of us have misguidedly tried in our early C++ days to write
a generic object construction/destruction tracing mechanism something
like:

class ObjectLifeTrace
{
public:
 ObjectLifeTrace()
 {
  cout << "object " <<  of class " <<
   get_most_derived_class_name() <<
   " created" << endl;
 }
 ~ObjectLifeTrace()
 {
  cout << "object " <<  of class " <<
   get_most_derived_class_name() <<
   " destroyed" << endl;
 }

 virtual /* static ? */ const char *  get_most_derived_class_name() = 0;
};

class derivedCl : public ObjectLifeTrace
{
 const char *  get_most_derived_class_name() { return "derivedCl"; }
}

Obviously this won't work in its current form (even an explicit call to
derivedCl::get_most_derived_class_name will fail) but what if static
virtuals were allowed ?.
Admittedly I can't think of any other use for them at the moment but it
would be nice to have the option.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Colin Rafferty <craffert@ml.com>
Date: 1997/04/28
Raw View
Paul Campbell writes:
> Colin Rafferty wrote:
>> Roger L Cauvin writes:
>>
>>> Yes, it is possible to get into trouble by calling derived class
>>> member functions that rely on uninitialized data.  It would be nice,
>>> however, if C++ gave the programmer the freedom during object
>>> construction to call derived class member functions that do *not*
>>> rely on uninitialized data.
>>
>> However, there is no way that the programmer of the base class can know
>> what the programmer of the derived class will do.
>>
>> More importantly, there is no way for the programmer of the base class
>> to force the programmer of the derived class to *not* access its member
>> variables.
>
> I seems to me that that there is one clear cut case where calling an
> overwridden virtual function within a ctor *should* be perfectly
> legit: where the derived class function accesses static members only.

As I wrote (and you quoted), there is no way for the writer of the base
class to know what the writer of the derived class will do, since they
may not be the same people.

There is also no way to specify that a virtual function may not access
its non-static member variables.

Therefore, there is no way that calling the derived class's function
from the base constructor can be safe.

--
Colin
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Gerard Weatherby <gerardw@alum.mit.edu>
Date: 1997/04/28
Raw View
Paul Campbell wrote:
How many of us have misguidedly tried in our early C++ days to write
a generic object construction/destruction tracing mechanism something
like:

class ObjectLifeTrace
{
public:
        ObjectLifeTrace()
        {
                cout << "object " <<  of class " <<
                        get_most_derived_class_name() <<
                        " created" << endl;
        }
        ~ObjectLifeTrace()
        {
                cout << "object " <<  of class " <<
                        get_most_derived_class_name() <<
                        " destroyed" << endl;
        }

        virtual /* static ? */ const char *
get_most_derived_class_name() = 0;
};

class derivedCl : public ObjectLifeTrace
{
        const char *  get_most_derived_class_name() { return
"derivedCl"; }
}

Obviously this won't work in its current form (even an explicit call to
derivedCl::get_most_derived_class_name will fail) but what if static
virtuals were allowed ?.
Admittedly I can't think of any other use for them at the moment but it
would be nice to have the option.
----------------------------------------

You can get the functionality with the current language definition:

class ObjectLifeTrace
{
public:
 ObjectLifeTrace(const char *dname)
  :derivedName(dname)
 {
   cout << "object of class " <<
   get_most_derived_class_name() <<
   " created" << endl;
 }
 ~ObjectLifeTrace()
 {
   cout << "object of class " <<
   get_most_derived_class_name() <<
   " destroyed" << endl;
 }

 virtual const char *  get_most_derived_class_name()
  { return derivedName; }
private:
  const char * const derivedName;
};

class derivedCl : public ObjectLifeTrace
{
  public:
    derivedCl( ) :ObjectLifeTrace(ourName) {}

  static const char *const ourName;
};

const char * const derivedCl::ourName = "derivedCl";
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Roger L. Cauvin" <rcauvin@homemail.com>
Date: 1997/04/29
Raw View

Colin Rafferty <craffert@ml.com> wrote in article
<ocr207zq6kn.fsf@spssunp.spspme.ml.com>...
>
> As I wrote (and you quoted), there is no way for the writer of the base
> class to know what the writer of the derived class will do, since they
> may not be the same people.
>
> There is also no way to specify that a virtual function may not access
> its non-static member variables.
>
> Therefore, there is no way that calling the derived class's function
> from the base constructor can be safe.

The absurdity of your argument is revealed when it is applied to ordinary
virtual functions, called outside of constructors and destructors:

[begin ridiculous argument]

An ordinary virtual function, called outside of a constructor or destructor,
can be overridden by derived classes.

There is no way for the writer of the base class to know what the writer of
the derived class will do, since they may not be the same people.

There is also no way to specify that a virtual function may not access or
modify member variables that the base class did not intend it to modify
safely.  In fact, there is no way to specify that a virtual function will not
simply do something disastrous like dereferencing a null pointer.

Therefore, there is no way that calling the derived class's function from
*any* base class member function can be safe.

[end ridiculous argument]

The premise that polymorphic dispatch is only valid when the base class can
guarantee the safety of derived class overrides would entail that *all*
virtual function calls should be short-circuited, whether or not they reside
in a constructor or destructor.

---

Roger L. Cauvin
rcauvin@homemail.com
Software Engineer
National Instruments
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Paul D. DeRocco" <strip_these_words_pderocco@ix.netcom.com>
Date: 1997/04/12
Raw View
Cesar Crusius wrote:
>
> First, thanks everyone who replied my question. The answers revealed what
> I think is a weakness of C++ (constructors do not call virtual
> methods). To see this imagine this simple example:
>
> ---------------------------------------------------------------
> class canvas
> {
> public:
>         canvas();
>         virtual ~canvas();
>         virtual void initializeColors();
> };
>
> canvas::canvas() { initializeColors(); }
> ---------------------------------------------------------------
>
> where initializeColors initialize the canvas for a 256 color video (not my
> application, by the way). This is a simple example, but imagine there are
> many initializatios taking place in the constructor. I provide the class
> "canvas" in some library. Now other person wants to derive a 2-color
> canvas. The logic way should be
>
> ---------------------------------------------------------------
> class monochrome::public canvas
> {
> public:
>         monochrome() {};
>         virtual ~monochrome();
>         virtual void initializeColors();
> };
>
> void monochrome::initializeColors()
> { ... initialize colors other way ... }
> ---------------------------------------------------------------
>
> Now declaring "monochrome myCanvas" won't work because the constructor
> would not call the base class method. As I said, this is a simple example,
> to get the picture one need to imagine a lot of initializations taking
> place at the "canvas" constructor.

I think the behavior of C++ is correct.

It appears that what you are trying to do is to put something into the
base class that requires each derived class to call its _own_
initializeColors function. But it isn't logical to call a derived
class's initializeColors function until the object actually _is_ an
instance of that derived class, which isn't the case until it's
completed the base class constructor and started the derived class
constructor.

This is an understandable impulse. You want to be able to say, "invoke
your own initializeColors function" just once, so it seems natural to
put it into the base class constructor, so you don't have to retype it
in every derived class. But in the general case, that's the wrong time
to call the function. In some specific cases, it may be harmless to do
that, but in the general case, it's not, so allowing the base class
constructor to call a derived class virtual function isn't a reasonable
default behavior.

There's no way to do what you want to do entirely. However, you could
define a virtual init() function in your base class, which all creators
of objects are required to invoke after creating the object. There has
been some discussion in the past of having the language define such a
special member function, and automatically call it whenever the most
derived constructor is complete. But that's not part of the language
yet, and that enhancement doesn't seem imminent.

--

Ciao,
Paul D. DeRocco

(Please send e-mail to mail:pderocco@ix.netcom.com instead of the
return address, which has been altered to foil junk mail senders.)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/04/13
Raw View

Colin Rafferty <craffert@ml.com> writes:

>Roger L Cauvin writes:
>> It's no
>> accident that Java allows such polymorphism during object construction.
>
>This is a deficiency of Java.

I don't think so.  There are two different possible design decisions.
Each has its advantages and disadvantages.

The failure mode of the C++ design is that it calls the "wrong" virtual
function.  The failure mode of the Java design decision is that the
program may accesses uninitialied data.  Since Java initializes all
members to null or zero before entering the constructor, and since Java
guarantees to trap dereferencing of null, the cost of failure in the
Java case is less than it would be in C++ if C++ had adopted the Java
design decision.  The C++ design decision certainly seems to confuse
quite a few people.  I myself once spent an hour helping someone debug
a problem that turned out to be due to this.  I suspect that the Java
failure mode would be less confusing.

Of course, looking at the costs of failure is not enough, you also have
to look at the probability of failure.  Which of the two design
decisions will most likely lead to fewer failures is arguable, and I
don't have any strong evidence either way.

>My guess is that this will be fixed by the time Java is standardized.

I think it is extremely unlikely that this aspect of Java will be changed.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/04/13
Raw View
Cesar Crusius <crusius@coffee.stanford.edu> writes:

 > First, thanks everyone who replied my question. The answers revealed what
 > I think is a weakness of C++ (constructors do not call virtual
 > methods). To see this imagine this simple example:
 >
 > ---------------------------------------------------------------
 > class canvas
 > {
 > public:
 >  canvas();
 >  virtual ~canvas();
 >  virtual void initializeColors();
 > };
 >
 > canvas::canvas() { initializeColors(); }
 > ---------------------------------------------------------------
 >
 > where initializeColors initialize the canvas for a 256 color video (not my
 > application, by the way). This is a simple example, but imagine there are
 > many initializatios taking place in the constructor. I provide the class
 > "canvas" in some library. Now other person wants to derive a 2-color
 > canvas. The logic way should be
 >
 > ---------------------------------------------------------------
 > class monochrome::public canvas
 > {
 > public:
 >  monochrome() {};
 >  virtual ~monochrome();
 >  virtual void initializeColors();
 > };
 >
 > void monochrome::initializeColors()
 > { ... initialize colors other way ... }
 > ---------------------------------------------------------------

Simply use a static virtual function !

(Since there are static, they can be called with the most
derived type.)

But don't try it with g++ since it doesn't exist in C++
(virtual and static are mutually exclusive); and I'm against
different rules for static vs normal virtual functions
anyway. (It's tricky and difficult (impossible) to implement
without some overhead.)

--
Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Roger L. Cauvin" <rcauvin@homemail.com>
Date: 1997/04/09
Raw View
Bjorn Fahller <Bjorn.Fahller@ebc.ericsson.se> wrote in article
<334B454B.3D5E@ebc.ericsson.se>...
> Cesar Crusius wrote:
> >
> > First, thanks everyone who replied my question. The answers revealed what
> > I think is a weakness of C++ (constructors do not call virtual
> > methods). To see this imagine this simple example:

... snip ...

> I don't see a simple way around this. Constructing from the
> specialisations towards the base is definitely not a good
> idea.

You gave an example of a polymorphic virtual function call from a constructor
that wasn't a good idea.  Cesar gave an example of one that *was* a good idea.
 Shouldn't it be the programmer's choice?

Yes, it is possible to get into trouble by calling derived class member
functions that rely on uninitialized data.  It would be nice, however, if C++
gave the programmer the freedom during object construction to call derived
class member functions that do *not* rely on uninitialized data.  It's no
accident that Java allows such polymorphism during object construction.

---

Roger L. Cauvin
rcauvin@homemail.com
Software Engineer
National Instruments
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: jim.hyslop@leitch.com (Jim Hyslop)
Date: 1997/04/10
Raw View

In article <tlciv1xxtq7.fsf@coffee.Stanford.EDU>,
crusius@coffee.stanford.edu says...
> 1) The constructor is the logical and intuitive place to make
>    initializations, so postponing the initializations to a later stage is
>    not the correct thing to do, in my point of view.
I can see your point.  It does seem un-intuitive to delay
initialization to later - especially if you want to have references or
const members in your class.

> 2) Declaring an abstract base class is also not the correct way to do it
>    (IMHO) because, if we want to leave room for modifications in the
>    initialization process, the base class constructor should be empty,
>    and for each derived class the programmer would need to perform all
>    the initializations, even if they are the "default". It becomes worse
>    when the base class need to be modified and extra initializations
>    become needed. In this case, the initializations cannot be overrided,
>    unless we require all derived classes to be rewriten.
I don't think it's as difficult as you make it sound.  Simply call the
base class initializer from your derived class.  And have some sort of
flag to prevent multiple initializations of one object.

You provide class Base:
class Base
{
public:
   Base();
   virtual ~Base();
   virtual int Initialize(); // 0==OK, non-0==ERROR
...
private:
   int x;
   bool Initialized;
};

Base::Base()
{
   Initialized=false;
}

int Base::Initialize()
{
   int ErrorCode = 0;
   if (!Initialized)
   {
      // do initialization stuff
      Initialized=true;
   }
   return ErrorCode;
}

I derive from it:

class Derived:public Base
{
public:
   Derived();
   virtual int Initialize();
...
};

int Derived::Initialize()
{
   int retval=Base::Initialize();
   if (retval ==0)
   {
      // do Derived initialization
   }
   return retval;
}

OK, now you go ahead and do whatever you want to Base::Initialize(),
and to the public interface of Base.  At most I'll need to recompile
Derived and relink - no re-writing of my code is required.

> 3) Last, one can always ensure that the base class method is called,
>    namely by writing "canvas::initializeColors()". But, with this stantard
>    for C++, one can never call the derived class method from the
>    constructor, so there is a (unecessary) loss of flexibility introduced.
I agree, this is a limitation of the language.  The alternative,
though, would be to set up the virtual handlers for each class, then
call the constructors for the classes; I don't know what the
ramifications of that would be (in terms of overhead - obviously that
would be a *major* change in the language specifications and
potentially break lots of code).

--
Jim Hyslop
jim.hyslop@leitch.com
Children have an innate understanding of statistics.  When they report
"Everyone is doing it," their findings are usually based on a survey
of one.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: dacut@henry.ece.cmu.edu (David A. Cuthbert)
Date: 1997/04/10
Raw View
Cesar Crusius  <crusius@coffee.stanford.edu> wrote:
>class canvas
>{
>public:
> canvas();
> virtual ~canvas();
> virtual void initializeColors();
>};
>
>canvas::canvas() { initializeColors(); }
>---------------------------------------------------------------
>class monochrome::public canvas
>{
>public:
> monochrome() {};
> virtual ~monochrome();
> virtual void initializeColors();
>};
>
>void monochrome::initializeColors()
>{ ... initialize colors other way ... }
>---------------------------------------------------------------

Any problems with using the following instead?

class canvas {
 public:
 canvas() { initializeColors(); }

 protected:
 canvas(bool initColors) {
  if(initColors)
   initializeColors();
 }

 public:
 virtual void  initializeColors();
};

class monochrome : public canvas {
 public:
 monochrome() : canvas(false) { initializeColors(); }

 protected:
 monochrome(bool initColors) : canvas(false) {
  if(initColors)
   initializeColors();
 }

 public:
 virtual void initializeColors();
};

>1) The constructor is the logical and intuitive place to make
>   initializations, so postponing the initializations to a later stage is
>   not the correct thing to do, in my point of view.

Right; the state of your object should always be valid from
construction to destruction.

>2) Declaring an abstract base class is also not the correct way to do it
>   (IMHO) because, if we want to leave room for modifications in the
>   initialization process, the base class constructor should be empty,
>   and for each derived class the programmer would need to perform all
>   the initializations, even if they are the "default". It becomes worse
>   when the base class need to be modified and extra initializations
>   become needed. In this case, the initializations cannot be overrided,
>   unless we require all derived classes to be rewriten.

See my above solution.

In short, you're adding a lot of "state" to your objects.  Your
original base class assumed that it is the one responsible for
initializing the palette.  In reality, this is the responsibility for
the most derived class.

This isn't very unusual.  Consider the following:

class Base {
 public:
 virtual Base&  operator = (Base const&) { /* stuff */ }
};

class DerivedLeft : public virtual Base {
 public:
 virtual DerivedLeft&  operator = (DerivedLeft const&) {
  /* stuff */
  Base::operator = (*this);
  return *this;
 }
};

class DerivedRight : public virtual Base {
 public:
 virtual DerivedRight&  operator = (DerivedRight const&) {
  /* stuff */
  Base::operator = (*this);
  return *this;
 }
};

class Leaf : public virtual DerivedLeft, DerivedRight {
 pubilc:
 virtual Leaf&  operator = (Leaf const&) {
  /* stuff */
  DerivedLeft::operator = (*this);
  DerivedRight::operator = (*this);
  return *this;
 }
};

In this case, Leaf::operator = () makes the mistake of calling
Base::operator = () twice.  Depending upon the function, the resulting
behavior can range from a simple inefficiency to disasterous bugs.

(The solution here is to use protected methods.)

>3) Last, one can always ensure that the base class method is called,
>   namely by writing "canvas::initializeColors()". But, with this stantard
>   for C++, one can never call the derived class method from the
>   constructor, so there is a (unecessary) loss of flexibility
>   introduced.

And the canvas:: qualifier is unnecessary in the canvas class, anyway.
--
David A. Cuthbert (henry.ece.cmu.edu!dacut)
Graduate Student, Electrical and Computer Engineering
Data Storage Systems Center, Carnegie Mellon University
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Colin Rafferty <craffert@ml.com>
Date: 1997/04/10
Raw View
Roger L Cauvin writes:
> Bjorn Fahller <Bjorn.Fahller@ebc.ericsson.se> wrote in article <334B454B.3D5E@ebc.ericsson.se>...
>> Cesar Crusius wrote:
>> >
>> > First, thanks everyone who replied my question. The answers revealed what
>> > I think is a weakness of C++ (constructors do not call virtual
>> > methods). To see this imagine this simple example:

>> I don't see a simple way around this. Constructing from the
>> specialisations towards the base is definitely not a good
>> idea.

> You gave an example of a polymorphic virtual function call from a constructor
> that wasn't a good idea.  Cesar gave an example of one that *was* a good idea.
>  Shouldn't it be the programmer's choice?

> Yes, it is possible to get into trouble by calling derived class member
> functions that rely on uninitialized data.  It would be nice, however, if C++
> gave the programmer the freedom during object construction to call derived
> class member functions that do *not* rely on uninitialized data.

However, there is no way that the programmer of the base class can know
what the programmer of the derived class will do.

More importantly, there is no way for the programmer of the base class
to force the programmer of the derived class to *not* access its member
variables.

The only safe way to allow this is to have some way of specifying in the
signature of the called function that the derived classes are not
allowed to access data.  Otherwise, this will cause errors that are very
difficult to find.

If the constructor of your base class needs information from its derived
classes, then you should add an argument to the base class's constructor.

If your base class needs to send information to its derived classes,
then you need to rethink your design.

> It's no
> accident that Java allows such polymorphism during object construction.

This is a deficiency of Java.  Similar problems exist, except that the
Java runtime system will throw() for accessing an unitialized object.
This is better than the undefined results in C++, but not by much.

My guess is that this will be fixed by the time Java is standardized.

--
Colin
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Cesar Crusius <crusius@coffee.stanford.edu>
Date: 1997/04/08
Raw View
First, thanks everyone who replied my question. The answers revealed what
I think is a weakness of C++ (constructors do not call virtual
methods). To see this imagine this simple example:

---------------------------------------------------------------
class canvas
{
public:
 canvas();
 virtual ~canvas();
 virtual void initializeColors();
};

canvas::canvas() { initializeColors(); }
---------------------------------------------------------------

where initializeColors initialize the canvas for a 256 color video (not my
application, by the way). This is a simple example, but imagine there are
many initializatios taking place in the constructor. I provide the class
"canvas" in some library. Now other person wants to derive a 2-color
canvas. The logic way should be

---------------------------------------------------------------
class monochrome::public canvas
{
public:
 monochrome() {};
 virtual ~monochrome();
 virtual void initializeColors();
};

void monochrome::initializeColors()
{ ... initialize colors other way ... }
---------------------------------------------------------------

Now declaring "monochrome myCanvas" won't work because the constructor
would not call the base class method. As I said, this is a simple example,
to get the picture one need to imagine a lot of initializations taking
place at the "canvas" constructor. Now to the cons I find in this
behavior:

1) The constructor is the logical and intuitive place to make
   initializations, so postponing the initializations to a later stage is
   not the correct thing to do, in my point of view.
2) Declaring an abstract base class is also not the correct way to do it
   (IMHO) because, if we want to leave room for modifications in the
   initialization process, the base class constructor should be empty,
   and for each derived class the programmer would need to perform all
   the initializations, even if they are the "default". It becomes worse
   when the base class need to be modified and extra initializations
   become needed. In this case, the initializations cannot be overrided,
   unless we require all derived classes to be rewriten.
3) Last, one can always ensure that the base class method is called,
   namely by writing "canvas::initializeColors()". But, with this stantard
   for C++, one can never call the derived class method from the
   constructor, so there is a (unecessary) loss of flexibility introduced.


Regards,

Cesar Crusius
crusius@leland.stanford.edu
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Bjorn Fahller <Bjorn.Fahller@ebc.ericsson.se>
Date: 1997/04/09
Raw View
Cesar Crusius wrote:
>
> First, thanks everyone who replied my question. The answers revealed what
> I think is a weakness of C++ (constructors do not call virtual
> methods). To see this imagine this simple example:

The logic behind this behaviour is simple.

Suppose this:

#include <iostream.h>

class A
{
public:
  A(void) { method(); };
  virtual void method();
};

class B : public A
{
public:
  B(void) : data(0) {};
  virtual void method() { cout << data << endl; };
private:
  int data;
};

int main(void)
{
  B b;
  return 0;
}

What does this program print? If A's constructor would call
B::method, the value of B::data would be undefined, since
B's constructor would not yet have run. Construction is always
done from the base and towards the specialisations. This also
means that if B::method would set B::data to something, and
B::method was called from A::A, B::B would then overwrite
B::data.

I don't see a simple way around this. Constructing from the
specialisations towards the base is definitely not a good
idea.
   _
/Bjorn.
--
Bjorn Fahller                  Tel: +46 8 4220898 /
NA/EBC/DN/NT                   -------------------
Ericsson Business Networks AB / A polar bear is a rectangular
S-131 89 Stockholm/SWEDEN    /  bear after a coordinate transform
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]