Як ви «переробите» на C ++?


85

Як я можу reallocв C ++? Здається, це відсутнє в мові - є newі deleteале ні resize!

Мені це потрібно, оскільки, оскільки моя програма читає більше даних, мені потрібно перерозподілити буфер, щоб утримувати їх. Я не вважаю правильним варіантом deleteвведення старого вказівника та newстворення нового, більшого.


8
Stroustrup відповів на це давно, див .: www2.research.att.com/~bs/bs_faq2.html#renew (Це хороший початок, якщо ви не знайомі з C ++ разом із Cline FAQ по C ++.)
dirkgently

8
Відповідь, на яку посилається @dirkgently, тепер знаходиться за адресою: stroustrup.com/bs_faq2.html#renew - а запитання щодо Cline тепер є частиною суперзапитань
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])

Це абсолютно правильний код С ++, оскільки вектор - це розумний С-масив.


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: Що ти маєш на увазі? Нехай це випадає із сфери дії, і його вже немає.
Гонки легкості на орбіті

51

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

newі deleteне можуть змінити розмір, оскільки вони виділяють достатньо пам’яті, щоб вмістити об’єкт даного типу. Розмір даного типу ніколи не зміниться. Є new[]іdelete[] але навряд чи коли-небудь є причина використовувати їх.

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


6
@bodacydo: Не застосовуйте зростаючий буфер, просто використовуйте std::vector- він автоматично зростатиме, коли це потрібно, і ви можете попередньо виділити пам'ять, якщо хочете ( reserve()).
гострий зуб

4
Використовуйте std :: vector <T>. Ось для чого це. У C ++ немає жодної причини використовувати самостійно new / delete / new [] / delete [], якщо ви явно не пишете класи управління ресурсами.
Щеня

4
@bod: Так, може. (Так може std::string, до речі.)
fredoverflow

1
Так, може, не біда. Навіть std::stringможе це зробити. До речі, є ймовірність того, що читання ваших даних також можна спростити. Як ви читаєте свої дані?
Томас

2
Здається thevector.resize(previous_size + incoming_size), що вам потрібно , за яким слідує memcpy(або подібне) &thevector[previous_size]. Дані вектора гарантовано зберігаються "як масив".
Томас

36

Змінювати розмір у 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.
Гонки легкості на орбіті
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.