|
|
Today, something blew my mind.
I was surfing the net, and I happened to wonder about how best ensure
stuff gets freed while coding in C++. So I went to Stack Overflow. There
was an accepted answer about RAII - but right below it, with 5x the
up-votes, was an answer that basically said
Everybody says RAII, and that's a good answer. But there's a better
one: DON'T allocate dynamic memory in the first place! That is, rather
than writing
Foo * foo = new Foo();
just write
Foo foo();
Reflecting on this for a moment, I saw the wisdom of it. If your stuff
isn't dynamic, you can never ever forget to free it, leave it dangling,
or have any of the other problems associated with manual memory
allocation. (About the worst thing you can do is give somebody else a
pointer to it - don't do that!)
But then I wondered... how can I apply that here?
char * buffer = new char[1024];
stream.read(buffer, 1024);
...stuff...
delete[] buffer;
The thing that actually confused me was what the syntax for creating a
non-dynamic array is - I've never seen anybody do that before. Ever.
(Not in C or C++, anyway.) It turns out the answer is pretty trivial:
char buffer[1024];
stream.read(buffer, 1024);
Now you don't have to *care* that the function might have fifteen return
statements, or that stuff might throw exceptions... your memory will
*always* be deallocated when you leave. Because that's how stacks work.
But then somebody else pointed out that this is find for a 1KB buffer,
but probably a bad idea for a 10MB one. (It also fails if you don't
statically know how much data you want - which is pretty much the
*original* reason for inventing dynamic allocation in the first place...)
The suggested solution? "Why don't you just use a std::vector<char>?"
Erm... because std::vector<char> /= char *. One is basically an
unstructured chunk of arbitrary memory, this other is a complex
implementation-dependent data structure.
"What's wrong with using vector.front()?"
...
My mind is blown.
OK, so the documentation says that vector.front() gives you a reference
to the first element of the vector. Nothing surprising there. However,
if you try to pass that to stream.read()... Woah, that's a whole *bunch*
of assumptions, right there! o_O
The documentation guarantees that what gets returned is a reference to
the first element. Firstly, I wasn't actually aware you could even use a
reference as a pointer; I thought they were separate concepts. But
secondly, I had assumed that the actual location of vector elements was
implementation-defined; they might be in a tree, a linked list, a table
or tables... anything!
But this code... this crazy code seems to be *assuming* that all the
elements always just happen to be perfectly contiguous in memory.
I'm still trying to figure out whether this audacious assumption about
the internal behaviour of a library class is more or less evil than just
trying to do manual memory management correctly...
Manual memory management? Pretty evil.
Relying on internal library implementation details? Pretty evil.
Maybe the set of coding evilness is only partially ordered...
Post a reply to this message
|
|