Come si "rialloca" in C ++?


87

Come posso reallocin C ++? Sembra che manchi dalla lingua - c'è newe deletema non resize!

Ne ho bisogno perché poiché il mio programma legge più dati, ho bisogno di riallocare il buffer per conservarlo. Non credo che deleteil vecchio puntatore e newil nuovo, più grande, sia l'opzione giusta.


8
Stroustrup ha risposto molto tempo fa, vedere: www2.research.att.com/~bs/bs_faq2.html#renew (È un buon inizio se sei nuovo in C ++ insieme alle FAQ di Cline su C ++.)
Dirkgently

9
La risposta a cui fa riferimento @dirkgently è ora su: stroustrup.com/bs_faq2.html#renew - e le FAQ di Cline ora fanno parte delle super FAQ: isocpp.org/faq
maxschlepzig

Risposte:


54

Usa :: std :: vector!

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

diventa

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

Poi

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

diventa

t.resize(n2);

Se vuoi passare il puntatore alla funzione, invece di

Foo(t)

uso

Foo(&t[0])

È codice C ++ assolutamente corretto, perché vector è un C-array intelligente.


1
La riga memset non dovrebbe essere memset (t, 0, sizeof (T) * n) ;? n invece di m?
Raphael Mayer

1
@anthom yes. dovrebbe essere davveroType* t = static_cast<Type*>(malloc(n * sizeof *t));
Ryan Haining

2
Con C ++ 11 ora si userebbe t.data()invece di&t[0]
knedlsepp

1
Come puoi quindi eliminarlo?
A3mlord

@ a3mlord: cosa intendi? Lascia che cada fuori campo e non c'è più.
Gare di leggerezza in orbita il

50

L'opzione giusta è probabilmente quella di utilizzare un contenitore che fa il lavoro per te, come std::vector.

newe deletenon possono ridimensionare, perché allocano una quantità di memoria sufficiente per contenere un oggetto del tipo specificato. La dimensione di un dato tipo non cambierà mai. Ci sono new[]e delete[]ma non c'è quasi mai un motivo per usarli.

Che cosa reallocsignifica in C è probabile che sia solo una malloc, memcpye free, in ogni caso, anche se i gestori di memoria sono autorizzati a fare qualcosa di intelligente se c'è abbastanza memoria libera contigua a disposizione.


6
@bodacydo: non implementare il buffer in crescita, usalo semplicemente: std::vectorcrescerà automaticamente quando necessario e puoi pre-allocare la memoria se vuoi ( reserve()).
punta di diamante

4
Usa std :: vector <T>. Ecco a cosa serve. In C ++, non c'è alcun motivo per utilizzare new / delete / new [] / delete [] da soli, a meno che non si stia scrivendo esplicitamente classi di gestione delle risorse.
Cucciolo

4
@bod: Sì, può. (Così può std::string, a proposito.)
fredoverflow

1
Sì, può, nessun problema. Anche std::stringpuò farlo. A proposito, c'è la possibilità che anche la lettura dei dati possa essere semplificata. Come stai leggendo i tuoi dati?
Thomas

2
Sembra thevector.resize(previous_size + incoming_size), seguito da un memcpy(o simile) in &thevector[previous_size], è ciò di cui hai bisogno. È garantito che i dati del vettore siano memorizzati "come un array".
Thomas

38

Il ridimensionamento in C ++ è scomodo a causa della potenziale necessità di chiamare costruttori e distruttori.

Non penso che ci sia una ragione fondamentale per cui in C ++ non potresti avere un resize[]operatore con cui andare new[]e delete[], che ha fatto qualcosa di simile a questo:

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

Ovviamente oldsizeverrebbe recuperato da una posizione segreta, lo stesso in cui si trova delete[], e Typeverrebbe dal tipo di operando. resize[]fallirebbe dove il tipo non è copiabili, il che è corretto, poiché tali oggetti semplicemente non possono essere riposizionati. Infine, il codice precedente costruisce in modo predefinito gli oggetti prima di assegnarli, cosa che non vorresti come comportamento effettivo.

Esiste una possibile ottimizzazione in cui newsize <= oldsizechiamare i distruttori per gli oggetti "oltre la fine" dell'array appena ridotto e non fare nient'altro. Lo standard dovrebbe definire se questa ottimizzazione è richiesta (come quando si tratta di resize()un vettore), consentita ma non specificata, consentita ma dipendente dall'implementazione o vietata.

La domanda che dovresti quindi porti è: "è effettivamente utile fornire questo, dato che vectorlo fa anche, ed è progettato specificamente per fornire un contenitore ridimensionabile (di memoria contigua - quel requisito è stato omesso in C ++ 98 ma risolto in C ++ 03) che si adatta meglio agli array con i modi di fare le cose C ++? "

Penso che la risposta sia ampiamente ritenuta "no". Se vuoi creare buffer ridimensionabili in C, usa malloc / free / realloc, che sono disponibili in C ++. Se vuoi creare buffer ridimensionabili in modo C ++, usa un vettore (o deque, se non hai effettivamente bisogno di archiviazione contigua). Non provare a mescolare i due utilizzando new[]per i buffer non elaborati, a meno che non stai implementando un contenitore simile a un vettore.


0

Ecco un esempio std :: move che implementa un vettore semplice con un realloc (* 2 ogni volta che raggiungiamo il limite). Se c'è un modo per fare meglio della copia che ho di seguito, fammelo sapere.

Compila come:

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

Codice:

#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);
}

-7

prova qualcosa del genere:

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

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

    // Board DataBoard[255];
    int SelectedBoard;

} RUNDATA;

Vector si lamenterà. Ecco perché esistono ancora array, malloc e new.


12
No, non è questo il motivo. E non vedo come questo risponda in alcun modo alla domanda. O perché stai usando typedefovunque come se stessi scrivendo C.
Lightness Races in Orbit
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.