Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prototype for function that allocates memory on the heap (C/C++)

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?

like image 396
Joe Snikeris Avatar asked Jul 23 '09 13:07

Joe Snikeris


2 Answers

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:

std::vector<char> f();

std::vector<char> data = f();
int data_length = data.size();
// ...
//delete[] data; 

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 f function 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 the f function by reference, and have f store 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:

// definition of f
template <typename iter>
void f(iter out);

// use of f
std::vector<char> vec;
f(std::back_inserter(vec));

Now the usual iterator operations can be used (*out to reference or write to the current element, and ++out to move the iterator forward to the next element) -- and more importantly, all the standard algorithms will now work. You could use std::copy to 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:

struct f { // simplified for the sake of example. In the real world, it should be given a proper copy constructor + assignment operator, or they should be made inaccessible to avoid copying the object
  f(){
    // do whatever the f function was originally meant to do here
    size = ???
    data = new char[size];
  }
  ~f() { delete[] data; }

int size;
char* data;
};

f data;
int data_length = data.size;
// ...
//delete[] data; 

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 delete or delete[] outside a RAII object, you're doing it wrong. If you're writing a new or `new[] outside a RAII object, you're doing it wrong, unless the result is immediately passed to a smart pointer"

like image 199
jalf Avatar answered Oct 21 '22 03:10

jalf


In 'proper' C++ you would return an object that contains the memory allocation somewhere inside of it. Something like a std::vector.

like image 42
Zan Lynx Avatar answered Oct 21 '22 05:10

Zan Lynx