Como você 'realoca' em C ++?

87

Como posso reallocem C ++? Parece faltar na linguagem - existe newe deletenão existe resize!

Eu preciso disso porque, à medida que meu programa lê mais dados, preciso realocar o buffer para mantê-los. Não acho que deleteo ponteiro antigo e o newnovo, maior, sejam a opção certa.

bodacydo
fonte
8
Stroustrup respondeu a isso há muito tempo, consulte: www2.research.att.com/~bs/bs_faq2.html#renew (Esse é um bom começo se você for novo em C ++ junto com as FAQs de C ++ de Cline.)
dirkgently
9
A resposta referenciada por @dirkgently está agora em: stroustrup.com/bs_faq2.html#renew - e o FAQ de Cline agora faz parte do super FAQ: isocpp.org/faq
maxschlepzig

Respostas:

54

Use :: std :: vector!

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

torna-se

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

Então

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

torna-se

t.resize(n2);

Se você quiser passar o ponteiro para a função, em vez de

Foo(t)

usar

Foo(&t[0])

É um código C ++ absolutamente correto, porque vetor é um array C inteligente.

f0b0s
fonte
1
A linha memset não deveria ser memset (t, 0, sizeof (T) * n) ;? n em vez de m?
Raphael Mayer
1
@anthom sim. realmente deveria serType* t = static_cast<Type*>(malloc(n * sizeof *t));
Ryan Haining,
2
Com C ++ 11, agora seria usado em t.data()vez de&t[0]
knedlsepp de
1
Como você pode excluir isso?
a3mlord
@ a3mlord: O que você quer dizer? Deixe sair do escopo e ele se foi.
Lightness Races in Orbit de
50

A opção certa é provavelmente usar um contêiner que faça o trabalho para você, como std::vector.

newe deletenão podem ser redimensionados, porque alocam apenas memória suficiente para armazenar um objeto de um determinado tipo. O tamanho de um determinado tipo nunca mudará. Há new[]e delete[]dificilmente há uma razão para usá-los.

O que reallocsignifica em C provavelmente será apenas um malloc, memcpye free, de qualquer maneira, embora os gerenciadores de memória possam fazer algo inteligente se houver memória livre contígua suficiente disponível.

Thomas
fonte
6
@bodacydo: Não implemente o buffer crescente, apenas use std::vector- ele aumentará automaticamente quando necessário e você pode pré-alocar memória se quiser ( reserve()).
sharptooth
4
Use std :: vector <T>. É para isso que serve. Em C ++, não há nenhuma razão para usar new / delete / new [] / delete [] por conta própria, a menos que você esteja explicitamente escrevendo classes de gerenciamento de recursos.
Cachorro de
4
@bod: Sim, pode. (A std::stringpropósito, também pode .)
fredoverflow
1
Sim, pode, sem problemas. Até std::stringpode fazer isso. A propósito, há uma chance de que a leitura de seus dados também possa ser simplificada. Como você está lendo seus dados?
Thomas
2
Parece que thevector.resize(previous_size + incoming_size), seguido por memcpy(ou semelhante) em &thevector[previous_size], é o que você precisa. Os dados do vetor são armazenados "como uma matriz".
Thomas
38

O redimensionamento em C ++ é estranho devido à necessidade potencial de chamar construtores e destruidores.

Não acho que haja uma razão fundamental pela qual em C ++ você não poderia ter um resize[]operador para acompanhar new[]e delete[], isso fez algo semelhante a isto:

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

Obviamente, oldsizeseria recuperado de um local secreto, mesmo que estivesse delete[], e Typeviria do tipo do operando. resize[]falharia onde o tipo não é copiável - o que é correto, uma vez que tais objetos simplesmente não podem ser realocados. Finalmente, o código acima constrói os objetos por padrão antes de atribuí-los, o que você não gostaria que fosse o comportamento real.

Há uma possível otimização em newsize <= oldsizeque chamar destruidores para os objetos "além do final" do array recém-formado e não fazer mais nada. O padrão teria que definir se essa otimização é necessária (como quando você resize()usa um vetor), permitida mas não especificada, permitida mas dependente da implementação ou proibida.

A pergunta que você deve fazer a si mesmo é: "é realmente útil fornecer isso, visto que vectortambém o faz, e é projetado especificamente para fornecer um contêiner redimensionável (de memória contígua - esse requisito omitido em C ++ 98, mas corrigido em C ++ 03) é um ajuste melhor do que arrays com as formas de C ++ de fazer as coisas? "

Acho que a resposta é amplamente considerada "não". Se você quiser fazer buffers redimensionáveis ​​à maneira C, use malloc / free / realloc, que estão disponíveis em C ++. Se você quiser fazer buffers redimensionáveis ​​da maneira C ++, use um vetor (ou deque, se você realmente não precisa de armazenamento contíguo). Não tente misturar os dois usando new[]buffers brutos, a menos que esteja implementando um contêiner semelhante a um vetor.

Steve Jessop
fonte
0

Aqui está um exemplo std :: move implementando um vetor simples com um realloc (* 2 cada vez que atingimos o limite). Se houver uma maneira de fazer melhor do que a cópia que tenho abaixo, por favor, me avise.

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
fonte
-7

tente algo assim:

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

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

    // Board DataBoard[255];
    int SelectedBoard;

} RUNDATA;

Vector vai reclamar. É por isso que arrays, malloc e new ainda existem.

algum convidado
fonte
12
Não, não é por isso. E não vejo como isso responde à pergunta de forma alguma. Ou por que você está usando em typedeftodos os lugares como se estivesse escrevendo C.
Lightness Races in Orbit