Deconstructors

K

Kip

Greetings,

It would seem as though whenever I find a feature that is not in C /
C++, I discover later that it was not implemented for good reason.

I am curious. What was the rationale behind not allowing deconstructors
to take parameters? Or can they and I am mistaken?
 
A

Andrew Koenig

I am curious. What was the rationale behind not allowing deconstructors
to take parameters? Or can they and I am mistaken?

If they did, how would you use them?
 
V

Victor Bazarov

Kip said:
It would seem as though whenever I find a feature that is not in C /
C++, I discover later that it was not implemented for good reason.

I am curious. What was the rationale behind not allowing
deconstructors to take parameters? Or can they and I am mistaken?

Destructors are most often called implicitly. If you define one that
takes arguments, what arguments would you want the system to pass?

V
 
A

Alf P. Steinbach

* Andrew Koenig:
If they did, how would you use them?

One idea that has been floating around is a bool argument that is 'true' if
and only if an exception thrown by the destructor would be caught by some
catch-block or by a hypothetical "..." function try block around 'main' (or
some other specific rule, it may be that this particular one is brain-dead).

Not that I think it's a good idea in general to throw from destructors.

But as I recall one proposed usage of that idea was some kind of scope guard
that threw (?) an exception from its destructor if it hadn't received a
commit() call.
 
P

Pete Becker

Alf said:
One idea that has been floating around is a bool argument that is 'true' if
and only if an exception thrown by the destructor would be caught by some
catch-block or by a hypothetical "..." function try block around 'main' (or
some other specific rule, it may be that this particular one is brain-dead).

That also requires two-phase exception handling, which is not currently
required. (i.e. the implementation is currently allowed to destroy as it
unwinds, without knowing in advance whether the exception will be
caught) So it's not just adding an argument; it's fundamentally
redesigning exceptions.
 
G

Glenn G. Chappell

Victor said:
Destructors are most often called implicitly. If you define one
that takes arguments, what arguments would you want the system to
pass?

None, just like with constructors.

I think your argument applies just as well to constructors, which,
after all, are called implicitly with no parameters when an array
is declared. But we don't consider it a problem that one can also
write constructors that take parameters.

However, I suggest that putting destructors with parameters into
the language is still a bad idea. Here is why:

Reason 1

Conceptually, there are usually lots of ways to make something,
but only one way to make it go away.

On the off chance that you can think of several ways to destroy an
object, you can still implement this in C++: store some indication
in the object about how it should be destroyed. The destructor
looks at this and acts accordingly.

Thus, multiple destruction methods can already be done, and it
seems rare enough that it is hardly worth adding new complexity
to the language.

Reason 2

{
Foo x;
x.~Foo(param);
}

x gets destroyed twice: once when I do it explicitly, and once
implicitly when it goes out of scope. This is bad.

Of course, one can call a destructor explicitly even in the current
version of C++, but it is rare, and so when we do it, we often
think carefully about what is happening.

So the real problem with destructors with parameters is that they
make calling a destructor explicitly seem "normal", despite the
fact that it is dangerous.
 
R

Raymond Martineau

Greetings,

It would seem as though whenever I find a feature that is not in C /
C++, I discover later that it was not implemented for good reason.

I am curious. What was the rationale behind not allowing deconstructors
to take parameters? Or can they and I am mistaken?

You could fake destructors taking parameters by setting some members just
before the object gets destroyed. For example:

class A {
public:
int dest_param;

A::~A()
{
switch (dest_param)
{
default:
/* ... */
break;
}
}
}

int main ()
{
A inst1;

inst1.dest_param = 123;
/* Destructor gets called at function exit. */
return 0;
}

There isn't a more direct way of doing this - as another poster mentioned,
destructors are called implicitly when an object falls out of scope.

FYI, you can directly pass paramers to the delete operators, but these
might not be as useful for the object that you are destroying as the
example shown above.
 
A

Alf P. Steinbach

* Pete Becker:
That also requires two-phase exception handling

Yes, and it doesn't even tell whether the destructor is called as a result of
stack unwinding or not.

A simpler and more efficient rule could be to have the argument specify
whether the destructor is called directly by exception-initiated stack
unwinding.

That would support the scope guard application (which on reflection the
brain-dead rule doesn't) but not much else.
 
M

Malte Starostik

Kip said:
Andrew Koenig wrote:




CThing *pThing = new CThing;

delete pThing(5);

But what if the thing is on the stack?

void func()
{
CThing thing;
thing.doStuff();
// and now?
thing.~thing(5);
// bad, default dtor still implicitly called at end of scope
}

Cheers,
Malte
 
H

Howard

Kip said:
CThing *pThing = new CThing;

delete pThing(5);

I think what was meant was, how whould you use that information? I'm not
sure what, in a destructor, would be useful to know, since the object is
going away anyway and any state changes you make to it will be lost. And
anything that you need to do prior to destruction can be done via a member
function (or by directly accessing public members).

Or, if you need to take some action before destruction, regardless of how
that destruction is going to occur, then you could consider containing your
object inside another object, whose job is to call a member function of that
inner object, prior to allowing everything to be destroyed.

I suspect that the designers simply felt there wasn't any need for such a
construct. Perhaps you have such a need? I sure can't think of one (except
for the idea discussed here regarding exceptions, but that's opening a
bigger can of worms).

-Howard
 
A

Andrew Koenig

I think what was meant was, how whould you use that information? I'm not
sure what, in a destructor, would be useful to know, since the object is
going away anyway and any state changes you make to it will be lost. And
anything that you need to do prior to destruction can be done via a member
function (or by directly accessing public members).

Moreover, this example doesn't really answer the question, because it covers
only one, fairly uncommon, case. For example:

{
CThing x;
}

At the }, how do you say what argument to give x's destructor?

Or what about this case?

CThing *p = new CThing[100];
delete [] p;

What if you want to give a different destructor argument to each element of
the array?

Or this case?

std::vector<CThing> v;

How do you tell v what arguments to give to destructors when it destroys v's
elements?

This last case is the most interesting, because it is an example of a more
general problem: The part of a program that constructs and destroys objects
of a given type may well be written separately from the definition of that
type itself. So how does one part of the program tell the other part what
destructor arguments to use?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,202
Messages
2,571,057
Members
47,662
Latest member
sxarexu

Latest Threads

Top