How do you 'realloc' in C++?
Solution 1
Use ::std::vector!
Type* t = (Type*)malloc(sizeof(Type)*n)
memset(t, 0, sizeof(Type)*m)
becomes
::std::vector<Type> t(n, 0);
Then
t = (Type*)realloc(t, sizeof(Type) * n2);
becomes
t.resize(n2);
If you want to pass pointer into function, instead of
Foo(t)
use
Foo(&t[0])
It is absolutely correct C++ code, because vector is a smart C-array.
Solution 2
The right option is probably to use a container that does the work for you, like std::vector
.
new
and delete
cannot resize, because they allocate just enough memory to hold an object of the given type. The size of a given type will never change. There are new[]
and delete[]
but there's hardly ever a reason to use them.
What realloc
does in C is likely to be just a malloc
, memcpy
and free
, anyway, although memory managers are allowed to do something clever if there is enough contiguous free memory available.
Solution 3
Resizing in C++ is awkward because of the potential need to call constructors and destructors.
I don't think there's a fundamental reason why in C++ you couldn't have a resize[]
operator to go with new[]
and delete[]
, that did something similar to this:
newbuf = new Type[newsize];
std::copy_n(oldbuf, std::min(oldsize, newsize), newbuf);
delete[] oldbuf;
return newbuf;
Obviously oldsize
would be retrieved from a secret location, same is it is in delete[]
, and Type
would come from the type of the operand. resize[]
would fail where the Type is not copyable - which is correct, since such objects simply cannot be relocated. Finally, the above code default-constructs the objects before assigning them, which you would not want as the actual behaviour.
There's a possible optimisation where newsize <= oldsize
, to call destructors for the objects "past the end" of the newly-ensmallened array and do nothing else. The standard would have to define whether this optimisation is required (as when you resize()
a vector), permitted but unspecified, permitted but implementation-dependent, or forbidden.
The question you should then ask yourself is, "is it actually useful to provide this, given that vector
also does it, and is designed specifically to provide a resize-able container (of contiguous memory--that requirement omitted in C++98 but fixed in C++03) that's a better fit than arrays with the C++ ways of doing things?"
I think the answer is widely thought to be "no". If you want to do resizeable buffers the C way, use malloc / free / realloc
, which are available in C++. If you want to do resizeable buffers the C++ way, use a vector (or deque
, if you don't actually need contiguous storage). Don't try to mix the two by using new[]
for raw buffers, unless you're implementing a vector-like container.
bodacydo
My name is Boda Cydo. I am from Africa but now live in Washington DC.
Updated on July 08, 2022Comments
-
bodacydo almost 2 years
How can I
realloc
in C++? It seems to be missing from the language - there isnew
anddelete
but notresize
!I need it because as my program reads more data, I need to reallocate the buffer to hold it. I don't think
delete
ing the old pointer andnew
ing a new, bigger one, is the right option. -
bodacydo almost 14 yearsSo what would be the right way to implement a growing buffer in C++? Currently I have
char *buf = (char *)malloc(size)
, then when it becomes too small I dobuf = realloc(size + more_size); size += more_size
. How can I do it with vector? -
sharptooth almost 14 years@bodacydo: Don't implement the growing buffer, just use
std::vector
- it will grow automatically when needed and you can pre-allocate memory if you want (reserve()
). -
Puppy almost 14 yearsUse std::vector<T>. That's what it's for. In C++, there is no reason whatsoever to use new/delete/new[]/delete[] yourself, unless you're explicitly writing resource management classes.
-
bodacydo almost 14 yearsSo should I use
std::vector<char>
? I am curious ifstd::vector<char>
can contain0
(NUL) bytes? -
fredoverflow almost 14 years@bod: Yes, it can. (So can
std::string
, by the way.) -
Keyur Padalia almost 14 yearsYes, it can, no problem. Even
std::string
can do that. By the way, there's a chance that your data reading can be simplified, too. How are you reading your data? -
bodacydo almost 14 yearsThomas: the data comes in from the network, and I don't really have control over how much of it is gonna come in. It works like this - a packet comes in and says 100 bytes are following. So I allocate 100 bytes, then there are several types of packets, one might say "200 more bytes", so I do realloc and size += 200.
-
Keyur Padalia almost 14 yearsSounds like
thevector.resize(previous_size + incoming_size)
, followed by amemcpy
(or similar) into&thevector[previous_size]
, is what you need. The vector's data is guaranteed to be stored "like an array". -
bodacydo almost 14 yearsThomas. I just wrote my code with
push_back
and it didn't work. Thanks for explaining the .resize() followed by memcpy. -
David Rodríguez - dribeas almost 14 years@bodacydo: Read the docs. The
v.push_back(d)
is semantically equivalent tov.resize(v.size()+1); v.back() = d;
That will allocate an extra element in the array and insert the data in that position. -
CB Bailey almost 14 years@dribeas: Better than that;
v.push_back(d)
is semantically equivalent tov.resize(v.size() + 1, d)
; there's no requirement for a default constructed temporary or an assignment operation. -
Raphael Mayer over 9 yearsShouldnt the memset line be memset(t, 0, sizeof(T) * n);? n instead of m?
-
Ryan Haining over 8 years@anthom yes. it should really be
Type* t = static_cast<Type*>(malloc(n * sizeof *t));
-
knedlsepp over 8 yearsWith C++11 one would now use
t.data()
instead of&t[0]
-
a3mlord over 8 yearsHow can you then delete this?
-
Lightness Races in Orbit about 8 years@a3mlord: What do you mean? Let it fall out of scope, and it's gone.
-
Olivier D'Ancona about 3 yearsWhen you want to build your own datastructure you should handle this completely. Basically, if your buffer is full you'll need to do the following operations: 1. Multiply your buffer capacity( x2 or x1.5 like Visual C++) 2. Allocate a memory space of your new capacity 3. Move your old buffer into the new memory space 4. Free the memory of your old buffer 5. Set your datastructure to point to the new memory space
-
Bruce Adams about 2 yearsThere were some interesting proposals related to resizing way back in 2006 which didn't gain traction at the time open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1953.html open-std.org/jtc1/sc22/wg14/www/docs/n1085.htm open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2045.html . I don't know if there are more recent versions.
-
Dmitry Ivanov about 2 yearslooking to specification,
realloc
expanding or contracting the existing area pointed to by ptr, if possible. The contents of the area remain unchanged up to the lesser of the new and old sizes. If the area is expanded, the contents of the new part of the array are undefined. So it looks like usingvector
not absolutely correct advice. I'm using new+memcpy+delete combination when need migrate legacy to the new style.