Wie ordnen Sie C ++ neu zu?

83

Wie kann ich reallocin C ++? Es scheint in der Sprache zu fehlen - es gibt newund deleteaber nicht resize!

Ich brauche es, weil mein Programm, wenn es mehr Daten liest, den Puffer neu zuweisen muss, um es zu halten. Ich denke nicht, dass es die richtige Option ist, den deletealten Zeiger und neweinen neuen, größeren Zeiger zu verwenden .

bodacydo
quelle
8
Stroustrup hat dies vor langer Zeit beantwortet, siehe: www2.research.att.com/~bs/bs_faq2.html#renew (Das ist ein guter Anfang, wenn Sie zusammen mit Clines C ++ - FAQ neu in C ++ sind.)
dirkgently
8
Die Antwort, auf die @dirkgently verweist, lautet jetzt: stroustrup.com/bs_faq2.html#renew - und Clines FAQ ist jetzt Teil der Super-FAQ: isocpp.org/faq
maxschlepzig

Antworten:

52

Verwenden Sie :: std :: vector!

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

wird

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

Dann

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

wird

t.resize(n2);

Wenn Sie den Zeiger anstelle der Funktion an die Funktion übergeben möchten

Foo(t)

verwenden

Foo(&t[0])

Es ist absolut korrekter C ++ - Code, da der Vektor ein intelligentes C-Array ist.

f0b0s
quelle
1
Sollte die Memset-Zeile nicht Memset sein (t, 0, Größe von (T) * n)? n statt m?
Raphael Mayer
1
@anthom ja. es sollte wirklich seinType* t = static_cast<Type*>(malloc(n * sizeof *t));
Ryan Haining
2
Mit C ++ 11 würde man jetzt t.data()anstelle von&t[0]
knedlsepp
1
Wie können Sie dies dann löschen?
a3mlord
@ a3mlord: Was meinst du? Lassen Sie es aus dem Rahmen fallen, und es ist weg.
Leichtigkeitsrennen im Orbit
51

Die richtige Option ist wahrscheinlich, einen Container zu verwenden, der die Arbeit für Sie erledigt, wie z std::vector.

newund deletekönnen die Größe nicht ändern, da sie gerade genug Speicher zuweisen, um ein Objekt des angegebenen Typs aufzunehmen. Die Größe eines bestimmten Typs wird sich nie ändern. Es gibt new[]und delete[]aber es gibt kaum einen Grund, sie zu benutzen.

Was reallocin C funktioniert, ist wahrscheinlich nur ein malloc, memcpyund auf freejeden Fall , obwohl Speichermanager etwas Kluges tun dürfen, wenn genügend zusammenhängender freier Speicher verfügbar ist.

Thomas
quelle
6
@bodacydo: Implementieren Sie den wachsenden Puffer nicht, std::vectorsondern verwenden Sie ihn einfach - er wächst bei Bedarf automatisch und Sie können Speicher vorab zuweisen, wenn Sie möchten ( reserve()).
Scharfzahn
4
Verwenden Sie std :: vector <T>. Dafür ist es da. In C ++ gibt es keinen Grund, new / delete / new [] / delete [] selbst zu verwenden, es sei denn, Sie schreiben explizit Ressourcenverwaltungsklassen.
Welpe
4
@ Bod: Ja, das kann es. (Kann std::stringübrigens auch.)
Fredoverflow
1
Ja, das kann kein Problem. Auch std::stringkann das tun. Übrigens besteht die Möglichkeit, dass auch das Lesen Ihrer Daten vereinfacht wird. Wie lesen Sie Ihre Daten?
Thomas
2
Klingt wie thevector.resize(previous_size + incoming_size), gefolgt von einem memcpy(oder ähnlichen) in &thevector[previous_size], ist das, was Sie brauchen. Die Daten des Vektors werden garantiert "wie ein Array" gespeichert.
Thomas
35

Die Größenänderung in C ++ ist umständlich, da möglicherweise Konstruktoren und Destruktoren aufgerufen werden müssen.

Ich glaube nicht, dass es einen fundamentalen Grund gibt, warum Sie in C ++ keinen resize[]Operator haben könnten, new[]und delete[]das hat etwas Ähnliches bewirkt:

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

Offensichtlich oldsizewürde von einem geheimen Ort abgerufen werden, genauso wie es ist delete[], und Typewürde vom Typ des Operanden kommen. resize[]würde fehlschlagen, wenn der Typ nicht kopierbar ist - was richtig ist, da solche Objekte einfach nicht verschoben werden können. Schließlich konstruiert der obige Code die Objekte standardmäßig, bevor sie zugewiesen werden, was Sie nicht als tatsächliches Verhalten wünschen würden.

Es gibt eine mögliche Optimierung newsize <= oldsize, bei der Destruktoren für die Objekte "nach dem Ende" des neu zusammengestellten Arrays aufgerufen werden und nichts anderes getan wird. Der Standard müsste definieren, ob diese Optimierung erforderlich ist (wie bei resize()einem Vektor), zulässig, aber nicht spezifiziert, zulässig, aber implementierungsabhängig oder verboten.

Die Frage, die Sie sich dann stellen sollten, lautet: "Ist es tatsächlich nützlich, dies bereitzustellen, da dies vectorauch der Fall ist, und wurde speziell entwickelt, um einen Container mit veränderbarer Größe (mit zusammenhängendem Speicher - diese Anforderung wurde in C ++ 98 jedoch weggelassen) bereitzustellen." behoben in C ++ 03) passt das besser als Arrays zu den C ++ - Methoden? "

Ich denke, die Antwort wird allgemein als "nein" angesehen. Wenn Sie anpassbare Puffer auf C-Weise erstellen möchten, verwenden Sie diese malloc / free / realloc, die in C ++ verfügbar sind. Wenn Sie die Größe von Puffern in C ++ ändern möchten, verwenden Sie einen Vektor (oder deque, wenn Sie keinen zusammenhängenden Speicher benötigen). Versuchen Sie nicht, beide mithilfe von Rohpuffern zu mischen new[], es sei denn, Sie implementieren einen vektorähnlichen Container.

Steve Jessop
quelle
0

Hier ist ein Beispiel für std :: move, das einen einfachen Vektor mit einem Realloc implementiert (* 2 jedes Mal, wenn wir das Limit erreichen). Wenn es einen Weg gibt, es besser zu machen als die Kopie, die ich unten habe, lassen Sie es mich bitte wissen.

Kompilieren als:

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

Code:

#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
quelle
-7

versuchen Sie so etwas:

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

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

    // Board DataBoard[255];
    int SelectedBoard;

} RUNDATA;

Vector wird sich beschweren. Deshalb gibt es immer noch Arrays, Malloc und New.

ein Gast
quelle
12
Nein, das ist nicht der Grund. Und ich sehe nicht, wie dies die Frage in irgendeiner Weise beantwortet. Oder warum Sie typedefüberall verwenden, als ob Sie C schreiben würden.
Leichtigkeitsrennen im Orbit