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