Topic: Consequence II: Virtual calls in constructors does not work


Author: phalpern@truffle.ma.ultranet.com (Pablo Halpern)
Date: 1997/04/18
Raw View
Cesar Crusius <crusius@coffee.stanford.edu> wrote:

>
>The answers for the last posting (Consequence: ...) are just assuring me
>that the standard behavior of C++ is an unecessary weakness of the
>language. To the points:

[ ... ]

>canvas::canvas(void)
>{
> initializeColors();
> initializeBorder();
> initializeBackground();
> ... 30 more calls ...
>}

[ ... ]
>
>Worse, if the base class is modified and another initialization is needed
>after some others, *all* the derived classes must be rewriten, even if
>this modification does not affect them. Now this is something.

The problem is not as bad as you say. There is a simple work-around to
prevent having to modify all of the derived classes. Simply push the
entire sequence into a function that can be called from the derived
classes:

// Normal constructor
canvas::canvas() { init(); }

// protected constructor used by dereived classes
canvas::canvas(bool doInit) { if (doInit) init(); }

void canvas::init()
{
        // Call virtual functions
 initializeColors();
 initializeBorder();
 initializeBackground();
 ... 30 more calls ...
}

derived::derived() : canvas(false) { init(); }
derived::derived(bool doInit) : canvas(false) { if (doInit) init(); }
derived::initializeColors()
{
   // override base class functionality
}

Now you only need to override those parts of the sequence which you need
to override, without repeating the whole sequence.

I, too have been a proponent of being able to call derived class virtual
functions from the base class constructor but, lacking the Java concept
of null objects, such a feature, while not useless, would be of limited
usefullness, since it could only be used when none of the derived class
members were needed. As soon as one derived-class member is needed to
make the code work, you would have to switch to a scheme like the above
anyway. In the one recent case where I wanted to call a derived class
virtual function in a constructor, it wouldn't have worked because I
*did* need the derived class members to be initialized first.

-------------------------------------------------------------
Pablo Halpern                   phalpern@truffle.ultranet.com

I am self-employed. Therefore, my opinions *do* represent
those of my employer.
---
[ 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/13
Raw View

The answers for the last posting (Consequence: ...) are just assuring me
that the standard behavior of C++ is an unecessary weakness of the
language. To the points:

1. Some said that, in order for the language to permit virtual calls from
   within constructors, some way to disable the use of uninitialized data
   should be defined. This is not necessary, since it is a duty of the
   programmer to don't do so. If a programmer uses uninitialized data, the
   detection of the error could be a headache. I agree. But if we are
   going to stay in this way we would need to forbid the use of pointers.
   An incorrect use of a pointer can lead to anything, anywhere (you can,
   for example, change the program itself by writing to the correct
   address in some machines).

Next, all the solutions to the original problem just propagate the
difficulty to a later stage. People agree that the correct place for
initializations is the constructor. But C++ make it impossible to do so in
some instances. Consider this example:

canvas::canvas(void)
{
 initializeColors();
 initializeBorder();
 initializeBackground();
 ... 30 more calls ...
}

(please just keep in mind that this is not my application but just
something to get the idea). Now let's analyze the proposed solutions:

2. Passing flags to the constructor to control the initialization doesn't
   work. Imagine, for example, that the colors must be initialized before
   the other ones. So there is no way to override just the colors
   initialization. The derived class will have to disable all
   initializations in the base class and perform them all again. A class
   derived from the derived class will behave the same way.
3. Defining an abstract base class does not work. Once a derived class
   make initializations the problem begins again if one wants to derive
   from the derived class. And every derived class would have to perform
   those 30 initializations even if just one is modified...

Worse, if the base class is modified and another initialization is needed
after some others, *all* the derived classes must be rewriten, even if
this modification does not affect them. Now this is something.

I am truly convinced that the C++ standard makes it impossible, in some
very reasonable applications, to make the initializations within the
constructors. And the constructors are the correct place to do so! The
standard try to make the language "safe", but it is a prorammer's duty.
Postergating initializations to a later stage is conceptually incorrect
(not to say inelegant). It is my feel that constructor should try to call
virtual methods from derived classes. If they will behave correctly,
that's the job of the programmer.

By the way, this reminds me that the standard for calling overloaded
virtual methods is also a weakness, but it's another thread...

Regards,

Cesar Crusius
crusius@leland.stanford.edu
---
[ 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                             ]