From: sturlamolden on 2 Aug 2010 22:03 On 3 Aug, 02:47, Roy Smith <r...(a)panix.com> wrote: > This one I don't understand. Yes, I get RAII, but surely there are > valid reasons to allocate memory outside of constructors. Containers > which resize themselves (such as std::vector) are one obvious example. That is because an exception might skip an arbitrarily placed delete[] or std::fclose when it rewinds the stack, so to avoid resource leaks they must be places inside a destructor of an object on the stack (which will be called). This is unsafe, anyone who writes this in C++ should be flogged: void foobar() { std::FILE *fid = fopen("whatever"); // some code here std::fclose(fid); } This idiom is safer : struct File { std::FILE *fid; File(const char *name) { // acquire resource in constructor fid = std::fopen(name); if (!fid) throw some_exception; } ~File() { // free resource in destructor if(fid) std::flose(fid); } }; void foobar() { File file("whatever"); // some code here } It is for the very same reason we should use std::vector instead of new[] for arrays. It is why new and delete should only be used inside constructors/destructors. It also why C++ has references in addition to pointers. Which means this is bad in C++, as new and delete is arbitrarily placed: void foobar() { File *myfile = new File("whatever); // some code here delete myfile; } An object should go on the stack, because if an exception is thrown, we need the destructor call. Which is why this (as shown above) is ok: void foobar() { File file("whatever"); // some code here } This is the kind of gotchas that allows C++ to shoot your leg off. In comparison C is much more forgiving, because there is no exceptions (unless you use setjmp/longjmp). This is ok in C, but not in C++: void foobar() { FILE *fid = fopen("whatever"); // some code here fclose(fid); } For the same reason we can place malloc and free wherever we like in C code. But in C++ we must restrict std::malloc and std::free (as well as new and delete) to constructor and destructor pairs. This kind of design is mandatory to make safe C++ programs. But it is not enforced by the compiler. And since the majority of C++ programmers don't obey by these rules, Java and C# tends to produce far less runtime errors and memory/resource leaks. And C++ textbooks tends to avoid teaching these important details. I'm inclined to believe it is because the authors don't understand it themselves. Objects on the stack are also required for operator overloading in C+ +. A common novice mistake is this: std::vector<double> *myvec = new std::vector<double>(10); myvec[5] = 10.0; // why does the compiler complain????? And then the novice will spend hours contemplating why the stupid compiler claims the type of myvec[5] is std::vector<double>. There was recently a post to the Cython mailing list claiming there is a bug in Cython's auto-generated C++ because of this. But this is how it should be: std::vector<double> myvec(10); myvec[5] = 10.0; // ok And now we see why C++ has references, as that is how we can efficiently reference and object on the stack without getting the operator overloading problem above. C++ is good, but most programmers are better off with C. It has fewer gotchas. And if we need OOP, we have Python... Sturla
From: sturlamolden on 2 Aug 2010 22:25 On 3 Aug, 04:03, sturlamolden <sturlamol...(a)yahoo.no> wrote: > struct File { > std::FILE *fid; > File(const char *name) { > // acquire resource in constructor > fid = std::fopen(name); > if (!fid) throw some_exception; > } > ~File() { > // free resource in destructor > if(fid) std::flose(fid); > } > > }; And since this is comp.lang.python, I'll add in that this sometimes applies to Python as well. Python, like C++, can have the call stack rewinded by an exception. If we call some raw OS resource allocation, e.g. malloc or fopen using ctypes, we have to place a deallocation in __del__ (and make sure the object is newer put in a reference cycle). The safer option, however, is to use a C extension object, which is guaranteed to have the destructor called (that is, __dealloc__ when using Cython or Pyrex). If we place raw resource allocation arbitrarily in Python code, it is just as bad as in C++. But in Python, programmers avoid this trap by mostly never allocating raw OS resources from within Python code. E.g. Python's file object is programmed to close itself on garbage collection, unlike a pointer retured from fopen using ctypes. Sturla
From: Roy Smith on 2 Aug 2010 22:46 In article <7d95c0d3-718d-4958-9364-263c833f1835(a)i24g2000yqa.googlegroups.com>, sturlamolden <sturlamolden(a)yahoo.no> wrote: > On 3 Aug, 02:47, Roy Smith <r...(a)panix.com> wrote: > > > This one I don't understand. �Yes, I get RAII, but surely there are > > valid reasons to allocate memory outside of constructors. �Containers > > which resize themselves (such as std::vector) are one obvious example. > > That is because an exception might skip an arbitrarily placed delete[] > or std::fclose when it rewinds the stack... Well, OK, but there's still nothing wrong with allocating memory outside of constructors, as long as you make sure the destructor cleans it up. Or you have made some other arrangements to make sure it's cleaned up (try/catch, auto_pointer, etc).
From: Aahz on 3 Aug 2010 00:02 In article <roy-788377.21035502082010(a)news.panix.com>, Roy Smith <roy(a)panix.com> wrote: >In article <i37ire$7gd$1(a)panix5.panix.com>, aahz(a)pythoncraft.com (Aahz) >wrote: >> >> http://www.netfunny.com/rhf/jokes/98/May/stroustrup.html > >The same story has been floating around for eons, just with the names >changed. I saw one where Wirth was ostensibly making fun of the people >who didn't understand that Pascal was all just a joke. > >I'm sure if you go back far enough, you can find a McCarthy / Lisp >version. It probably goes something like, "So, anyway, we tried to >figure out what was the most absurd way to abuse punctuation we could >imagine. Somebody suggested that every program would have to end with >37 close parentheses. When we finally stopped laughing, we started >sketching out a grammar on the chalkboard that would let us do that". http://www.netfunny.com/rhf/jokes/90q2/lispcode.html -- Aahz (aahz(a)pythoncraft.com) <*> http://www.pythoncraft.com/ (You knew I was going to post that, right?)
From: Aahz on 3 Aug 2010 00:05
In article <2b473423-0a22-4f4d-943f-31ea2d6020e6(a)z10g2000yqb.googlegroups.com>, sturlamolden <sturlamolden(a)yahoo.no> wrote: > >And since this is comp.lang.python, I'll add in that this sometimes >applies to Python as well. Python, like C++, can have the call stack >rewinded by an exception. If we call some raw OS resource allocation, >e.g. malloc or fopen using ctypes, we have to place a deallocation in >__del__ (and make sure the object is newer put in a reference cycle). >The safer option, however, is to use a C extension object, which is >guaranteed to have the destructor called (that is, __dealloc__ when >using Cython or Pyrex). Actually, with Python 2.6 or later, the Pythonic solution is to use ``with``. -- Aahz (aahz(a)pythoncraft.com) <*> http://www.pythoncraft.com/ "....Normal is what cuts off your sixth finger and your tail..." --Siobhan |