|
I may be reading your comments wrong, but did you state a C++ ref class destructor is a finalizer?
That's not the case. The destructor maps to Dispose() and a finalizer must be explicitly added.
Destructors do not get called if you allocate an object on the managed heap and never explicitly
delete it. The finalizer, however will be called when the handle no longer has any references to it
and the GC gets around to cleaning it up. That's why native objects should be freed in a finalizer,
not a destructor (although the destructor should call the finalizer - the compiler takes care of
SuppressFinalize).
Having to explicitly delete objects would be counterintuitive, as Michael originally stated.
Being able to (deterministically) using the familiar C++ syntax, however, is a nice, powerful
feature.
Note I'm only referring to C++ - I'll make no comments about C# except for the related Dispose/Finalize
pattern of CLR.
Cheers,
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
I'm pretty sure we've said the same thing. However...
You say destructors are not called on an object allocated on the managed heap which isn't strictly true. If you allocate it on the managed heap with gcnew then yes the destructor is never called if you don't manually call delete . However, if you instantiate the ref class object with stack semantics then the destructor is called implicitly when the object goes out of scope (even though underneath its still allocated on the managed heap like all ref class objects). This is the critical addition to C++/CLI (vs MC++) that allowed for RAII style coding like native C++. Michael was claiming this is not the case (as it is in native C++) and that the finalizer is what is called, which is not correct. The finalizer will be called eventually either way if you define one, but the way things work is obviously much different than what a destructor provides.
The confusion is that in C# when you declare ~Class1() this is actually mapped to the finalizer (Dispose(false) ) in the dispose pattern. C# has no concept of destructors like C++ or C++/CLI even though the semantics there look like you're declaring a destructor. In C++/CLI you accomplish the same thing by declaring !Class1() which more properly distinguishes the finalizer from the destructor. Then declaring ~Class1() in C++/CLI does a very different thing than ~Class1() in C#. That's why in C# you need to explicitly call Dispose() in a finally block or use a using block with things like FileWriter objects to ensure there is no resource leak. C++/CLI's destructor semantics provide the handy native C++ style, FileWriter fw; which will automatically be disposed of properly when it goes out of scope (deterministically).
|
|
|
|
|
iddqd515 wrote: in C# when you declare ~Class1() this is actually mapped to the finalizer (Dispose(false)) in the dispose pattern.
Dispose() isn't the finalizer...Finalize() is, right?
Regardless, C++ does the same - the destructor becomes Dispose() (quite literally,
as Michael's original warning message stated).
In C++ the Destructor is explicitly called when a stack-based syntax managed object
goes out of scope, the destructor is called directly, a managed heap object
is explicitly deleted with delete, or another language calls Dispose() on an object.
If the class implements a finalizer then that needs to be (at least SHOULD be) called from
the destructor. The compiler takes care of calling SuppressFinalize so the finalizer will not
be called again by the GC.
Isn't that the same as the C# (and probably VB) Dispose/Finalize pattern?
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Are you being purposefully daft? I understand what's going on; I'm arguing that "C# does not have destructors" is an untrue statement. I'm not repudiating the mechanics.
Dispute this: The C# language specification allows for the definition of destructors for class types.
|
|
|
|
|
From: http://en.wikipedia.org/wiki/Destructor_%28computer_science%29
In object-oriented programming, a destructor (sometimes shortened to dtor) is a method which is automatically invoked when the object is destroyed. Its main purpose is to clean up and to free the resources which were acquired by the object along its life cycle and unlink it from other objects or resources invalidating any references in the process. The use of destructors is key to the concept of RAII.
How in C# do you declare such a thing? A finalizer is not automatically invoked when the object is destroyed deterministically. It is nondeterministically called by the GC at some point in the future. Most important, in no way does a finalizer enable the RAII pattern.
Take a FileStream class which encapsulates a resource in C#. Declare a finalizer for it. When an instance of a FileStream class goes out of the scope the finalizer is not called then. You do not know when it is called. Furthermore, even if the GC does a collect and there are no roots in the code to the object, the simple existence of the finalizer counts as a root and can cause the FileStream object to be promoted into the next generation and not collected by the GC until the next collect cycle (who knows when that will be). So while your FileStream object may have gone out of scope ages ago, by not calling Dispose and assuming the finalizer will close the FileStream , you have potentially caused a resource leak. The next function call that assumes the file you previously had used with the FileStream object is closed may throw an exception because you were relying on the garbage collector to nondeterministically release a critical resource.
On the other hand, a destructor in C++/CLI will be called the moment the FileStream goes out of scope (assuming you declared the object with stack semantics and not gcnew ). Then in the destructor you make sure there is a call to Dispose and you ensure that the resource is released immediately. You don't need a try/finally and you don't need to explicitly call Dispose and nor does anyone who uses your class in the future. The destructor incurs no performance penalty during allocation or deallocation like a finalizer does either. There's no way to declare such a function in C#.
To say 'I'm not repudiating the mechanics' therefore C# has destructors is completely wrong. The mechanics are exactly what separate a finalizer and a destructor. You can't just say 'oh it has a ~Class() declaration therefore its a destructor even if it works completely differently.' They work in fundamentally different ways. Start declaring finalizers for all your C# classes as if it was a C++ destructor and watch the programs performance and reliability plummet.
|
|
|
|
|
iddqd515 wrote: How in C# do you declare such a thing?
~Class()
|
|
|
|
|
Well like I said then, make sure to declare ~Class() in all your C# classes and don't bother explicitly invoking Dispose . I'm sure you'll find it works fantastically. Also make sure to take advantage of finalizers to declare reference classes in C# with stack semantics.
-- modified at 15:30 Monday 13th August, 2007
|
|
|
|
|
No need to get pissy. As I said earlier, I'm not advocating the use of what the language itself calls (or, apparently, used to call) a destructor; I'm just claiming that such a beast exists.
From the C# Language Specification:
[Note: In the previous version of this standard, what is now referred to as a "finalizer" was called a "destructor". Experience has shown that the term "destructor" caused confusion and often resulted to incorrect expectations, especially to programmers knowing C++. In C++, a destructor is called in a determinate manner, whereas, in C#, a finalizer is not. To get determinate behavior from C#, one should use Dispose. end note]
|
|
|
|
|
So that right there says 'sorry we incorrectly used to refer to this as a destructor. We realized it was confusing because its really not a destructor at all. To get destructor like behavior in C# use the Dispose method.' Therefore, C# has no destructors. And even in the past, it only had them in name, not function. But its been awhile since anyone referred to them as destructors since its blatantly confusing, especially with the introduction of C++/CLI in '05.
|
|
|
|
|
iddqd515 wrote: On the other hand, a destructor in C++/CLI will be called the moment the FileStream goes
out of scope (assuming you declared the object with stack semantics and not gcnew).
Then in the destructor you make sure there is a call to Dispose
That may be where my confusion with your statements is coming from.
That sounds like managed extensions. In VS 2005, you can't call Dispose() - the compiler
won't let you. This is now done implicitly with the destructos symantics.
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Sorry, I was being ambiguous. All this Dispose talk and switching between C# and C++/CLI gets me mixed up occasionally. You're correct, I meant in the destructor a call to Dispose(true) is made. In any case, the important part (C++/CLI destructors are deterministic) is the same, while a C# finalizer is completely different due to its nondeterministic nature. Luckily I've never had to use MC++. *shudder*
-- modified at 15:49 Monday 13th August, 2007
|
|
|
|
|
Cool. I'm just making sure for my own clarification.
I recently ported code from VS2003 and while I was ready for many of the changes, the
Dispose stuff was a surprise. I want to make sure I've got it clear in my head since
I only used the documentation as a learnin' resource
Cheers,
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Yeah its taken me over a month to really grasp the whole pattern and how it meshes with traditional C++ semantics. Its not helpful when you try to find stuff about the Dispose pattern and end up with C# examples mixing terms to create more confusion. On the plus side, now C++/CLI basically works just like you would expect native C++ to in most cases and you only need to worry about finalizers for classes with unmanaged members. Certainly an improvement over MC++ (as most things are).
|
|
|
|
|
Yes indeed, there is a destructor-like syntax, but it is actually called a finalizer;
it is a bit confusing if you ask me.
And as you said it has undeterministic behavior: It gets executed by the gc
when it decides to collect the object.
Luc Pattyn [Forum Guidelines] [My Articles]
this weeks tips:
- make Visual display line numbers: Tools/Options/TextEditor/...
- show exceptions with ToString() to see all information
- before you ask a question here, search CodeProject, then Google
|
|
|
|
|
Michael Chapman wrote: That just seems totally counterintuitive to using managed code.
Yes, but so is mixing native types and having destructors.
The garbage collector will only cleanup managed objects if you don't specifically call a destructor.
Since you don't need deterministic cleanup of your native object, the smart pointer is a good solution,
since it provides the destructor/finalizer for you.
Only if you needed deterministic cleanup of your native object would you need to implement a destructor
and possibly a finalizer as shown in the article (although you do it implicitely with the smart pointer).
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
|
Interesting! Thanks for the link George!
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
hmm...my Sunday morning reply made no sense - I'll take another crack at it.
What I meant to say was...
If you have unmanaged resources in a managed class then you'll need a way to
free them, as always.
You could just cleanup your native resources in the destructor BUT the destructor
won't be called unless you explicitly call it (using delete or calling it directly).
Having to do THAT would be counterintuitive
To ensure your native object created with new gets deleted, you should implement
a destructor AND a finalizer in its managed owner class - that's what Nish's smart pointer
class does for you.
That way you don't have to explicitly destruct your managed object. If it is left
for the GC to cleanup, the GC will call the finalizer (NOT the destructor!)...
ref class MyRefClass
{
int *NativeIntArray;
public:
MyRefClass() {NativeIntArray = new int[400];}
~MyRefClass() {this->!MyRefClass();}
!MyRefClass() {delete[] NativeIntArray;}
} Cheers,
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
OK. I'm making a DLL with a bunch of Win forms controls. The project is in C++/CLI, compiled with the "/pure" switch.
I made a control that contained a TextBox, and two Panels. Each panel has a background image, which I set up using the BackgroundImage property in the "Properties" window. I built the project, the custom control shows up in my toolbox, life is good.
Now, I went to make a second control that uses multiple instances of the first. I added a new "User Control" item to the project and went into design view. I went to the toolbox, and tried dragging an instance of Control1 onto Control2. What an ugly error message:
Failed to create component 'Control1'.... Could not find any resources appropriate for the specified culture or the neutral culture. Make sure 'MyNamespace.MySubnamespace.Control1.resources' was correctly embedded or linked into assembly 'MyAssembly' at compile time, or that all satellite assemblies required are loadable and fully signed.
What the crap!!!???? I did a little research, and found an article that suggested the compiler was not creating the correct resource file. In fact, the only resource file I could find in the intermediate directory was "MySubnamespace.resources". Fine. So Visual Studio is too stupid to name its own resources correctly. So I selected the "Control1.resx" file, right-clicked and selected "Properties". I changed the intermediate file name to be "MyNamespace.Mysubnamespace.Control1.resources", as the error message seemed to suggest was required.
Guess what? I still get the same error message!!!! Can someone explain or point me to a GOOD article on what I'm doing wrong and how to resolve it?
By the way, I don't get that error message once I take out the panel background images. It's obviously a problem with linking the resource file that contains the background image data.
Thank you.
-- modified at 13:00 Saturday 11th August, 2007
|
|
|
|
|
When does the assignment operator get called as opposed to the default ctor? For example, the first line calls the default ctor while the second calls the assignment.
<br />
MyClass theObj = 5;<br />
theObj = theObj;<br />
TIA
I am a SysAdmin, I battle my own daemons.
|
|
|
|
|
Like the name implies, a constructor constructs an object.
You're constructing an object on the first line.
The second line is an assignment (explicitly using the assignment operator).
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
why does the returned object have to be const?
<br />
const Counter Counter::operator++(int)<br />
{<br />
Counter temp(*this);<br />
++itsVal;<br />
return temp;<br />
}<br />
TIA
I am a SysAdmin, I battle my own daemons.
|
|
|
|
|
I don't think it should be const. Where did you see that form?
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
hi,
I think it is to prevent the following ambigius code snippit
<code>Counter count;
Counter foo = count++;
count++++;
codito ergo sum
|
|
|
|
|
Hi,
I have the following code that won't compile with the error:
e:\PROGRAMMING\SANDBOX\RTTI\dynamic_cast1.cpp(31): error C2683: dynamic_cast : 'A' is not a polymorphic type
<br />
#include "stdafx.h"<br />
<br />
using namespace std;<br />
<br />
class A { };<br />
<br />
class B { };<br />
<br />
void f()<br />
{<br />
A* pa = new A;<br />
B* pb = new B;<br />
void* pv = dynamic_cast<void*>(pa);<br />
pv = dynamic_cast<void*>(pb);<br />
}<br />
<br />
int main()<br />
{<br />
return 0;<br />
}
It's an example in VStudio and is expected to work. Can anyone help me make sense of the error message? TIA!
I am a SysAdmin, I battle my own daemons.
|
|
|
|