Как выделить память для std :: vector, а затем вызвать конструктор для некоторых элементов? - PullRequest
2 голосов
/ 17 июня 2020

Я «модернизирую» (довольно старый) проект C ++ и спотыкаюсь об этой части:

Старый код выделяет память для массива c Dynami, а затем при необходимости вызываю конструктор для элемента . Я предполагаю, что вызов конструктора для всех элементов обходится дорого, поэтому автор выбрал именно этот способ (производительность критична для этого проекта). Старый код выглядит так (упрощенно):

struct my_struct {
    my_struct(int x, int y, int z) { /* expensive ctor */ }
};

struct other_class {
    my_struct* arr;
    other_class(int n) {        
        arr = (my_struct*) malloc(n * sizeof(arr[0]);
    }

    void foo(int idx, int a, int b, int c) {
        new (&arr[idx]) my_struct(a, b, c);
    }
};

Я изменил arr на std::vector<my_struct> и использовал std::reserve, чтобы «зарезервировать» память. Код работает нормально, прошли все текущие тесты, но я знаю, что это не нормально, поскольку std::reserve не увеличивает размер этого вектора, поэтому вызов arr.size() все равно вернет 0. Вот мой код:

struct other_class {
    std::vector<my_struct> arr;
    other_class(int n) {        
        arr.reserve(n);
    }

    void foo(int idx, int a, int b, int c) {
        new (&arr[idx]) my_struct(a, b, c);
    }
};

Как сделать этот код быстрым и безопасным (при условии, что я не могу добавить ctor по умолчанию в my_struct)? Спасибо.


Изменить: вот пример кода. Он компилируется и запускается, как ожидалось, без каких-либо предупреждений: http://cpp.sh/8ytwf

Ответы [ 2 ]

6 голосов
/ 17 июня 2020

, но я знаю, что это не нормально, поскольку std :: reserve не увеличивает размер этого вектора

std::reserve действительно увеличивает емкость векторов, и когда вы хотите только выделить память и только последующие элементы pu sh, тогда это именно то, что вам нужно.

Размер базового массива, которым управляет вектор, не равен векторам size(). size() возвращает количество элементов в контейнере, и пока их нет size() == 0.

При последующем размещении элементов pu sh вам не нужно использовать новое размещение, но вы должны использовать push_back или emplace_back. В частности, это неверно:

 new (&arr[idx]) my_struct(a, b, c);

Поскольку вы обращаетесь к вектору за пределами границ (помните: размер - это количество элементов, емкость может быть больше, но вы не можете получить доступ к индексам, превышающим размер вектора ). Вместо:

 arr.emplace_back(a,b,c);
2 голосов
/ 17 июня 2020

Хотя мне больше нравится решение emplace_back, в этом случае вы можете использовать std::optional. Это позволит вам использовать idx, чтобы определить, какой элемент заполняется допустимым объектом.

struct other_class 
{
    std::vector<std::optional<my_struct>> arr;
    other_class( int n ) : arr( n, std::nullopt )
    { }

    void foo( int idx, int a, int b, int c ) 
    {
        arr.at( idx ).emplace( a, b, c );
    }
};
...