Как вы «перераспределяете» в C ++?

87

Как я могу reallocна C ++? В языке вроде бы отсутствует - есть newи deleteно нетresize !

Мне это нужно, потому что по мере того, как моя программа читает больше данных, мне нужно перераспределить буфер для его хранения. Я не думаю, что deleteиспользовать старый указатель и newиспользовать новый, более крупный - правильный вариант.

Bodacydo
источник
8
Страуструп ответил на этот вопрос давно, см .: www2.research.att.com/~bs/bs_faq2.html#renew (Это хорошее начало, если вы новичок в C ++ вместе с FAQ Cline по C ++.)
dirkgently
9
Ответ, на который ссылается @dirkgently, теперь находится по адресу: stroustrup.com/bs_faq2.html#renew, а FAQ Клайна теперь является частью супер-часто задаваемых вопросов: isocpp.org/faq
maxschlepzig

Ответы:

54

Используйте :: std :: vector!

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

становится

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

потом

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

становится

t.resize(n2);

Если вы хотите передать указатель в функцию вместо

Foo(t)

использовать

Foo(&t[0])

Это абсолютно правильный код на C ++, потому что vector - это умный C-массив.

f0b0s
источник
1
Разве строка memset не должна быть memset (t, 0, sizeof (T) * n) ;? n вместо m?
Рафаэль Майер
1
@anthom да. так и должно бытьType* t = static_cast<Type*>(malloc(n * sizeof *t));
Райан Хейнинг
2
В C ++ 11 теперь можно было бы использовать t.data()вместо&t[0]
knedlsepp
1
Как потом это удалить?
a3mlord
@ a3mlord: Что ты имеешь в виду? Пусть это выпадет из поля зрения, и оно исчезнет.
Гонки за легкостью на орбите,
50

Правильный вариант, вероятно, использовать контейнер, который делает всю работу за вас, например std::vector.

newи deleteне могут изменять размер, потому что они выделяют достаточно памяти для хранения объекта данного типа. Размер данного типа никогда не изменится. Есть new[]и delete[]но вряд ли когда - нибудь повод , чтобы использовать их.

То, что reallocделает в C, вероятно, будет просто malloc, memcpyи free, во всяком случае, хотя менеджерам памяти разрешено делать что-то умное, если имеется достаточно непрерывной свободной памяти.

Томас
источник
6
@bodacydo: не реализуйте растущий буфер, просто используйте std::vector- он будет увеличиваться автоматически, когда это необходимо, и вы можете предварительно выделить память, если хотите ( reserve()).
Sharptooth
4
Используйте std :: vector <T>. Вот для чего это нужно. В C ++ нет никаких причин использовать new / delete / new [] / delete [] самостоятельно, если только вы явно не пишете классы управления ресурсами.
Puppy
4
@bod: Да, может. ( std::stringКстати, так и может.)
fredoverflow
1
Да, без проблем. Даже std::stringможет это сделать. Кстати, есть шанс, что чтение данных тоже можно упростить. Как вы читаете свои данные?
Томас
2
Похоже thevector.resize(previous_size + incoming_size), что за ним следует memcpy(или что-то подобное) &thevector[previous_size], это то, что вам нужно. Данные вектора гарантированно хранятся «как массив».
Томас
38

Изменение размера в C ++ неудобно из-за потенциальной необходимости вызывать конструкторы и деструкторы.

Я не думаю, что есть фундаментальная причина, по которой в C ++ у вас не могло быть resize[]оператора, new[]и он delete[]делал что-то подобное:

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

Очевидно oldsize, будет получен из секретного места, в котором оно находится delete[], и Typeбудет исходить из типа операнда.resize[]потерпит неудачу, если тип не копируется - что правильно, поскольку такие объекты просто нельзя переместить. Наконец, приведенный выше код создает объекты по умолчанию перед их назначением, что вам не нужно в качестве фактического поведения.

Возможна оптимизация newsize <= oldsize, при которой вызывать деструкторы для объектов «за концом» только что обработанного массива и больше ничего не делать. Стандарт должен определять, требуется ли эта оптимизация (как в случае resize()с вектором), разрешена, но не определена, разрешена, но зависит от реализации, или запрещена.

Тогда вам следует задать себе следующий вопрос: «Действительно ли полезно предоставлять это, учитывая, что он vectorтоже делает это, и разработан специально для предоставления изменяемого размера контейнера (непрерывной памяти - это требование опущено в C ++ 98, но исправлено в C ++ 03), что лучше, чем массивы с методами работы C ++? "

Я думаю, что широко распространено мнение, что ответ - «нет». Если вы хотите сделать буферы с изменяемым размером в C, используйте те malloc / free / realloc, которые доступны в C ++. Если вы хотите изменять размер буферов, как в C ++, используйте вектор (или deque, если вам действительно не нужно непрерывное хранилище). Не пытайтесь смешивать их, используя new[]для необработанных буферов, если только вы не реализуете векторный контейнер.

Стив Джессоп
источник
0

Вот пример std :: move, реализующий простой вектор с realloc (* 2 каждый раз, когда мы достигаем предела). Если есть способ добиться большего, чем копия, представленная ниже, пожалуйста, дайте мне знать.

Скомпилировать как:

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

Код:

#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

попробуйте что-нибудь вроде этого:

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

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

    // Board DataBoard[255];
    int SelectedBoard;

} RUNDATA;

Вектор будет жаловаться. Вот почему все еще существуют массивы, malloc и new.

какой-то гость
источник
12
Нет, не поэтому. И я не понимаю, как это вообще отвечает на вопрос. Или почему вы используете typedefвезде, как если бы вы писали C.
Lightness Races in Orbit