Why is there no realloc equivalent to the new/delete family?

Viewed 398

As the title says, I know there is no equivalent to C's realloc in the new/delete family of operators.

I have already found this question that lightly touches on the subject but it doesn't really answer the "why".

My questions are:

  1. Why is it a bad idea to be able to realloc objects?
  2. Why is it a bad idea for an object to change its size? (Implementing a collection seems a perfectly valid reason for an object to change its size.)
  3. What rule would be broken in these cases and why is this rule objectively good?
3 Answers

Realloc has two behaviors, one of them is not acceptable in the C++ object model. Realloc can increase the size of a piece of storage, or it can allocate new storage and copy everything from the old storage into the new.

The thing is, C++ doesn't think of objects as just bags of bits. They're living, breathing types that hold invariants. And some of those invariants don't tolerate having their bits copied around well.

In C++, copying an object's bits does not mean you have effectively copied the object. This is only allowed for trivially copyable types, and there are plenty of types that aren't trivially copyable.

As such, a C++ realloc equivalent cannot be used on any allocation. You would need to split the call into two separate calls: one that attempts to expand the memory and does nothing if it can't, and the regular heap allocation call into which you would manually copy using existing C++ techniques.


As one example, many std::list implementations store a terminator node in the std::list object itself which is used to represent the start/end of the linked list. If you simply copied its bits, pointers to the terminator node would point to the old allocation that is now gone.

That's bad.

In order to allow an object to have arbitrary class invariants that the code which accesses those types can maintain, it is necessary to treat an object as something more than just the bits of its object representation. And most C++ types maintain some invariant for which its object representation cannot survive bitwise copying.

You cannot change the storage of an existing object in C++. The only what you can do is to create new objects — in "reallocated" memory — that will have the same content as the original objects. This is exactly what std::vector is capable of.

A Problem with C++ is that this functionality involves generally much more than just copying bytes. Copying the content of objects by copying their binary representation is enabled only for a limited set of types — so-called trivially-copyalbe types. For the other ones, copy/move constructors and destructors need to be involved.

There is no such functionality because the C library doesn't provide a suitable interface to implement one. new and delete are implementable in terms of malloc and free, but the hypothetical renew is not implementable in terms of realloc, because you cannot move bytes of a C++ object (others have pointed out and explained this fact). When C++ was in its early stages, it was an important consideration, and it still is to some extent. You usually don't want to write your own, possibly inferior, allocator, when you can just piggy-back on top of very mature and proven malloc and friends.

It is possible to implement renew in terms of a low-level allocator that provides a function like try_realloc. Check if the block can grow in situ, grow it if yes, allocate a new block and move existing objects to it if not. Win-win? Apparently it turns out that this functionality is not too important. std::vector de-facto policy of doubling the allocated size works just fine and provides very good performance, so why bother?

Related