¿Cómo se 'reasigna' en C ++?

85

¿Cómo puedo hacerlo reallocen C ++? Parece que falta en el lenguaje - ¡hay newy deletepero no resize!

Lo necesito porque a medida que mi programa lee más datos, necesito reasignar el búfer para contenerlo. No creo que deleteusar el puntero antiguo y newuno nuevo y más grande sea la opción correcta.

bodacydo
fuente
8
Stroustrup respondió a esto hace mucho tiempo, consulte: www2.research.att.com/~bs/bs_faq2.html#renew (Ese es un buen comienzo si es nuevo en C ++ junto con las preguntas frecuentes sobre C ++ de Cline)
dirkgently
8
La respuesta a la que hace referencia @dirkgently está ahora en: stroustrup.com/bs_faq2.html#renew - y las preguntas frecuentes de Cline ahora son parte de las súper preguntas frecuentes: isocpp.org/faq
maxschlepzig

Respuestas:

54

Utilice :: std :: vector!

Type* t = (Type*)malloc(sizeof(Type)*n) 
memset(t, 0, sizeof(Type)*m)

se convierte en

::std::vector<Type> t(n, 0);

Entonces

t = (Type*)realloc(t, sizeof(Type) * n2);

se convierte en

t.resize(n2);

Si desea pasar el puntero a la función, en lugar de

Foo(t)

utilizar

Foo(&t[0])

Es un código C ++ absolutamente correcto, porque el vector es una matriz C inteligente.

f0b0s
fuente
1
¿No debería ser la línea Memset Memset (t, 0, sizeof (T) * n) ;? n en lugar de m?
Raphael Mayer
1
@anthom sí. realmente debería serType* t = static_cast<Type*>(malloc(n * sizeof *t));
Ryan Haining
2
Con C ++ 11, ahora se usaría en t.data()lugar de&t[0]
knedlsepp
1
¿Cómo puedes borrar esto?
a3mlord
@ a3mlord: ¿Qué quieres decir? Déjelo caer fuera de alcance y desaparecerá.
Lightness Races in Orbit
51

La opción correcta es probablemente usar un contenedor que haga el trabajo por usted, como std::vector.

newy deleteno pueden cambiar de tamaño, porque asignan suficiente memoria para contener un objeto del tipo dado. El tamaño de un tipo determinado nunca cambiará. Hay new[]y, delete[]pero casi nunca hay una razón para usarlos.

Lo que reallochace en C es probable que sea solo un malloc, memcpyy free, de todos modos, aunque los administradores de memoria pueden hacer algo inteligente si hay suficiente memoria libre contigua disponible.

Thomas
fuente
6
@bodacydo: No implemente el búfer de crecimiento, solo úselo; std::vectorcrecerá automáticamente cuando sea necesario y puede preasignar memoria si lo desea ( reserve()).
diente afilado
4
Utilice std :: vector <T>. Para eso es. En C ++, no hay ninguna razón para usar new / delete / new [] / delete [] usted mismo, a menos que esté escribiendo explícitamente clases de administración de recursos.
Puppy
4
@bod: Sí, puede. (También puede std::string, por cierto.)
fredoverflow
1
Sí, puede, no hay problema. Incluso std::stringpuedo hacer eso. Por cierto, existe la posibilidad de que su lectura de datos también se pueda simplificar. ¿Cómo estás leyendo tus datos?
Thomas
2
Parece que thevector.resize(previous_size + incoming_size), seguido de un memcpy(o similar) en &thevector[previous_size], es lo que necesita. Se garantiza que los datos del vector se almacenarán "como una matriz".
Thomas
36

Cambiar el tamaño en C ++ es incómodo debido a la posible necesidad de llamar a constructores y destructores.

No creo que haya una razón fundamental por la cual en C ++ no podrías tener un resize[]operador con quien ir new[]y delete[]eso hizo algo similar a esto:

newbuf = new Type[newsize];
std::copy_n(oldbuf, std::min(oldsize, newsize), newbuf);
delete[] oldbuf;
return newbuf;

Obviamente oldsize, se recuperaría de una ubicación secreta, en la misma ubicación delete[], y Typevendría del tipo de operando. resize[]fallaría si el tipo no se puede copiar, lo cual es correcto, ya que estos objetos simplemente no se pueden reubicar. Finalmente, el código anterior construye por defecto los objetos antes de asignarlos, lo que no querría como comportamiento real.

Hay una posible optimización en la que newsize <= oldsize, para llamar a los destructores de los objetos "más allá del final" de la matriz recién ensmallened y no hacer nada más. El estándar tendría que definir si esta optimización es necesaria (como cuando se trata de resize()un vector), permitida pero no especificada, permitida pero dependiente de la implementación o prohibida.

La pregunta que debería hacerse es, "¿es realmente útil proporcionar esto, dado que vectortambién lo hace, y está diseñado específicamente para proporcionar un contenedor que se puede cambiar de tamaño (de memoria contigua? Ese requisito se omite en C ++ 98 pero arreglado en C ++ 03) que se ajusta mejor que las matrices con las formas de hacer las cosas de C ++? "

Creo que la respuesta es generalmente "no". Si desea hacer búferes redimensionables a la manera de C, use malloc / free / realloc, que están disponibles en C ++. Si desea hacer búferes redimensionables a la manera de C ++, use un vector (o deque, si en realidad no necesita almacenamiento contiguo). No intente mezclar los dos utilizando new[]para búferes sin formato, a menos que esté implementando un contenedor similar a un vector.

Steve Jessop
fuente
0

Aquí hay un ejemplo de std :: move que implementa un vector simple con un realloc (* 2 cada vez que llegamos al límite). Si hay una manera de hacerlo mejor que la copia que tengo a continuación, hágamelo saber.

Compilar como:

  g++ -std=c++2a -O2 -Wall -pedantic foo.cpp

Código:

#include <iostream>
#include <algorithm>

template<class T> class MyVector {
private:
    T *data;
    size_t maxlen;
    size_t currlen;
public:
    MyVector<T> () : data (nullptr), maxlen(0), currlen(0) { }
    MyVector<T> (int maxlen) : data (new T [maxlen]), maxlen(maxlen), currlen(0) { }

    MyVector<T> (const MyVector& o) {
        std::cout << "copy ctor called" << std::endl;
        data = new T [o.maxlen];
        maxlen = o.maxlen;
        currlen = o.currlen;
        std::copy(o.data, o.data + o.maxlen, data);
    }

    MyVector<T> (const MyVector<T>&& o) {
        std::cout << "move ctor called" << std::endl;
        data = o.data;
        maxlen = o.maxlen;
        currlen = o.currlen;
    }

    void push_back (const T& i) {
        if (currlen >= maxlen) {
            maxlen *= 2;
            auto newdata = new T [maxlen];
            std::copy(data, data + currlen, newdata);
            if (data) {
                delete[] data;
            }
            data = newdata;
        }
        data[currlen++] = i;
    }

    friend std::ostream& operator<<(std::ostream &os, const MyVector<T>& o) {
        auto s = o.data;
        auto e = o.data + o.currlen;;
        while (s < e) {
            os << "[" << *s << "]";
            s++;
        }
        return os;
    }
};

int main() {
    auto c = new MyVector<int>(1);
    c->push_back(10);
    c->push_back(11);
}
Neil McGill
fuente
-7

prueba algo como eso:

typedef struct Board
{
    string name;
    int size = 0;
};

typedef struct tagRDATA
{
    vector <Board> myBoards(255);

    // Board DataBoard[255];
    int SelectedBoard;

} RUNDATA;

Vector se quejará. Es por eso que aún existen arrays, malloc y new.

algún invitado
fuente
12
No, no es por eso. Y no veo cómo esto responde a la pregunta de ninguna manera. O por qué está utilizando typedefen todas partes como si estuviera escribiendo C
Luminosidad razas en órbita