Topic: Reference initialization question -- is it safe?
Author: jk@steel.orel.ru (Eugene Karpachov)
Date: 19 Mar 01 22:12:53 GMT Raw View
16 Mar 01 22:08:39 GMT James Kanze :
>In the special case you cite, I don't see a problem, IF we consider
>that the constructor has been entered as soon as the member
>initializers have started; the implicit conversion is of an already
>constructed base.
I always use such a technique because it seems intuitive to me; it is obvious
that base class object is fully constructed when we are in derived constructor
body. But now I'm confused by the standard. It seems that the standard is
completely useless about that.
It still unclear to me - can I use "Dietmar Kuehl trick" about inheritance as
I always did? Isn't it directly forbidden by the standard? Is calling base
member function from constructor body directly forbidden by the standard?
Excuse my bad English.
--
jk
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: "James Kuyper Jr." <kuyper@wizard.net>
Date: 26 Mar 01 23:03:35 GMT Raw View
Hiram Berry wrote:
...
> in order to disambiguate the question. If there are competing
> interpretations, the alternative which leads to the least amount of
> restriction should always be chosen as the correct one.
Least amount of restriction for whom? The standard balances the
implementor's freedom vs. the developer's freedom. In general, if you
give the implementor more freedom, the programmer has less, and vice
versa. For instance, the implementor has the freedom to choose the most
appropriate type for 'int' for a given platform - that means that the
programmer can't make any assumptions about what size 'int' is; the size
must either be checked, or rendered irrelevant by carefully chosen code.
As another example, the programmer is free to choose any name that isn't
reserved in the context in which it's used - which means that the
standard headers must be written so as to not use such names.
As has been pointed out - the proper way to resolve true ambiguities is
to file a DR, not to invent disambiguation rules. For one thing, if you
don't raise a DR, there's a chance the committee won't even think about
the problem, much less resolve it.
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: "Siemel Naran" <namespace@KILL.excite.com>
Date: 21 Mar 2001 07:04:41 -0500 Raw View
"Eugene Karpachov" <jk@steel.orel.ru> wrote in message
> It still unclear to me - can I use "Dietmar Kuehl trick" about inheritance
as
> I always did? Isn't it directly forbidden by the standard? Is calling base
> member function from constructor body directly forbidden by the standard?
Yes, Dietmar Kuehl's trick about inheritance works, as always. To
summarize, it is
class Derived : private Variable, public Base {
Derived(...) : Variable(...), Base(...) { }
};
Because we derive from Variable first, it is initialized first. Therefore
we can pass it -- either a reference to the Variable or a pointer to the
Variable -- down to the base class constructor. This constructor can use
the Variable, and safely store the reference/pointer.
Calling a base class member function from the derived class initialization
list or constructor body is allowed. But it is not in question here, if I'm
not mistaken.
--
+++++++++++
Siemel Naran
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: James Kanze <James.Kanze@dresdner-bank.com>
Date: 22 Mar 01 08:56:34 GMT Raw View
Eugene Karpachov wrote:
> 16 Mar 01 22:08:39 GMT James Kanze :
> >In the special case you cite, I don't see a problem, IF we consider
> >that the constructor has been entered as soon as the member
> >initializers have started; the implicit conversion is of an already
> >constructed base.
> I always use such a technique because it seems intuitive to me; it
> is obvious that base class object is fully constructed when we are
> in derived constructor body. But now I'm confused by the
> standard. It seems that the standard is completely useless about
> that.
> It still unclear to me - can I use "Dietmar Kuehl trick" about
> inheritance as I always did? Isn't it directly forbidden by the
> standard? Is calling base member function from constructor body
> directly forbidden by the standard?
The problem is that the standard isn't really clear. The conversion
isn't valid (officially) until the constructor of the derived class
has been entered, but there is nothing to clearly say whether the
initializers are part of the constructor or not.
In practice, I'm going to continue to use it. The compiler MUST be
able to make the conversion in order to call the constructor of the
base class. It would be perverse for it not to convert correctly in
other cases.
--
James Kanze mailto:kanze@gabi-soft.de
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelh ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: "Hiram Berry" <burningb@burningbridges.com>
Date: 23 Mar 2001 22:38:31 -0500 Raw View
"James Kanze" <James.Kanze@dresdner-bank.com> wrote in message
news:3AB7119B.6C6BF4D1@dresdner-bank.com...
> Eugene Karpachov wrote:
> > It still unclear to me - can I use "Dietmar Kuehl trick" about
> > inheritance as I always did? Isn't it directly forbidden by the
> > standard? Is calling base member function from constructor body
> > directly forbidden by the standard?
>
> The problem is that the standard isn't really clear. The conversion
> isn't valid (officially) until the constructor of the derived class
> has been entered, but there is nothing to clearly say whether the
> initializers are part of the constructor or not.
>
Doesn't the lifetime of the object being created start at the point where
the process of calling the constructor is complete, ie. after the arguments
to the constructor have been fully evaluated and passed? Processing the
initialization list definitely comes after that point, so I conclude that
the initialization list *is* part of the construction phase, during the
lifetime of the object, and the prohibitions in 3.8 ,which apply to times
outside the limits of the lifetime of objects, do not apply. If they did,
the existence of the restrictions for the construction phase in 12.6.2 and
12.7 wouldn't make any sense. Also for other usages such as name scoping
the initialization list is specifically included along with the constructor
body for some purposes. These usages would be worthless if the
initialization list weren't part of the constructor, so there is some
further corroboration that it must be considered a part of the constructor.
> In practice, I'm going to continue to use it. The compiler MUST be
> able to make the conversion in order to call the constructor of the
> base class. It would be perverse for it not to convert correctly in
> other cases.
Quite so. We shouldn't cripple the power of the language just because there
is some lack of textual clarity here or there in the standard. In such
cases the collective intent of other parts of it should be brought to bear
in order to disambiguate the question. If there are competing
interpretations, the alternative which leads to the least amount of
restriction should always be chosen as the correct one.
_______________
Hiram Berry
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: Francis Glassborow <francis.glassborow@ntlworld.com>
Date: 26 Mar 01 00:37:13 GMT Raw View
In article <99do84$n0q@dispatch.concentric.net>, Hiram Berry
<burningb@burningbridges.com> writes
>Quite so. We shouldn't cripple the power of the language just because there
>is some lack of textual clarity here or there in the standard. In such
>cases the collective intent of other parts of it should be brought to bear
>in order to disambiguate the question. If there are competing
>interpretations, the alternative which leads to the least amount of
>restriction should always be chosen as the correct one.
Better still is to raise a request for interpretation with the Standards
Committees
--
Francis Glassborow
See http://www.accu.org for details of The ACCU Spring Conference, 2001
(includes many regular participants to C & C++ newsgroups)
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: James Kanze <kanze@gabi-soft.de>
Date: 26 Mar 01 03:27:38 GMT Raw View
"Hiram Berry" <burningb@burningbridges.com> writes:
|> Doesn't the lifetime of the object being created start at the point
|> where the process of calling the constructor is complete, ie. after
|> the arguments to the constructor have been fully evaluated and
|> passed?
It depends on what you mean by the lifetime of the object. In the most
general sense, the lifetime only begins when the constructor has
completed.
--
James Kanze mailto:kanze@gabi-soft.de
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelh ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: James Kanze <James.Kanze@dresdner-bank.com>
Date: 16 Mar 01 22:00:58 GMT Raw View
Eugene Karpachov wrote:
> 13 Mar 01 05:06:55 GMT James Kanze :
> >> If you want to make it clean, you can do this
> >> class ZzzBoss : private ZzzHelper, public Yyy {
> >> public:
> >> ZzzBoss( ... ) : YyyBoss( static_cast<YyyHelper&>(*this)), m_helper(
> >> .... ) { }
> >> };
> >Right. This is Dietmar Kuehl's trick.
> With all my respect to Dietmar Kuehl, I think that many people
> discovered this (very obvious) trick themselves; but now I'm in
> doubt - is this trick legal?
I wouldn't exclude people rediscovering it today, but Dietmar first
presented it a long time ago. Even today, I find that most people
aren't very aware of private inheritance, and there still seems to be
some hestitation concerning multiple inheritance.
> >> BTW, the use of static_cast is not necessary as the compiler will
> >> automatically cast from Derived* to Base*. But just to be
> >> explicit...
> >Well, if you take the standard literally, if you use the
> >static_cast,
> To take the standard literally is kind of cheating, isn't it? I'm
> concerned about what authors of standard say, but I'm concerned more
> about what their intent is and what they mean to say.
Formally, you should be able to take the standard literally. In this
case, someone else has posted another section of the standard which
also forbids the static_cast. I suspect that a defect report is in
order, since in one section, a supposely exclusive list doesn't
mention explicit type conversions, and in another, it does.
--
James Kanze mailto:kanze@gabi-soft.de
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelh ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: James Kanze <James.Kanze@dresdner-bank.com>
Date: 16 Mar 01 22:08:39 GMT Raw View
Eugene Karpachov wrote:
> 13 Mar 01 05:04:08 GMT James Kanze :
> >It is explicitly forbidden in 3.8; implicitly converting to a
> >reference to the base class results in undefined behavior if the
> >object is not fully constructed.
> The standard says same thing about conversion to base pointer; does
> this mean that we cannot call inherited member functions in
> constructor? Calling inherited member functions may be considered as
> implicit conversion of "this" to "some_base *", isn't it?
It is. And the paragraph in question doesn't seem too clear about
it. There is, however, text in 3.8/3 which says that the behavior
may be different during construction and destruction; in particular,
see 12.7/2: "To explicitly or implicitly convert a pointer (an lvalue)
referring to an object of class X to a pointer (reference) to a direct
or indirect base class B of X, the construction of X and the
construction of all of its direct or indirect bases that directly or
indirectly derive from B shall have started and the destruction of
these classes shall not have completed." Section 12.6/8 gives other
liberties and restrictions, mainly concerning virtual functions and
RTTI.
The real question remains: when can we consider that the constructor
has started: in the member initialization, or only after the member
initialization has finished. In the case of 12.6/8, there is explicit
wording to say that the operations being discussed are undefined IF
the base class and member constructors have not finished. In 12.7/2,
it does say that the constructors of the base classes must also have
started. While this definitely excludes the derived to base
conversion of a data member (such as the filebuf), it isn't clear that
the default copy constructor is even legal:
Derived::Derived( Derived const& other )
: Base( other )
{
// ...
}
You'll notice that there is an implicit conversion of Derived to Base
here; an intuitive reading would be that the Base constructor has not
been entered, so the conversion is undefined behavior. I'm sure,
however, that this was not the intent of the committee.
In the special case you cite, I don't see a problem, IF we consider
that the constructor has been entered as soon as the member
initializers have started; the implicit conversion is of an already
constructed base.
I think it might also be worth considering an extention to the
standard (but not now) to allow all derived to base conversions of
base classes and member objects anywhere in the constructor (including
in the member initialization list). In all cases, the compiler knows
the actual final type and its complete layout, and so should have no
problem doing the conversion with simple pointer arithmetic of
constants; it doesn't have to access the dynamic information of the
class as it would normally have to do in e.g. the case of a virtual
base class, because the actual most derived type is absolutly known.
--
James Kanze mailto:kanze@gabi-soft.de
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelh ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: ivec@mail.com (Ivan Vecerina)
Date: 9 Mar 2001 12:00:00 -0500 Raw View
Ivan Vecerina said:
>Richard Kelly said:
>>I have two sets of classes, a set of helper classes and a set of boss
>>classes. (See below for some pseudo-code.) All helper classes are
>>related by public inheritance, and the same is true of the boss
>>classes. Also, helper classes provide services to corresponding boss
>>classes via dedicated helper objects.
>>
>>Now, all of the base classes of the boss object must use the same
>>helper object. Therefore, I put a helper object in the most derived
>>boss class and a helper reference in every other boss class.
>>
>>My question is this. Is it safe, i.e. guaranteed by the standard to
>>work correctly, to initialize the helper references before
>>constructing the helper object? (See ZzzBoss::ZzzBoss() below.
>>Naturally, no boss actually uses its helper until after the helper
>>has been constructed.) It works fine, but is it guaranteed?
>
>It is guaranteed to work, as long as you take all necessary
>precautions - e.g. nothing but the address of the object is used
>until it is fully initialized.
Ooops, I forgot to check your example in detail to fully answer
your question. And indeed, you are doing more than just take the
address of the uninitialized member m_helper, by trying to access
a pointer to its base class.
As pointed out by J rg Barfurth, that part is not legal
for non-PODs (although it will work on the compilers I know
as long as you do not use virtual base classes).
Any ugly solution would be to write the most-derived class
like this (to fix the order of initialization):
class ZzzBoss : private ZzzHelper, public Yyy {
public:
ZzzBoss( ... ) : ZzzHelper(...), YyyBoss( *this ) {
// do stuff, and possibly use m_helper
}
void zzz() {
ZzzHelper::doZzz( ... );
}
};
But you are probably better of reconsidering your class hierarchy
(e.g. would containment between Boss classes be an option?), or
using a separate initializer function (=>pointers have to be
used instead of references).
Sorry for the confusion,
Ivan
--
Ivan Vecerina - Surgical Navigation Network
--
Brainbench MVP for C++
http://www.brainbench.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://www.research.att.com/~austern/csc/faq.html ]
Author: Richard Kelly <richard.m.kelly@jpl.nasa.gov>
Date: 10 Mar 2001 10:32:51 -0500 Raw View
Siemel Naran wrote:
>
> I don't know if this is legal. It feels illegal. But one implementation of
> iostreams I've looked at actually does this! The class ifstream, which
> derives from istream, holds in this implementation a streambuf by value.
> And ifstream's constructor passes a pointer to this streambuf down to the
> istream class, and next initializes the streambuf (this is standard -- to
> initialize the base object first and then the member variables). It works,
> provided that the base class constructor only stores the value of the
> pointer, but does not access the pointed-to data.
I gather from J rg's post that that particular iostream implementation
is legal, provided they don't any any conversions on the streambuf.
> If you want to make it clean, you can do this
>
> class ZzzBoss : private ZzzHelper, public Yyy {
> public:
> ZzzBoss( ... ) : YyyBoss( static_cast<YyyHelper&>(*this)), m_helper(
> .... ) { }
> };
>
> BTW, the use of static_cast is not necessary as the compiler will
> automatically cast from Derived* to Base*. But just to be explicit...
Perhaps I don't quite understand you. I don't think that will work,
because the Boss classes are not related to the Helper classes by
inheritance. So the static_cast won't compile, true?
> But consider these 2 alternatives too.
>
> (1) In each class, provide virtual functions to return the helper class.
> Use covariant returns (which MSVC does not support). So
>
> class Xxx { virtual XxxHelper& helper() = 0; };
> class Yyy { virtual YyyHelper& helper() = 0; };
> class Zzz { virtual ZzzHelper& helper() { return d_helper; } };
An interesting suggestion. I hadn't thought of that. Unfortunately, I
don't believe I can use covariant returns, because our compiler (GCC)
doesn't handle covariant return correctly in all cases. Worse yet, it
doesn't always complain, preferring instead to generate bad code. I
would be concerned that this is one of the cases in which it generates
bad code.
--> Note that we are very pleased with GCC in many other ways. <--
Suppose that weren't a problem, though. I avoided another solution,
which involved dynamic_cast. Under the hood, would there be a run-time
penalty for this use of covariant return?
> (2) What if you derive a new class from ZzzBoss? Then it will be AaaBoss
> and will use AaaHelper derived from ZzzHelper. Then you'll have a situation
> where ZzzBoss holds a copy of the ZzzHelper part of the object, and AaaBoss
> holds a copy of the whole AaaHelper object. So not only do you waste space
> storing 2 helper objects by value when you only need 1, but the 2 objects
> are not identical (one is a ZzzHelper while the other is a AaaHelper, so a
> virtual function call m_helper.f() behaves differently depending on where
> you write this function).
Fortunately, this is not an issue. The Boss classes follow the
guideline that only leaf classes may be concrete. Although I didn't
show it in my example, XxxBoss and YyyBoss are abstract, and ZzzBoss is
concrete. As long as we continue to follow this guideline with the Boss
classes -- we seem to be on safe ground there -- class AaaBoss will
never exist.
Actually, we follow that guideline almost everywhere, and AFAIK it
serves us well. The Helper classes are a rare exception. Because of
the uses to which they may be put, it is impossible for any of them to
be abstract. (Well, it's not really impossible, but the only way for
any of them to be abstract involves a pointless exercise in wanton
stupidity.)
> So you could store the helper class in the most base class, as an auto_ptr
> perhaps, and write 2 constructors for each derived class: one that receives
> a helper from a more derived class and passes it down to the base class, and
> another that creates a helper and passes it down to the derived class. So:
> [elided]
That's a pretty good scheme, except that I think it prohibited by J rg's
premature conversion reasoning.
[elided]
> Also, instead of passing raw pointers, I'd like to pass auto_ptr's. For
> exception safety.
Agreed. We almost never use raw pointers. I tend to write "pointer"
when I really mean some kind of smart pointer.
Given the comments of you, Ivan, and J rg, I'm going to use a Helper
pointer in every abstract Boss class, a Helper in the concrete Boss
class, and I'll have the concrete Boss assign the Helper pointers (via
some function probably, not directly) after it enters the body of its
constructor.
Thank you,
Rick
--
Richard M Kelly richard.m.kelly@jpl.nasa.gov
[PGP => D5 C3 CC D2 B7 D0 A9 B0 D5 90 B2 55 5A 80 23 B6 FC AB 67 B4]
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: Richard Kelly <richard.m.kelly@jpl.nasa.gov>
Date: 10 Mar 01 18:04:12 GMT Raw View
Joerg Barfurth wrote:
>
> Richard Kelly <richard.m.kelly@jpl.nasa.gov> wrote:
>
> > My question is this. Is it safe, i.e. guaranteed by the standard to
> > work correctly, to initialize the helper references before
> > constructing the helper object? (See ZzzBoss::ZzzBoss() below.
> > Naturally, no boss actually uses its helper until after the helper
> > has been constructed.) It works fine, but is it guaranteed?
>
> No, it is not. The standard is quite explicit about that in 12.7/p1,2:
>
> [elided]
>
> Assuming
>
> struct XHelp {};
> struct YHelp : XHelp {};
>
> struct XBoss { XBoss(XHelp* ); };
> struct YBoss { YHelp myhelp; YBoss(); };
>
> Lets look at the constructor:
>
> YBoss:YBoss()
> : XBoss(&myhelp)
> {
> }
>
> When this constructor is called, what happens ?
>
> Compiler calls YBoss::YBoss() to construct an object
> Start constructing YBoss object at (this)
> Take address of this->myhelp (OK: Constructing *this has started)
>
> Convert pointer to this->myhelp from (YHelp*) to (XHelp*)
> # ERROR: Constructing this->myhelp has not started #
>
> Start constructing XBoss base subobject of this
> Within: Remember helper pointer passed as argument (OK)
> Done constructing XBoss subobject
>
> Start constructing member myhelp ( too late :-( )
> Done constructing myhelp
>
> Execute ctor body
> Done constructing YBoss object
>
> As the problem is not in taking the address, but in converting that to a
> base class type, using a refernce doesn't change a thing.
Thank you for your clear and convincing explanation. I had searched the
standard for all mention of references and a few other things, but I
hadn't seen the connection to the 12.7/p1,2 information about
conversions.
Clearly, I have to solve this another way. Fortunately, it's hard to do
so, but I had hoped to use this technique, because it seemed very
clean. (See my replies to other posters.)
Rick
--
Richard M Kelly richard.m.kelly@jpl.nasa.gov
[PGP => D5 C3 CC D2 B7 D0 A9 B0 D5 90 B2 55 5A 80 23 B6 FC AB 67 B4]
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: Richard Kelly <richard.m.kelly@jpl.nasa.gov>
Date: 10 Mar 01 21:06:29 GMT Raw View
Ivan Vecerina wrote:
>
> Ivan Vecerina said:
> >Richard Kelly said:
> >>My question is this. Is it safe, i.e. guaranteed by the standard to
> >>work correctly, to initialize the helper references before
> >>constructing the helper object? (See ZzzBoss::ZzzBoss() below.
> >>Naturally, no boss actually uses its helper until after the helper
> >>has been constructed.) It works fine, but is it guaranteed?
> >
> >It is guaranteed to work, as long as you take all necessary
> >precautions - e.g. nothing but the address of the object is used
> >until it is fully initialized.
>
> Ooops, I forgot to check your example in detail to fully answer
> your question. And indeed, you are doing more than just take the
> address of the uninitialized member m_helper, by trying to access
> a pointer to its base class.
> As pointed out by J rg Barfurth, that part is not legal
> for non-PODs (although it will work on the compilers I know
> as long as you do not use virtual base classes).
Yes, I had been thinking along the same lines as you. Clearly, J rg is
correct.
> Any ugly solution would be to write the most-derived class
> like this (to fix the order of initialization):
> [elided]
>
> But you are probably better of reconsidering your class hierarchy
> (e.g. would containment between Boss classes be an option?),
If I understand you correctly, you're asking if one Boss can contain
another. No, it can't. Inheritance really is the proper relationship.
The LSP applies.
> using a separate initializer function (=>pointers have to be
> used instead of references).
That was my second choice. I wasn't thrilled with it, though, because
the nature of the classes is that there's always exactly one Helper per
concrete (most derived) Boss, all base classes of that Boss share the
same Helper, and the lifetime of a Helper matches the lifetime of its
Boss. So I thought references to the concrete Boss's Helper would
express the relationships more clearly.
Now I will fall back to pointers, assigning each Boss's pointer after
the concrete Boss starts executing the body of its constructor.
Fortunately, I don't have to fall all the way back to plan C, which was
to have a single Helper pointer in the most base of all the Boss
classes, using dynamic_cast from the derived Bosses. That idea left me
totally cold.
> Sorry for the confusion,
Not at all. I was the confused one.
Thank you,
Rick
--
Richard M Kelly richard.m.kelly@jpl.nasa.gov
[PGP => D5 C3 CC D2 B7 D0 A9 B0 D5 90 B2 55 5A 80 23 B6 FC AB 67 B4]
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: "Siemel Naran" <namespace@KILL.excite.com>
Date: 11 Mar 01 06:01:29 GMT Raw View
"Richard Kelly" <richard.m.kelly@jpl.nasa.gov> wrote in message
> > class ZzzBoss : private ZzzHelper, public Yyy {
> > public:
> > ZzzBoss( ... ) : YyyBoss( static_cast<YyyHelper&>(*this)), m_helper(
> > .... ) { }
> > };
> >
> > BTW, the use of static_cast is not necessary as the compiler will
> > automatically cast from Derived* to Base*. But just to be explicit...
>
> Perhaps I don't quite understand you. I don't think that will work,
> because the Boss classes are not related to the Helper classes by
> inheritance. So the static_cast won't compile, true?
My code is fine. I have done this sort of thing once before, for my own
iostream hierarchy. Note that in the boss class ZzzBoss I first derive
privately from the helper class ZzzHelper, and second publicly from the base
boss class YyyBoss. The initialization rules garauntee that the first class
I derive from will be initialized first -- in this case the helper class
ZzzHelper. The second class I derive from will be initialized second --
namely YyyBoss, to whose constructor we pass the fully constructed
ZzzHelper/YyyHelper object.
> > class Xxx { virtual XxxHelper& helper() = 0; };
> > class Yyy { virtual YyyHelper& helper() = 0; };
> > class Zzz { virtual ZzzHelper& helper() { return d_helper; } };
>
> An interesting suggestion. I hadn't thought of that. Unfortunately, I
> don't believe I can use covariant returns, because our compiler (GCC)
> doesn't handle covariant return correctly in all cases. Worse yet, it
> doesn't always complain, preferring instead to generate bad code. I
> would be concerned that this is one of the cases in which it generates
> bad code.
The latest G++ does support covariant returns fully.
> Suppose that weren't a problem, though. I avoided another solution,
> which involved dynamic_cast. Under the hood, would there be a run-time
> penalty for this use of covariant return?
Under the hood it probably uses static_cast.
> > (2) What if you derive a new class from ZzzBoss? Then it will be
AaaBoss
> > and will use AaaHelper derived from ZzzHelper. Then you'll have a
situation
> > where ZzzBoss holds a copy of the ZzzHelper part of the object, and
AaaBoss
> > holds a copy of the whole AaaHelper object. So not only do you waste
space
> > storing 2 helper objects by value when you only need 1, but the 2
objects
> > are not identical (one is a ZzzHelper while the other is a AaaHelper, so
a
> > virtual function call m_helper.f() behaves differently depending on
where
> > you write this function).
>
> Fortunately, this is not an issue. The Boss classes follow the
> guideline that only leaf classes may be concrete. Although I didn't
> show it in my example, XxxBoss and YyyBoss are abstract, and ZzzBoss is
> concrete. As long as we continue to follow this guideline with the Boss
> classes -- we seem to be on safe ground there -- class AaaBoss will
> never exist.
That's a good guideline. But in real code, you'll likely be able to adhere
to this guideline only about 80% of the time (not 100% of the time). So
still keep the idea in mind. Especially if you are writing a library to be
used by others, for you have no idea how they'll choose to use your library.
> > So you could store the helper class in the most base class, as an
auto_ptr
> > perhaps, and write 2 constructors for each derived class: one that
receives
> > a helper from a more derived class and passes it down to the base class,
and
> > another that creates a helper and passes it down to the derived class.
So:
> > [elided]
>
> That's a pretty good scheme, except that I think it prohibited by J rg's
> premature conversion reasoning.
No, it is fully conforming because the helper class will be created in the
most derived class as the first step, and then this fully initialized helper
class will be passed down to the base classes.
--
+++++++++++
Siemel Naran
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: ivec@mail.com (Ivan Vecerina)
Date: 05 Mar 01 10:26:35 GMT Raw View
Richard Kelly said:
>I have two sets of classes, a set of helper classes and a set of boss
>classes. (See below for some pseudo-code.) All helper classes are
>related by public inheritance, and the same is true of the boss
>classes. Also, helper classes provide services to corresponding boss
>classes via dedicated helper objects.
>
>Now, all of the base classes of the boss object must use the same
>helper object. Therefore, I put a helper object in the most derived
>boss class and a helper reference in every other boss class.
>
>My question is this. Is it safe, i.e. guaranteed by the standard to
>work correctly, to initialize the helper references before
>constructing the helper object? (See ZzzBoss::ZzzBoss() below.
>Naturally, no boss actually uses its helper until after the helper
>has been constructed.) It works fine, but is it guaranteed?
It is guaranteed to work, as long as you take all necessary
precautions - e.g. nothing but the address of the object is used
until it is fully initialized.
This is in the standard at 12.6.2 /7:
Names in the expression-list of a mem-initializer are evaluated in the scope of the
constructor for which the mem-initializer is specified.
[Example:
class X {
int a;
int b;
int i;
int j;
public:
const int& r;
X(int i): r(a), b(i), i(i), j(this->i) {}
};
initializes X::r to refer to X::a, initializes X::b with the value of the
constructor parameter i, initializes
X::i with the value of the constructor parameter i, and initializes X::j with the
value of X::i; this
takes place each time an object of class X is created. ]
[Note: because the mem-initializer are evaluated in the scope of the
constructor, the this pointer can be used in the expression-list of a
mem-initializer to refer to the object being initialized. ]
I hope this helps,
Ivan
--
Ivan Vecerina
--
Brainbench MVP for C++
http://www.brainbench.com
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: joerg.barfurth@attglobal.net (Joerg Barfurth)
Date: 6 Mar 2001 05:48:02 -0500 Raw View
Richard Kelly <richard.m.kelly@jpl.nasa.gov> wrote:
> I have two sets of classes, a set of helper classes and a set of boss
> classes. (See below for some pseudo-code.) All helper classes are
> related by public inheritance, and the same is true of the boss
> classes. Also, helper classes provide services to corresponding boss
> classes via dedicated helper objects.
>
> Now, all of the base classes of the boss object must use the same
> helper object. Therefore, I put a helper object in the most derived
> boss class and a helper reference in every other boss class.
>
> My question is this. Is it safe, i.e. guaranteed by the standard to
> work correctly, to initialize the helper references before
> constructing the helper object? (See ZzzBoss::ZzzBoss() below.
> Naturally, no boss actually uses its helper until after the helper
> has been constructed.) It works fine, but is it guaranteed?
No, it is not. The standard is quite explicit about that in 12.7/p1,2:
12.7/1: "For an object of non-POD class type (clause 9), before the
constructor begins execution and after the destructor finishes
execution, referring to any non-static member or base class results in
undefined behavior."
12.7/2: "To explicitly or implicitly convert a pointer (an lvalue)
referring to an object of class X to a pointer (reference) to a direct
or indirect base class B of X, the construction of X and the
construction of all of its direct or indirect bases, that directly or
indirectly derive from B shall have started and the destruction of these
classes shall not have completed, otherwise the conversion results in
undefined behavior."
The examples in both these paragraphs illuminate this, but the meaning
is clear: Converting a derived class pointer (or reference) to an object
to a base class pointer (or reference) yields undefined behavior, if the
construction of that object hasn't started yet.
Assuming
struct XHelp {};
struct YHelp : XHelp {};
struct XBoss { XBoss(XHelp* ); };
struct YBoss { YHelp myhelp; YBoss(); };
Lets look at the constructor:
YBoss:YBoss()
: XBoss(&myhelp)
{
}
When this constructor is called, what happens ?
Compiler calls YBoss::YBoss() to construct an object
Start constructing YBoss object at (this)
Take address of this->myhelp (OK: Constructing *this has started)
Convert pointer to this->myhelp from (YHelp*) to (XHelp*)
# ERROR: Constructing this->myhelp has not started #
Start constructing XBoss base subobject of this
Within: Remember helper pointer passed as argument (OK)
Done constructing XBoss subobject
Start constructing member myhelp ( too late :-( )
Done constructing myhelp
Execute ctor body
Done constructing YBoss object
As the problem is not in taking the address, but in converting that to a
base class type, using a refernce doesn't change a thing.
HTH, J rg
--
J rg Barfurth joerg.barfurth@attglobal.net
<<<<<<<<<<<<< using std::disclaimer; <<<<<<<<<<<<<<<<<<<<<<<<<<<<
Software Developer http://www.OpenOffice.org
StarOffice Configuration http://www.sun.com/staroffice
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: James Kanze <James.Kanze@dresdner-bank.com>
Date: 13 Mar 01 05:04:08 GMT Raw View
Ivan Vecerina wrote:
> Richard Kelly said:
> >I have two sets of classes, a set of helper classes and a set of
> >boss classes. (See below for some pseudo-code.) All helper
> >classes are related by public inheritance, and the same is true of
> >the boss classes. Also, helper classes provide services to
> >corresponding boss classes via dedicated helper objects.
> >Now, all of the base classes of the boss object must use the same
> >helper object. Therefore, I put a helper object in the most
> >derived boss class and a helper reference in every other boss
> >class.
> >My question is this. Is it safe, i.e. guaranteed by the standard
> >to work correctly, to initialize the helper references before
> >constructing the helper object? (See ZzzBoss::ZzzBoss() below.
> >Naturally, no boss actually uses its helper until after the helper
> >has been constructed.) It works fine, but is it guaranteed?
> It is guaranteed to work, as long as you take all necessary
> precautions - e.g. nothing but the address of the object is used
> until it is fully initialized.
> This is in the standard at 12.6.2 /7:
It is explicitly forbidden in 3.8; implicitly converting to a
reference to the base class results in undefined behavior if the
object is not fully constructed.
> Names in the expression-list of a mem-initializer are evaluated in
> the scope of the constructor for which the mem-initializer is
> specified.
Scope is not a problem here; otherwise, he would have had a compiler
error. It is the implicit conversion to a reference to the base class
which causes undefined behavior.
--
James Kanze mailto:kanze@gabi-soft.de
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelh ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: James Kanze <James.Kanze@dresdner-bank.com>
Date: 13 Mar 01 05:06:55 GMT Raw View
Siemel Naran wrote:
> "Richard Kelly" <richard.m.kelly@jpl.nasa.gov> wrote in message
> > class XxxHelper {
> > class YyyHelper : public XxxHelper {
> > class ZzzHelper : public YyyHelper {
> > class XxxBoss {
> > public:
> > XxxBoss( XxxHelper& helper ) : m_helper( helper ) {
> > // do stuff, but don't use helper or m_helper
> > }
> > void xxx() {
> > m_helper.doXxx( ... );
> > }
> > private:
> > XxxHelper& m_helper;
> >
> > };
> > class YyyBoss : public XxxBoss {
> > public:
> > YyyBoss( YyyHelper& helper ) : XxxBoss( helper ), m_helper( helper )
> > {
> > // do stuff, but don't use helper or m_helper
> > }
> > void yyy() {
> > m_helper.doYyy( ... );
> > }
> > private:
> > YyyHelper& m_helper;
> > };
> > class ZzzBoss : public Yyy {
> > public:
> > ZzzBoss( ... ) : YyyBoss( m_helper ), m_helper( ... ) {
> > // do stuff, and possibly use m_helper
> > }
> > void zzz() {
> > m_helper.doZzz( ... );
> > }
> > private:
> > ZzzHelper m_helper;
> > };
> I don't know if this is legal. It feels illegal. But one
> implementation of iostreams I've looked at actually does this!
It's clearly illegal according to 3.8/6. The object is not a POD, and
the "lvalue is implicitly converted to a reference to the base class
type". (IMHO, this is curious wording. Would it be legal if I used
an explicit static_cast in the initialization? If so, what is the
difference for the compiler?) In the case of virtual base classes, it
will even fail with typical implementations; I suspect that this is
the motivation behind the prohibition.
The results are undefined behavior, which means that an implementation
may make it work, either generally or in specific cases. Iostream is
part of the implementation, and is at liberty to take advantage of
what it knows about the way the implementation works internally.
Interestingly, the standard itself does this in its definition of
ifstream, although the member is "for expostion" only. An
implementation is not required to implement it in this fashion. (On
the other hand, since a call to operator new is an observable side
effect, I don't think that an implementation is allowed to allocate
the filebuf using operator new either.
Dietmar Kuehl devised an interesting trick to handle this: the derived
class inherits privately from the helper class (or filebuf). This
allows ensuring that the construction of this class occurs first,
either by specifying the class first in the inheritance list, or by
using virtual inheritance.
> The class ifstream, which derives from istream, holds in this
> implementation a streambuf by value. And ifstream's constructor
> passes a pointer to this streambuf down to the istream class, and
> next initializes the streambuf (this is standard -- to initialize
> the base object first and then the member variables). It works,
> provided that the base class constructor only stores the value of
> the pointer, but does not access the pointed-to data.
It works because the implementation makes it work, and takes advantage
of this.
There are two aspects. The standard guarantees (I think) that the
istream constructor will do nothing with the pointer except store it
somewhere. So there is no risk on that score. But the implicit
conversion from derived to base is undefined behavior.
> If you want to make it clean, you can do this
>
> class ZzzBoss : private ZzzHelper, public Yyy {
> public:
> ZzzBoss( ... ) : YyyBoss( static_cast<YyyHelper&>(*this)), m_helper(
> .... ) { }
> };
Right. This is Dietmar Kuehl's trick.
> BTW, the use of static_cast is not necessary as the compiler will
> automatically cast from Derived* to Base*. But just to be
> explicit...
Well, if you take the standard literally, if you use the static_cast,
you don't need to be concerned about the order of initialization,
since there is no longer the implicit conversion which causes
undefined behavior.
--
James Kanze mailto:kanze@gabi-soft.de
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelh ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: jk@steel.orel.ru (Eugene Karpachov)
Date: 13 Mar 01 16:37:33 GMT Raw View
13 Mar 01 05:06:55 GMT James Kanze :
>> If you want to make it clean, you can do this
>>
>> class ZzzBoss : private ZzzHelper, public Yyy {
>> public:
>> ZzzBoss( ... ) : YyyBoss( static_cast<YyyHelper&>(*this)), m_helper(
>> .... ) { }
>> };
>
>Right. This is Dietmar Kuehl's trick.
With all my respect to Dietmar Kuehl, I think that many people discovered this
(very obvious) trick themselves; but now I'm in doubt - is this trick legal?
>> BTW, the use of static_cast is not necessary as the compiler will
>> automatically cast from Derived* to Base*. But just to be
>> explicit...
>
>Well, if you take the standard literally, if you use the static_cast,
To take the standard literally is kind of cheating, isn't it? I'm concerned
about what authors of standard say, but I'm concerned more about what their
intent is and what they mean to say.
--
jk
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: jk@steel.orel.ru (Eugene Karpachov)
Date: 13 Mar 01 16:42:38 GMT Raw View
13 Mar 01 05:04:08 GMT James Kanze :
>It is explicitly forbidden in 3.8; implicitly converting to a
>reference to the base class results in undefined behavior if the
>object is not fully constructed.
The standard says same thing about conversion to base pointer; does this mean
that we cannot call inherited member functions in constructor? Calling
inherited member functions may be considered as implicit conversion of "this"
to "some_base *", isn't it?
For example, it seems I can't write:
class some_ostream : private some_buf, public std::ostream {
public:
some_ostream() : std::ostream(this) {}
// because here is conversion:
// std::ostream(static_cast<std::streambuf*>(this));
};
but can I write as below?
class some_ostream : public std::ostream {
private:
some_buf buf_;
public:
some_ostream() {
init(&buf_); // isn't here an implicit conversion
// static_cast<basic_ios*>(this)->init(&buf) ?
}
};
--
jk
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]
Author: "William M. Miller" <wmmiller@MailAndNews.com>
Date: Tue, 13 Mar 2001 22:03:53 GMT Raw View
>===== Original Message From default@dresdner-bank.com =====
>It's clearly illegal according to 3.8/6. The object is not a POD, and
>the "lvalue is implicitly converted to a reference to the base class
>type". (IMHO, this is curious wording. Would it be legal if I used
>an explicit static_cast in the initialization?
No. If you look at the bullet immediately following the "implicitly
converted" prohibition in 3.8p6, it prohibits use of the lvalue as
the operand of static_cast (except to char& or unsigned char&).
-- William M. Miller
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: Richard Kelly <richard.m.kelly@jpl.nasa.gov>
Date: 13 Mar 2001 17:48:47 -0500 Raw View
Siemel Naran wrote:
>
> "Richard Kelly" <richard.m.kelly@jpl.nasa.gov> wrote in message
>
> > Perhaps I don't quite understand you. I don't think that will work,
> > because the Boss classes are not related to the Helper classes by
> > inheritance. So the static_cast won't compile, true?
>
> My code is fine. I have done this sort of thing once before, for my own
> iostream hierarchy. Note that in the boss class ZzzBoss I first derive
> privately from the helper class ZzzHelper, and second publicly from the base
> boss class YyyBoss. The initialization rules garauntee that the first class
> I derive from will be initialized first -- in this case the helper class
> ZzzHelper. The second class I derive from will be initialized second --
> namely YyyBoss, to whose constructor we pass the fully constructed
> ZzzHelper/YyyHelper object.
Oops. I was blinded by my preconceptions of the relationships between
the Boss and Helper classes. Now I see what you're doing. It's an
interesting technique.
> The latest G++ does support covariant returns fully.
That's good to know. Thank you.
> > Fortunately, this is not an issue. The Boss classes follow the
> > guideline that only leaf classes may be concrete. Although I didn't
> > show it in my example, XxxBoss and YyyBoss are abstract, and ZzzBoss is
> > concrete. As long as we continue to follow this guideline with the Boss
> > classes -- we seem to be on safe ground there -- class AaaBoss will
> > never exist.
>
> That's a good guideline. But in real code, you'll likely be able to adhere
> to this guideline only about 80% of the time (not 100% of the time). So
> still keep the idea in mind. Especially if you are writing a library to be
> used by others, for you have no idea how they'll choose to use your library.
Agreed.
> > That's a pretty good scheme, except that I think it prohibited by J rg's
> > premature conversion reasoning.
>
> No, it is fully conforming because the helper class will be created in the
> most derived class as the first step, and then this fully initialized helper
> class will be passed down to the base classes.
You're right, of course. I hadn't understood your suggestion before.
Thank you,
Rick
--
Richard M Kelly richard.m.kelly@jpl.nasa.gov
[PGP => D5 C3 CC D2 B7 D0 A9 B0 D5 90 B2 55 5A 80 23 B6 FC AB 67 B4]
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: Richard Kelly <richard.m.kelly@jpl.nasa.gov>
Date: 1 Mar 2001 10:56:50 -0500 Raw View
I have two sets of classes, a set of helper classes and a set of boss
classes. (See below for some pseudo-code.) All helper classes are
related by public inheritance, and the same is true of the boss
classes. Also, helper classes provide services to corresponding boss
classes via dedicated helper objects.
Now, all of the base classes of the boss object must use the same
helper object. Therefore, I put a helper object in the most derived
boss class and a helper reference in every other boss class.
My question is this. Is it safe, i.e. guaranteed by the standard to
work correctly, to initialize the helper references before
constructing the helper object? (See ZzzBoss::ZzzBoss() below.
Naturally, no boss actually uses its helper until after the helper
has been constructed.) It works fine, but is it guaranteed?
// ------------------------------------------------------------
// HELPER CLASSES
// ------------------------------------------------------------
class XxxHelper {
public:
void doXxx( ... ) { ... }
private:
...
};
class YyyHelper : public XxxHelper {
public:
void doYyy( ... ) { ... }
private:
...
};
class ZzzHelper : public YyyHelper {
public:
void doZzz( ... ) { ... }
private:
...
};
// ------------------------------------------------------------
// BOSS CLASSES
// ------------------------------------------------------------
class XxxBoss {
public:
XxxBoss( XxxHelper& helper ) : m_helper( helper ) {
// do stuff, but don't use helper or m_helper
}
void xxx() {
m_helper.doXxx( ... );
}
private:
XxxHelper& m_helper;
};
class YyyBoss : public XxxBoss {
public:
YyyBoss( YyyHelper& helper ) : XxxBoss( helper ), m_helper( helper )
{
// do stuff, but don't use helper or m_helper
}
void yyy() {
m_helper.doYyy( ... );
}
private:
YyyHelper& m_helper;
};
class ZzzBoss : public Yyy {
public:
ZzzBoss( ... ) : YyyBoss( m_helper ), m_helper( ... ) {
// do stuff, and possibly use m_helper
}
void zzz() {
m_helper.doZzz( ... );
}
private:
ZzzHelper m_helper;
};
Thank you,
Rick
--
Richard M Kelly richard.m.kelly@jpl.nasa.gov
[PGP => D5 C3 CC D2 B7 D0 A9 B0 D5 90 B2 55 5A 80 23 B6 FC AB 67 B4]
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: "Siemel Naran" <namespace@KILL.excite.com>
Date: 04 Mar 01 03:16:20 GMT Raw View
"Richard Kelly" <richard.m.kelly@jpl.nasa.gov> wrote in message
> class XxxHelper {
> class YyyHelper : public XxxHelper {
> class ZzzHelper : public YyyHelper {
> class XxxBoss {
> public:
> XxxBoss( XxxHelper& helper ) : m_helper( helper ) {
> // do stuff, but don't use helper or m_helper
> }
> void xxx() {
> m_helper.doXxx( ... );
> }
> private:
> XxxHelper& m_helper;
>
> };
>
> class YyyBoss : public XxxBoss {
> public:
> YyyBoss( YyyHelper& helper ) : XxxBoss( helper ), m_helper( helper )
> {
> // do stuff, but don't use helper or m_helper
> }
> void yyy() {
> m_helper.doYyy( ... );
> }
> private:
> YyyHelper& m_helper;
> };
>
> class ZzzBoss : public Yyy {
> public:
> ZzzBoss( ... ) : YyyBoss( m_helper ), m_helper( ... ) {
> // do stuff, and possibly use m_helper
> }
> void zzz() {
> m_helper.doZzz( ... );
> }
> private:
> ZzzHelper m_helper;
> };
I don't know if this is legal. It feels illegal. But one implementation of
iostreams I've looked at actually does this! The class ifstream, which
derives from istream, holds in this implementation a streambuf by value.
And ifstream's constructor passes a pointer to this streambuf down to the
istream class, and next initializes the streambuf (this is standard -- to
initialize the base object first and then the member variables). It works,
provided that the base class constructor only stores the value of the
pointer, but does not access the pointed-to data.
If you want to make it clean, you can do this
class ZzzBoss : private ZzzHelper, public Yyy {
public:
ZzzBoss( ... ) : YyyBoss( static_cast<YyyHelper&>(*this)), m_helper(
.... ) { }
};
BTW, the use of static_cast is not necessary as the compiler will
automatically cast from Derived* to Base*. But just to be explicit...
But consider these 2 alternatives too.
(1) In each class, provide virtual functions to return the helper class.
Use covariant returns (which MSVC does not support). So
class Xxx { virtual XxxHelper& helper() = 0; };
class Yyy { virtual YyyHelper& helper() = 0; };
class Zzz { virtual ZzzHelper& helper() { return d_helper; } };
(2) What if you derive a new class from ZzzBoss? Then it will be AaaBoss
and will use AaaHelper derived from ZzzHelper. Then you'll have a situation
where ZzzBoss holds a copy of the ZzzHelper part of the object, and AaaBoss
holds a copy of the whole AaaHelper object. So not only do you waste space
storing 2 helper objects by value when you only need 1, but the 2 objects
are not identical (one is a ZzzHelper while the other is a AaaHelper, so a
virtual function call m_helper.f() behaves differently depending on where
you write this function).
So you could store the helper class in the most base class, as an auto_ptr
perhaps, and write 2 constructors for each derived class: one that receives
a helper from a more derived class and passes it down to the base class, and
another that creates a helper and passes it down to the derived class. So:
class XxxBoss {
protected:
const std::auto_ptr<XxxHelper> d_helper;
public:
explicit XxxBoss(XxxHelper * helper);
};
class YyyBoss : public XxxBoss {
public:
explicit YyyBoss(YyyHelper * helper) : XxxBoss(helper) { }
YyyBoss() : XxxBoss(new YyyHelper) { }
class ZzzBoss : public YyyBoss {
public:
explicit ZzzBoss(ZzzHelper * helper) : YyyBoss(helper) { }
ZzzBoss() : YyyBoss(new ZzzHelper) { }
<BEGIN superflous>
If you don't like protected variables, then in the base class you could make
d_helper private and provide protected template functions to return it.
protected: // XxxBoss
template <class Boss> Boss& helper() { return
static_cast<Boss&>(*d_helper); }
template <class Boss> const Boss& helper() const;
protected: // YyyBoss
YyyHelper& helper() { return this->XxxBoss::template
helper<YyyHelper>(); }
protected: // ZzzBoss
ZzzHelper& helper() { return this->XxxBoss::template
helper<ZzzHelper>(); }
Also, instead of passing raw pointers, I'd like to pass auto_ptr's. For
exception safety.
class XxxBoss {
public:
explicit XxxBoss(std::auto_ptr<XxxHelper> helper);
};
class YyyBoss : public XxxBoss {
public:
explicit YyyBoss(std::auto_ptr<YyyHelper> helper) : XxxBoss(helper)
{ }
YyyBoss() : XxxBoss(std::auto_ptr<YyyHelper>(new YyyHelper)) { }
};
Unfortunately, the code is an error because auto_ptr only has T::T(T&) and
so you cannot pass a temporary auto_ptr, as in the 2nd constructor of
YyyBoss, to another function.
<END superflous>
--
+++++++++++
Siemel Naran
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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://www.research.att.com/~austern/csc/faq.html ]
[ Note that the FAQ URL has changed! Please update your bookmarks. ]