How do you 'realloc' in C++?

92,196

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.

Share:
92,196
bodacydo
Author by

bodacydo

My name is Boda Cydo. I am from Africa but now live in Washington DC.

Updated on July 08, 2022

Comments

  • bodacydo
    bodacydo almost 2 years

    How can I realloc in C++? It seems to be missing from the language - there is new and delete but not resize!

    I need it because as my program reads more data, I need to reallocate the buffer to hold it. I don't think deleteing the old pointer and newing a new, bigger one, is the right option.

  • bodacydo
    bodacydo almost 14 years
    So 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 do buf = realloc(size + more_size); size += more_size. How can I do it with vector?
  • sharptooth
    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
    Puppy almost 14 years
    Use 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
    bodacydo almost 14 years
    So should I use std::vector<char>? I am curious if std::vector<char> can contain 0 (NUL) bytes?
  • fredoverflow
    fredoverflow almost 14 years
    @bod: Yes, it can. (So can std::string, by the way.)
  • Keyur Padalia
    Keyur Padalia almost 14 years
    Yes, 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
    bodacydo almost 14 years
    Thomas: 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
    Keyur Padalia almost 14 years
    Sounds like thevector.resize(previous_size + incoming_size), followed by a memcpy (or similar) into &thevector[previous_size], is what you need. The vector's data is guaranteed to be stored "like an array".
  • bodacydo
    bodacydo almost 14 years
    Thomas. 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
    David Rodríguez - dribeas almost 14 years
    @bodacydo: Read the docs. The v.push_back(d) is semantically equivalent to v.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
    CB Bailey almost 14 years
    @dribeas: Better than that; v.push_back(d) is semantically equivalent to v.resize(v.size() + 1, d); there's no requirement for a default constructed temporary or an assignment operation.
  • Raphael Mayer
    Raphael Mayer over 9 years
    Shouldnt the memset line be memset(t, 0, sizeof(T) * n);? n instead of m?
  • Ryan Haining
    Ryan Haining over 8 years
    @anthom yes. it should really be Type* t = static_cast<Type*>(malloc(n * sizeof *t));
  • knedlsepp
    knedlsepp over 8 years
    With C++11 one would now use t.data() instead of &t[0]
  • a3mlord
    a3mlord over 8 years
    How can you then delete this?
  • Lightness Races in Orbit
    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
    Olivier D'Ancona about 3 years
    When 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
    Bruce Adams about 2 years
    There 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
    Dmitry Ivanov about 2 years
    looking 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 using vector not absolutely correct advice. I'm using new+memcpy+delete combination when need migrate legacy to the new style.