Re: Destructor: not gauranteed to be called?
- From: "Arnaud Debaene" <adebaene@xxxxxxxxxxxxxxxx>
- Date: Tue, 31 Jan 2006 22:06:47 +0100
Brandon Bray [MSFT] wrote:
> It's unfortunate that C# decided to use the tilda syntax for finalizers,
> and
> even more unfortunate that the old Managed C++ syntax did the same thing.
Well, that's one point on which we agree ;-)
>> The whole point of the IDisposable interface is to circumvent this
>> limitation of the GC, although it is still an inferior solution
>> compared to the native, synchronous C++ destructor, IMHO.
>
> I'm curious how IDisposable presents an inferior solution. From my
> perspective as a language designer, I see IDisposable as the
> implementation detail for destructors in C++. Really, you don't have
> to know anything about IDisposable to use destructors in C++/CLI. To
> me, the biggest limitation imposed on destructors as a result of
> IDisposable is that all destructors are public and virtual. I
> actually that's a good thing, and it's a mistake that unmanaged C++
> allows destructors to be anything else.
I was thinking more about C# "raw" implementation of IDisposable (where the
compiler doesn't generate the Dispose method; nor the call to Dipose in
client code), because in this model, it becomes the responsability of the
client of an object to free the internal ressources held by the object, by
calling explicitely Dispose, or through the "using" keyword.
For this point, C++/CLI stack semantic is a huge step in the good direction
WRT to Managed C++ / C#.
>
>> Agreed. There is NO destruction in .NET (nor in Java).
>
> The premise of this statement is flawed. Destruction is a language
> level service, because only the language can determine when it is
> appropriate to deterministically cleanup objects. Why? Because the
> programmer needs to be involved - otherwise you deal with the
> infamous halting problem. The CLR is a collection of services that
> can be supplied to a running program. As long as we're dealing with
> Turing Machines, the CLR will never be able to provide deterministic
> cleanup as a service.
I agree, but I think we must go a step latter : What is generally called
"destruction" is in fact a 2 parts process :
1) Logical destruction, which correspond to the user code in the
destructor/finalizer function. To be most usefull, this operation should be
synchronous with the release of the last reference to the object (ie, a
stack object goes out of scope, a heap object is not referenced anymore or
is dekleted in native C++), because it allows to implment the RAII idiom and
therefore make it much easier to write exception-safe code.
<troll - well perhaps not THAT troll>I would argue that it's almost
impossible to write a non-trivial exception-safe code without the RAII idiom
</troll>.
2) Resource automatic freeing (mainly memory) , which can be done
automagically and asynchrounously by a GC.
Both native C++ and .NET collapse those 2 distinct operations into one
concept (the destructor or the run-by-the-GC-finalizer), whereas IMHO they
should be more clearly separated. Again, CLI stack semantic with automatic
implementation of IDiposable and automatic call to Dispose is the right
answer IMHO.
> So, that means deterministic cleanup must be moved to the language
> level. The best way to accomplish that and maintain a sense of
> cross-language functionality was to create a common API. That was
> IDisposable. From there, it's a matter of how the languages treat
> destruction semantics. C++/CLI does everything that unmanaged C++
> does, including automatic creation of destructors when embedded types
> have destructors.
Yes, is is the intent. I am not sure however that stack semantics can bu
used in all cases to implement the RAII idiom.Well, I suspect one could
declare small "ref struct". The real problem of course is that it is
unusable from C# or VB.NET.
Nonetheless, I see your point about the fact that a common API (IDiposable)
was the best bet to tackle the problem in a language neutral manner. Too bad
that other languages (C#, VB.NET) took the easy and wrong road of letting
the Dispose call responsibility in the client hands.
Anyway, as a C++ bare-to-the-metal-performance-fan (sarcarsm...), I still
regret that IDisposable must go through a virtual call overhead.
>> No more memory leaks... The main reason for GC is to avoid raw memory
>> leaks, not to get a better model for logical destruction of objects.
>
> While GC is primarily about memory leaks, I would argue it serves to
> do much more. C++ is inherently not type safe because it allows for
> things like use of an object after delete. GC in the context of a
> language like C++ is the only way to achieve type safety.
Well, I would not call "type safety" the danger of dereferencing a dangling
pointer, but I take your point (for me, "type safety" is about the danger of
an incorrect cast that may run unnoticed).
> Also, if you are truly using Object Oriented Programming, objects will
> represent resources like files, network connections, UI, etc. This
> means that memory has a direct correlation to other resources, so GC
> has the potential to cleanup a lot more than just memory.
If you use the IDiposable pattern, yes. The finalizer is much more limited
in what you can do, since you can't reference another object from within a
finalizer. The problem is that most developper know about finalizers (which
they think about as destructors), but don't know about IDisposable, or are
unaware of the stack semantics.
> Lastly, deterministic cleanup is really bad at cleaning up in certain
> situations. A frequent example is shared resources that form a
> dependency cycle. The impact of reference counting is well
> understood, and all of the practices applied to unmanaged C++
> frequently result in fragile programs.
Agreed. Let's say the ideal solution to this problem still remains to be
invented ;-)
> The problem that usually
> results is programmers don't adapt to a different environment, and
> instead try to contort deterministic practices to a non-deterministic
> environment.
Yes, but implementors don't make our life easier when they use the same
syntax for finalizers and destructors!
> The short story... writing robust code still requires smart people
> thinking solutions all the way through.
Amen...
Arnaud
MVP - VC
.
- References:
- Destructor: not gauranteed to be called?
- From: Peter Oliphant
- Re: Destructor: not gauranteed to be called?
- From: Carl Daniel [VC++ MVP]
- Re: Destructor: not gauranteed to be called?
- From: Peter Oliphant
- Re: Destructor: not gauranteed to be called?
- From: Arnaud Debaene
- Re: Destructor: not gauranteed to be called?
- From: Brandon Bray [MSFT]
- Destructor: not gauranteed to be called?
- Prev by Date: Re: Visual C++ bug report
- Next by Date: Re: Destructor: not gauranteed to be called?
- Previous by thread: Re: Destructor: not gauranteed to be called?
- Next by thread: Re: Destructor: not gauranteed to be called?
- Index(es):
Relevant Pages
|