for me personally i am way more productive with C# than i am with C++, and using the library is much more convenient/consistent than a mix/match of Win32, stdlib, or boost libraries.
back on topic, there is no X is better than Y. there is only X is better suited for Z than Y. in general i think learning C++ first will make you a better C# programmer....
the one thing you need to watch out for is not to think in the old language. learn to think in the new language.
people who have seen "VB6 style" code in .NET know what i'm talking about.... :'(
>> Plus, it helps improve performance -- if a large number of objects go out of scope at once, they don't need to all be destroyed immediately while your thread sits on its ass waiting.
The role that destructors play in memory management is just a side-effect of their real purpose; to do *something* precisely when the object goes out of scope. For example, you may have an object that writes an XML start tag in it's constructor, and a closing tag in it's destructor, eg:
Obviously, for things to work properly, the objects have to be destroyed in a specific order.Code:xml_writer a( "outermost" ), b( "middle" ), c( "innermost" );
The importance and usefulness of the destructor really can't be overstated. Their absence from languages such as C# and Java is a real shame, indeed.
Code:#include <cmath> #include <complex> bool euler_flip(bool value) { return std::pow ( std::complex<float>(std::exp(1.0)), std::complex<float>(0, 1) * std::complex<float>(std::atan(1.0) *(1 << (value + 2))) ).real() < 0; }
yes, you *could* do that with a constructor/destructor, but i'm still unconvinced from your XML example why C# lacking true deterministic deallocation is "inherently flawed."
edit: btw, i say true deterministic deallocation because even though in C# you can manually call Dispose() on an object, technically the instance is still available in memory, unlike C++'s delete.
Last edited by bling; 04-30-2009 at 03:55 PM.
>> but i'm still unconvinced from your XML example why C# lacking true deterministic deallocation is "inherently flawed."
That was just a very simple demonstrative example. There are literally innumerable mechanisms that can be described using the constructor/destructor idiom, as they essentially model a stack (and thus recursion), arguably one of the most important concepts of computer science. The question isn't "what are they good for?" but "what can't they be used for?".
Code:#include <cmath> #include <complex> bool euler_flip(bool value) { return std::pow ( std::complex<float>(std::exp(1.0)), std::complex<float>(0, 1) * std::complex<float>(std::atan(1.0) *(1 << (value + 2))) ).real() < 0; }
well, if you're argument is simply to do something upon construction and destruction, rather than allocating/deallocating memory, then there really isn't much difference between C#'s IDisposable pattern vs C++'s destructor....
actually, in C++/CLI if you declare a C++ destructor it actually compiles into a Dispose() method, which via stack semantics Dispose() will automatically be called when the object goes out of scope.
i will say that *properly* implementing the IDisposable pattern in .NET is a complete pain in the butt with numerous places where you can screw up. and since it is a pattern, there is no guarantee that it will be done exactly the same across different libraries and programmers....which leads to more pain in the butt.
here's one of the most comprehensive guides to implementing the pattern:
Joe Duffy's Weblog
yes...25 pages printed.
>> i will say that *properly* implementing the IDisposable pattern in .NET is a complete pain in the butt
I don't even bother with it; I always invoke cleanup code manually. It sure would be nice if I didn't have to, though.
Code:#include <cmath> #include <complex> bool euler_flip(bool value) { return std::pow ( std::complex<float>(std::exp(1.0)), std::complex<float>(0, 1) * std::complex<float>(std::atan(1.0) *(1 << (value + 2))) ).real() < 0; }
Okay, this is confusingOriginally Posted by bling
Are you saying that although the programmer can call Dispose earlier (i.e., deterministic but manual deallocation), Dispose will be invoked automatically when the object goes out of scope? My impression (from Clemens Szyperski's annotation in the article you linked to) is that if the programmer does not call Dispose, Dispose will be called sometime during finalization, in which case it is not equivalent to the deterministic destruction provided by (standard) C++ destructors.
Look up a C++ Reference and learn How To Ask Questions The Smart WayOriginally Posted by Bjarne Stroustrup (2000-10-14)
...and in a tangentially related point
...as opposed to stopping executing at ANOTHER point to do it, when you have no control on the performance hit.Plus, it helps improve performance -- if a large number of objects go out of scope at once, they don't need to all be destroyed immediately while your thread sits on its ass waiting.
My point here is that this doesn't save time overall - the same work has to be done at some point, and if you're not in control of that then it will increase the memory footprint while those objects that are out of scope hang around.
It's a double edged cutting issue.
If a collection of 20,000 objects fall out of scope, how long does that take? It depends on what happens as they do. Tens of millions can be processed per second on typical hardware when the destructor is trivial, and depending on what's really going on, the net result (sometimes due to compiler optimization) can be zero work has to be done. That's almost never true on .NET; some under the hood management of the implied base object has to be managed at the minimum on a collection of such objects.
Last edited by JVene; 05-01-2009 at 06:27 AM.
I always thought that it was deterministic, but apparently not. According to MSDN:
Note the misuse of the word "automatically".The primary use of this interface is to release unmanaged resources. The garbage collector automatically releases the memory allocated to a managed object when that object is no longer used. However, it is not possible to predict when garbage collection will occur.
[edit]
I see. It's deterministic iff the object is manipulated within the "using" syntax.
[/edit]
Last edited by Sebastiani; 05-01-2009 at 06:29 AM.
Code:#include <cmath> #include <complex> bool euler_flip(bool value) { return std::pow ( std::complex<float>(std::exp(1.0)), std::complex<float>(0, 1) * std::complex<float>(std::atan(1.0) *(1 << (value + 2))) ).real() < 0; }
How is it misuse? It is true that the garbage collector automatically releases the memory for managed objects, since (at least ideally) the programmer does not need to manually do so.Originally Posted by Sebastiani
Look up a C++ Reference and learn How To Ask Questions The Smart WayOriginally Posted by Bjarne Stroustrup (2000-10-14)
I should have amended that in my edit. But yes, it is automatic (as long as the "using" syntax is employed, of course).
Code:#include <cmath> #include <complex> bool euler_flip(bool value) { return std::pow ( std::complex<float>(std::exp(1.0)), std::complex<float>(0, 1) * std::complex<float>(std::atan(1.0) *(1 << (value + 2))) ).real() < 0; }
yes, very confusing indeed !! i hope Sebastiani's link from MSDN made it a little more clear.
basically:
a) the programmer cannot deterministically clean up managed resources
b) IDisposable provides a pattern for cleaning up unmanaged resources (and optionally do so deterministically by calling Dispose() directly)
anyways, IDisposable is such a complicated mess probably because .NET has to support multiple languages. for most languages, Dispose() is optional, so if you do not call it, unmanaged resources will not be freed until the GC kicks in. however, for langauges like C++, which has RAII, Dispose() will automatically called when an object goes out of scope. i believe the C++/CLI compiler will automatically generate that code even if you didn't define it.
Last edited by bling; 05-01-2009 at 12:07 PM.