From 4408761521708436975
X-Google-Language: ENGLISH,ASCII-7-bit
X-Google-Thread: f78e5,3cdb654b702f7476
X-Google-Attributes: gidf78e5,public
From: Steve Clamage <stephen.clamage@sun.com>
Subject: Re: virtual functions
Date: 2000/08/18
Message-ID: <399C6D1E.937D2AD5@sun.com>#1/1
X-Deja-AN: 659692570
Content-Transfer-Encoding: 7bit
Approved: Fergus Henderson <fjh@cs.mu.oz.au>, moderator of comp.std.c++
References: <399A78E4.8345D7B@gmx.at> <m23dk5jiuq.fsf@brownie.frogger.foobar.snot> <399B8FA6.540D5B7F@gmx.at>
X-Accept-Language: en
X-Authentication-Warning: mulga.cs.mu.OZ.AU: fjh set sender to devnull@stump.algebra.com using -f
Content-Type: text/plain; charset=us-ascii
X-Complaints-To: abuse@demon.net
X-Mail2News-Path: news.demon.net!mulga.cs.mu.oz.au
X-Trace: mail2news.demon.co.uk 966598034 mail2news:4437 mail2news mail2news.demon.co.uk
Organization: Sun Microsystems
X-Robomod: STUMP, ichudov@algebra.com (Igor Chudov)
Mime-Version: 1.0
Newsgroups: comp.std.c++

"Gerhard W. Gruber" wrote:
> 
> "llewelly."@@edevnull.dot.com wrote:
> 
> > Your compiler is correct in this case; if a virtual function is called
> >   from inside a constructor, the method of that class - *not* the
> >   overriding method - is called.
> 
> Is this only true for the constructor? From your statement it appears
> so, but I'm not sure on this.

Constructor and destructor. More explanation below.

> 
> > It seems to me that a virtual initialization method such as what you
> >   want implies a close coupling between the implementation of the base
> >   class's constructor and the derived class. How could the author of
> >   the overriding virtual initialization method know what to
> >   initialize without understanding the implementation of the base
> >   class's constructor? Such tight coupling is usually poor design, as
> >   it makes derived classes likely to break when the implementation of
> >   the base class is changed.
> 
> I don't really see why this should be poor design. When I call the base
> class constcructor, I can assume that the class is properly initialized
> (That's what the constructor is for, isnt't it?). So when the base class
> constructor returns I can change part of the initialization in the
> derived class that fits the derivation. Isn't that so?

Initialization occurs at most once, by definition. You cannot initialize
a base-class member; only a base class constructor can initialize it.
You can assign a new value to a base-class member in a derived-class
constructor, but it will have already been initialized.

Going back to the original question: There are at least two reasonable
models for initialization and destruction of hierarchical objects.

1. The object always has its (most-derived) declared type. Virtual
function calls at all stages of construction or destruction yield
the function associated with the most-derived type. This is the
Java model.

2. The type of an object is the type associated with the constructor
or destructor currently running. Virtual function calls yield the
function associated with the current type.  This is the C++ model.

The C++ model has the advantage of safety. Presumably, you
have overridden a virtual function in a derived class in order to
operate on derived-class data. (That is not necessarily the case,
but it is certainly the most common situation.) If a base-class
constructor by default calls a derived-class function, it will
try to operate on data that has not yet been initialized, and
might not even have memory allocated for it yet.  Similarly,
if a base-class destructor by default calls a derived-class
function, it will try to operate on data that no longer exists.

Example:

class Base {
public:
	Base() { foo(); }
	~Base() { foo(); }
	virtual void foo(); // does something for class Base
};
class Derived : public Base {
	ofstream& file;
public:
	Derived(const char* n) : file(*new ofstream(n)) { }
	~Derived() { delete &file; }
	virtual void foo() { file << "running... "; }
};
Derived d; // what happens?

With the C++ model, you can't go wrong by accident. In the example,
the base-class version of foo gets called from the Base constructor,
and it deals with only base-class issues.

With the Java model, you have to be careful about inappropriate
running of derived-class virtual functions in all of your designs.
In the example, constructing a Derived object results in the
Base constructor trying to access a stream that hasn't been
created yet; the destructor tries to access a stream that has
already been destroyed. In both cases, no memory exists for
the "file" member to refer to.

In the (IMHO unusual) case where you truly want to run a derived-
class virtual function from a base-class constructor or destructor,
you have to design around the C++ object model.  But as llewelly
points out, you have also coupled the base and derived classes in
what is probably an undesirable way. You can probably find a better
way to factor the design, and eliminate the coupling.

-- 
Steve Clamage, stephen.clamage@sun.com

---
[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]




