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.


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.


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.


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.


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

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


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.