Topic: object contruction/destruction in c++, design flaw?
Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1995/10/02 Raw View
henk_kampman@idsys.nl (Henk Kampman) writes:
>Why is the destructor only called for fully contructed objects?
If the destructor were called for partially constructed objects, how
would it know which parts of the object to destroy? This would require
the constructor first initialize all sub-objects in a way that could
not possibly throw any exceptions, and then later assign them their final
values; in many cases, this might not even be possible, and even in those
cases where it is possible, it may be inefficient.
>To my opinion this results in code duplication in the contructor and destructor
That need not be the case. For exampe, in a lot of cases you can use
`auto_ptr' to avoid code duplication.
>class a
>{
>public:
> char* mBuffer1;
> char* mBuffer2;
> a();
> ~a();
>};
Use the following instead:
class a {
public:
auto_ptr<char> mBuffer1;
auto_ptr<char> mBuffer2;
a();
~a();
}
>a::a()
>{
> mBuffer1 = NULL;
> mBuffer2 = NULL;
>
> try
> {
> mBuffer1 = new char[1000];
> if(mBuffer1 == NULL)
> throw MemError;
According to the draft standard, `new' will throw an exception rather
than returning a null pointer, so the `if' statement here is unnecessary.
> mBuffer2 = new char[1000];
> if(mBuffer1 == NULL)
> throw MemError;
> }
> catch(...) // allocation failed
> {
> delete mBuffer1;
> delete mBuffer2;
>
> throw;
> }
>
>}
With auto_ptr, this becomes just
a::a()
{
mBuffer1 = new char[1000];
mBuffer2 = new char[1000];
}
or alternately
a::a()
: mBuffer1(new char[1000]),
mBuffer2(new char[1000])
{}
>a::~a()
>{
> delete mBuffer1;
> delete mBuffer2;
>}
With auto_ptr, you don't even need a destructor - the compiler-generated
one will do the right thing.
--
Fergus Henderson | "Australia is the richest country in the world,
fjh@cs.mu.oz.au | according to a new system of measuring wealth
http://www.cs.mu.oz.au/~fjh | announced by the World Bank yesterday."
PGP: finger fjh@128.250.37.3 | - Melbourne newspaper "The Age", 18 Sept 1995.
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: kuehl@uzwil.rz.uni-konstanz.de (Dietmar Kuehl)
Date: 1995/10/05 Raw View
Hi,
auto_ptr doesn't seem to be usable for array objects...
Fergus Henderson (fjh@munta.cs.mu.OZ.AU) wrote:
: Use the following instead:
:
: class a {
: public:
: auto_ptr<char> mBuffer1;
: auto_ptr<char> mBuffer2;
: a();
: ~a();
: }
:
: With auto_ptr, this becomes just
:
: a::a()
: {
: mBuffer1 = new char[1000];
: mBuffer2 = new char[1000];
: }
:
: or alternately
:
: a::a()
: : mBuffer1(new char[1000]),
: mBuffer2(new char[1000])
: {}
:
: With auto_ptr, you don't even need a destructor - the compiler-generated
: one will do the right thing.
I doubt this statement. Doesn't this result in "undefined behavior"?
[Moderator's note: yes, you're absolutely right. My mistake.
-fjh.]
According to lib.auto.ptr.cons the destructor of auto_ptr results in
"delete get()". But applying "operator delete" to an array object (the
result of "new char[1000]" is an array object: expr.new verse 7) yields
undefined behavior (expr.delete). Is this yet another problem for the
auto_ptr class that it can't deal with array objects or am I missing
something (maybe I'm not up to date with the latest state of the
standard...)?
dk
--
http://www.informatik.uni-konstanz.de/~kuehl
dietmar.kuehl@uni-konstanz.de
I am a realistic optimist - that's why I appear to be slightly pessimistic
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: lars.farm@nts.mh.se (Lars Farm)
Date: 1995/10/05 Raw View
In article <44odn6$c61@gabi.gabi-soft.fr>,
kanze@gabi-soft.fr (J. Kanze) wrote:
>Basically, the most correct way of writting this would be to change the
>pointer declarations to auto_ptr, and forget the try/catch block
>completely. In fact, I would generally write it this way, with the
>initialization in the initialization part of the constructor, e.g.:
>
> a::a()
> : mBuffer1( new char[ 1000 ] )
> , mBuffer2( new char[ 1000 ] )
> {
> }
In article <9527516.21208@mulga.cs.mu.OZ.AU>,
fjh@munta.cs.mu.OZ.AU (Fergus Henderson) wrote:
>With auto_ptr, this becomes just
>
> a::a()
> {
> mBuffer1 = new char[1000];
> mBuffer2 = new char[1000];
> }
>
>or alternately
>
> a::a()
> : mBuffer1(new char[1000]),
> mBuffer2(new char[1000])
> {}
[...]
>With auto_ptr, you don't even need a destructor - the compiler-generated
>one will do the right thing.
auto_ptr does delete p; not delete[] p; is this "the right thing" when you
do p = new char[1000]? If so, please disregard the rest!
[Moderator's note: Lars Farm is right. My suggestion was
incorrect. -fjh.]
This demonstrates the confusion that comes from new/new[]. Other posts
defending two kinds of new have claimed that new/new[] is no problem and
users are/should/must be able to differentiate between new/new[] and
delete/delete[]. Fergus Henderson and James Kanze clearly are experts and
both experts did the same thing! If they miss this, ordinary users will
too, and much more so.
The motivation for splitting new into new/new[] is supposed to be speed and
size optimization. I would like to know what the actual quantified gain of
splitting new into new/new[] is. Can anyone quantify
(speed%,heapsize%,codesize%) the total gains in (1) a typical C++ program
and in (2) an extreme but complete working C++ program allocating lots and
lots of tiny single objects? I'm not interested in the microlevel of one
single object, but rather the effects on the entire programs.
The cost of splitting new into new/new[] is clear and demonstrated above.
Users must remember that (1) new != new[] (2) new belongs to delete not
delete[] (3) new[] belongs to delete[] not delete. (4) when to use which.
The language becomes harder to learn, use (even for experts) and teach.
If the gains turn out "small" wouldn't it be reasonable to merge new/new[]
and delete/delete[] so that new X is the same as new X[1]? Wouldn't the
simplification be worth it?
--
Lars Farm, lars.farm@nts.mh.se
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: jbuck@Synopsys.COM (Joe Buck)
Date: 1995/10/05 Raw View
fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
I've compressed Fergus's example (for writing a constructor that is
safe from out-of-memory exceptions) a bit by putting the constructor inline.
The result goes like this.
class a {
public:
auto_ptr<char> mBuffer1;
auto_ptr<char> mBuffer2;
a() {
mBuffer1 = new char[1000];
mBuffer2 = new char[1000];
}
};
Looks good; no destructor needed. But just a minute: auto_ptr<T>'s
destructor does
delete T;
not
delete [] T;
right? This means this usage is incorrect, even though it so happens
that it will work in many implementations for objects without destructors.
Shouldn't there be a separate auto_array_ptr to handle pointers to arrays
correctly?
--
-- Joe Buck <jbuck@synopsys.com> (not speaking for Synopsys, Inc)
Anagrams for "information superhighway": Enormous hairy pig with fan
A rough whimper of insanity
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: "James M. Curran" <72261.655@CompuServe.COM>
Date: 1995/10/13 Raw View
|> a::a()
|> {
|> mBuffer1 = new char[ 1000 ] ;
|> try
|> {
|> mBuffer2 = new char[ 1000 ] ;
|> }
|> catch (...)
|> {
|> delete mBuffer2 ;
|> }
|> }
That last bit should be "delete mBuffer1;", shouldn't it? Or am
I missing something?
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: miker3@ix.netcom.com (Mike Rubenstein)
Date: 1995/10/14 Raw View
"James M. Curran" <72261.655@CompuServe.COM> wrote:
>|> a::a()
>|> {
>|> mBuffer1 = new char[ 1000 ] ;
>|> try
>|> {
>|> mBuffer2 = new char[ 1000 ] ;
>|> }
>|> catch (...)
>|> {
>|> delete mBuffer2 ;
>|> }
>|> }
>
>That last bit should be "delete mBuffer1;", shouldn't it? Or am
>I missing something?
Would you believe "delete mBuffer1[];"?
Michael M Rubenstein
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Author: henk_kampman@idsys.nl (Henk Kampman)
Date: 1995/09/29 Raw View
Why is the destructor only called for fully contructed objects?
To my oppinion this results in code duplication in the contructor and destructor
For example:
class a
{
public:
char* mBuffer1;
char* mBuffer2;
a();
~a();
};
a::a()
{
mBuffer1 = NULL;
mBuffer2 = NULL;
try
{
mBuffer1 = new char[1000];
if(mBuffer1 == NULL)
throw MemError;
mBuffer2 = new char[1000];
if(mBuffer1 == NULL)
throw MemError;
}
catch(...) // allocation failed
{
delete mBuffer1;
delete mBuffer2;
throw;
}
}
a::~a()
{
delete mBuffer1;
delete mBuffer2;
}
If the destructor would be called in case of incomplete contruction, the
code would be much simpler, even in this small example.
a::a()
{
// initialize for save destruction
mBuffer1 = NULL;
mBuffer2 = NULL;
mBuffer1 = new char[1000];
if(mBuffer1 == NULL)
throw MemError;
mBuffer2 = new char[1000];
if(mBuffer1 == NULL)
throw MemError;
}
// this would be called in case of an exception
// if for example the mBuffer2 allocation failed
a::~a()
{
delete mBuffer1;
delete mBuffer2;
}
So, why not call a destructor if an object is partially contructed?
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]