2

There is no realloc in C++; Of course you can always allocate a new block with new, copy the old block to the new block, free the old block with delete.

The problem is that realloc can often be faster than this - this happens when the memory block is re-sized 'in place' - that is the return value of realloc happens to be the same as the argument pointer value; this also avoids the copying of memory from old block to new block.

What does the standard say - is one allowed to pass pointer returned by the global new operator to realloc? (in glibc both new and malloc are using the same allocator, so there should not be a practical problem here). Does the C++ standard have anything to say on this subject?

(the current glibc standard C++ library does not do realloc, even in vector - because once an object has been allocated the runtime can't call the destructor in place on the old memory block and call placement new on the new block - if the memory block has been moved by realloc that is. It would have been simpler if the standard library would have had an in-place realloc that can't move the argument block to a new location)

edit:

Actually some memory allocators have an in-place realloc function; these are of course non-standard.

  • jemalloc has an API called rallocm with flag ALLOCM_NO_MOVE
  • dlmalloc (Doug Lea malloc) has an API called dlrealloc_in_place

If you use jemalloc or dlmalloc for global operator new and delete, then you could use these non standard functions for a vector class that attempts in-place realloc before it does a moving malloc with copying. Or one could petition the C++ standard committee to add a global non-moving reallocation method to the standard.

edit:

or you can do a vector class where memory management is done via malloc/realloc/free, but this implementation may only be instantiated if the element type is plain old data (i.e. if std::is_pod returns true on the type that the vector is holding).

Community
  • 1
  • 1
MichaelMoser
  • 3,172
  • 1
  • 26
  • 26
  • 2
    `realloc` can only be used on pointers that have been allocated by `malloc`, `calloc` or `realloc`. – Jabberwocky Nov 11 '15 at 11:06
  • 3
    It is useful to read: [_Why doesn't C++ have an equivalent to realloc()?_](http://stackoverflow.com/a/16715066/952747) – masoud Nov 11 '15 at 11:29
  • There's exactly 0% chance of the Committee adding a global non-moving reallocation method to the standard. – Nik Bougalis Nov 11 '15 at 12:05
  • 1
    Let the standard library implementors worry about this stuff. If there is a faster way to grow a vector, it is safe to assume they will find it and usw it. – n. m. could be an AI Nov 11 '15 at 12:20
  • 2
    I tried and failed to put realloc-like functionality under the hood of vector: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1953.html – Howard Hinnant Nov 11 '15 at 15:15
  • Great work @HowardHinnant ; i guess one of the problems is that it is hard to quantify when in-place realloc applies; each case depends on the implementation of malloc - the size of the bins, etc. Sometimes it does save a bit of copying, sometimes it doesn't ... – MichaelMoser Nov 11 '15 at 17:07
  • 1
    I took it to the C committee in an attempt to tame realloc: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1085.htm That also failed. – Howard Hinnant Nov 11 '15 at 17:13

2 Answers2

6

No, you can't mix free/realloc with the built in new.

Much of the functionality you're seeking may be available using std::vector. It's guaranteed (AFAIK) that the objects in the vector to be stored contiguously (just like an old style array), you can get the pointer to the storage and you can resize the array in a standard defined way. The only thing that seems to be lacking is the possibility to supply your own storage (ie have a T* and get a std::vector<T> with that storage).

What you otherwise might do if you really need to (check the below drawbacks first) is to replace the default implementation of new and delete to use malloc and free:

void* operator new(size_t n)
{
    return malloc(n);
}

void operator delete(void* p)
{
     free(p);
}

after doing that, you can use realloc on a pointer got by new.

As pointed out by Michael Walz you need to take care if the objects you're allocating requires initialization. It could be even worse if your object can't be moved by just copying the memory (which realloc does if it can't resize the block in place). This means that it's useless on anything else than dumb structs (and even then you'll end up with C like semantics with uninitialized data anyway - not even zero-initialized).

In addition malloc, free and realloc doesn't play well with C++ stronger typing. This

skyking
  • 13,817
  • 1
  • 35
  • 57
  • 1
    And who calls the constructors of the objects contained in the newly allocated memory once you have called `realloc` ? – Jabberwocky Nov 11 '15 at 11:08
  • @MichaelWalz The compiler should provide for calling the `operator new` before calling the constructor (and calling the constructor after the memory has been allocated). – skyking Nov 11 '15 at 11:10
  • You need to be careful with this solution: If the `new`-call is part of a library that wasn't compiled with your `new` function it will still use the default implementation. – Simon Kraemer Nov 11 '15 at 11:11
  • @SimonKraemer I was of the impression that the library should use the replaced `operator new`, but I may be mistaken. Do you have a source for your claim? – skyking Nov 11 '15 at 11:14
  • 3
    Consider this : `myclass *p = new myclass[10];`. The `myclass` constructor will be called 10 times, once for each element of the array. Now you have `p = (myclass*)realloc(15 * sizeof(myclass));`. Do you think the `myclass` constructor will be called 5 times? Of course not, `p[10]` to `p[14]` will just contain garbage pointers. – Jabberwocky Nov 11 '15 at 11:17
  • @skyking A library is already compiled and therefore it only uses binary code. If you now redefine `operator new` in a program that uses the library this won't change the binary code *in* the library. So the library will still use the "old" `new` while the program uses the "new" `new`. – Simon Kraemer Nov 11 '15 at 11:24
  • @SimonKraemer It's compiled, but it's not linked - that happens when you link your program with it. For example I tried this on `gcc` by issuing a array allocation and it ends up in my defined `operator new` even though it is called from a library function (`operator new[]`). – skyking Nov 11 '15 at 11:41
  • @MichaelWalz Ah, I thought you meant at the location of `new myclass[10]` - there it's straight forward the compiler makes sure you allocate space for 10 objects and then calls the constructor 10 times. When you then call `realloc` the compiler will only call `realloc`, you have to make sure that you construct/destruct the elements if needed. – skyking Nov 11 '15 at 11:44
  • @skyking I get what's the problem... I was talking about dynamic libraries (.dll/.so). I found [this SO entry](http://stackoverflow.com/questions/21997668/operator-new-and-delete-overloading-scope) that confirms what you said for static library and (more or less) what I said for dynamic libraries. I work with DLLs most of the time, so... – Simon Kraemer Nov 11 '15 at 11:58
  • @SimonKraemer My test with `gcc` actually uses dynamic library for defining `operator new[]` so it seems to work there though. I disagree with the discussion about static/dynamic linking - the standard doesn't say how the executable should be brought together, that is whether you use dynamic or static linking is an implementation detail and I mean that the implementation is not excused from the standard by choosing a way to bring the executable together that doesn't support the requirements in the standard. – skyking Nov 11 '15 at 12:07
  • 2
    This is just a bad idea, no matter how you cut, slice or dice it. If you're considering this approach, **STOP NOW**. – Nik Bougalis Nov 11 '15 at 12:08
  • 1
    @skyking It didn't work for me the last time I tried it and it seems it didn't work for other people as well. Maybe we did something wrong, maybe we didn't. It doesn't matter. As you said yourself *"the standard doesn't say how the executable should be brought together,"*. As long as this is the case overloading `operator new` is something that should prevented IMHO. – Simon Kraemer Nov 11 '15 at 12:28
  • @SimonKraemer It's not overloading - the standard refers it to as **replacing** and the standard is IMHO quite clear about the meaning of it. It's a pity if some implementation doesn't implement it correctly. Replacing `new` and `delete` is useful for other scenarios than to allow mixing C and C++ memory allocation functions. – skyking Nov 11 '15 at 12:31
  • @NikBougalis Did you downvote with that comment? I've updated the answer and I think it's now clearer that one should prefer `std::vector` instead in many of the situation where using `realloc` would be tempting. – skyking Nov 11 '15 at 12:45
  • No, I did not. I think the answer combined with the comments is fine and there's no justification for a doe vote. – Nik Bougalis Nov 11 '15 at 12:46
  • @skyking I just saw I did accidentially. Sorry about that. – Simon Kraemer Nov 11 '15 at 14:28
  • @skyking I just did some recherche on what went wrong when I tried to overload/replace new the last time. Looks like I need to recall most of what I said: I tried to find a memory leak by implementing `void *operator new(size_t, const char *, int);` which was then called by redining new with `#define new new(__FILE__, __LINE__)` (same with `delete`). Of course this won't affect any already compiled code. Mea culpa. – Simon Kraemer Nov 11 '15 at 14:44
2

There is no realloc in c++;

And it's a good thing too! It's a horrible idea. It's not a function - it's a whole family of functions, all rolled into one.

  • Want to use realloc instead of malloc? You can!
  • Want to use realloc instead of free? You can!
  • Want to grow a memory block? Yes, yes you can!
  • Want to shrink it? Why yes, you can do this too.
  • Want to do nothing at all with it? Of course you can. Did you even need to ask?

This bloated interface makes it impossible to use correctly and reliably and even if you suspect an error, the fact that are essentially no incorrect inputs make it hard to debug. Combined with the fact that the result is more often than not used incorrectly, you have, at best, a recipe for memory leaks!

So please, don't think of new ways to use realloc - especially in C++. And if you see code using it change it to not use it. reallocneeds to die.

Of course you can always allocate a new block with new, copy the old block to the new block, free the old block with delete.

Yes, you can and should do this. It helps explain exactly what's happening, it's less error-prone than calling realloc and determining if it succeeded or failed, and will always do the right thing when allocating arrays by invoking constructors and destructors at the right time.

The problem is that realloc can often be faster than this - this happens when the memory block is re-sized 'in place' - that is the return value of realloc happens to be the same as the argument pointer value; this also avoids the copying of memory from old block to new block.

With all due respect, the biggest bottleneck of your program is not likely to be memory copying. And starting with C++11 which gave us move semantics, this is more likely to be true than ever.

Of course just because it's unlikely it doesn't mean it's impossible. If you really need to optimize this case (as demonstrated by a thorough performance analysis), you're likely to be better off contemplating ways to avoid the need to reallocate, or spending time tuning your application and adjusting its behavior to cause it allocate more memory from the get-go, reducing the need and/or frequency of reallocations.

Nik Bougalis
  • 10,495
  • 1
  • 21
  • 37