I’m fairly new to C++ so this is probably somewhat of a beginner question. It regards the “proper” style for doing something I suspect to be rather common.
I’m writing a function that, in performing its duties, allocates memory on the heap for use by the caller. I’m curious about what a good prototype for this function should look like. Right now I’ve got:
int f(char** buffer);
To use it, I would write:
char* data;
int data_length = f(&data);
// ...
delete[] data;
However, the fact that I’m passing a pointer to a pointer tips me off that I’m probably doing this the wrong way.
Anyone care to enlighten me?
In C, that would have been more or less legal.
In C++, functions typically shouldn’t do that. You should try to use RAII to guarantee memory doesn’t get leaked.
And now you might say “how would it leak memory, I call
delete[]just there!”, but what if an exception is thrown at the// ...lines?Depending on what exactly the functions are meant to do, you have several options to consider. One obvious one is to replace the array with a vector:
and now we no longer need to explicitly delete, because the vector is allocated on the stack, and its destructor is called when it goes out of scope.
I should mention, in response to comments, that the above implies a copy of the vector, which could potentially be expensive. Most compilers will, if the
ffunction is not too complex, optimize that copy away so this will be fine. (and if the function isn’t called too often, the overhead won’t matter anyway). But if that doesn’t happen, you could instead pass an empty array to theffunction by reference, and havefstore its data in that instead of returning a new vector.If the performance of returning a copy is unacceptable, another alternative would be to decouple the choice of container entirely, and use iterators instead:
Now the usual iterator operations can be used (
*outto reference or write to the current element, and++outto move the iterator forward to the next element) — and more importantly, all the standard algorithms will now work. You could usestd::copyto copy the data to the iterator, for example. This is the approach usually chosen by the standard library (ie. it is a good idea;)) when a function has to return a sequence of data.Another option would be to make your own object taking responsibility for the allocation/deallocation:
And again we no longer need to explicitly delete because the allocation is managed by an object on the stack. The latter is obviously more work, and there’s more room for errors, so if the standard vector class (or other standard library components) do the job, prefer them. This example is only if you need something customized to your situation.
The general rule of thumb in C++ is that “if you’re writing a
deleteordelete[]outside a RAII object, you’re doing it wrong. If you’re writing anewor `new[] outside a RAII object, you’re doing it wrong, unless the result is immediately passed to a smart pointer”