Правильно выделить массив с размещением нового - PullRequest
0 голосов
/ 05 сентября 2018

Я строю распределитель памяти и использую размещение новых. Скажем, я хочу «поместить» 10 элементов в уже выделенный массив в куче.

Сначала обычный new выделяет необходимое количество байтов в куче, затем я создаю свои WE объекты в соответствующих местах.

struct WE {
    WE() {
        std::cout << "WE default constructed\n";
    }
    ~WE() {
        std::cout << "WE default destructed\n";
    }

    double d;
    int i;
    char c;
};

Правильно ли следующее использование размещения?

Код компилируется и вывод кажется правильным, но у меня есть некоторые сомнения.

// 1. allocate
const int elements = 10;
int nbytes = elements * sizeof(WE);
char* memory = new char[nbytes];
WE* pB = (WE*)memory;
int step = sizeof(WE);
// 2. construct
for (int i = 0; i < nbytes; i += step)
    new (pB + i) WE();
// 3. process
for (int i = 0; i < nbytes; i += step)
    pB[i].i = i * 2;
for (int i = 0; i < nbytes; i += step)
    std::cout << '[' << i << ']' << '=' << pB[i].i << '\n';
// 4. destruct
for (int i = 0; i < nbytes; i += step)
    pB[i].~WE();
// 5. deallocate
delete[] memory;
pB = nullptr;
memory = nullptr;

Если все хорошо с вопросом выше, то позвольте мне задать дополнительный вопрос, как бы я выровнял этот массив по произвольной границе байта? Скажем, я хочу выравнивание на sizeof(WE), что 16 (а не на alignof(WE), что 8). Будет ли этой модификации: alignas(sizeof(WE)) char* memory = new char[nbytes]; достаточно, чтобы сделать трюк? Я также слышал о std::aligned_storage. Я не уверен, может ли это дать какие-либо преимущества. (Если второй вопрос вас смутил или я что-то напутал в первой части, забудьте об этом.) Заранее спасибо.

1 Ответ

0 голосов
/ 05 сентября 2018

Для построения объектов (размещение новых) вы можете выполнять итерации по байтам / по символам:

for (int i = 0; i < nbytes; i += step) new (memory + i) WE();

или поэлементно:

for (int i = 0; i < elements; i++) new (pB + i) WE();

В остальных циклах, где вы получаете доступ к элементам, вам нужно использовать второй параметр.

Что касается выравнивания, динамическое выделение памяти возвращает блок памяти, выровненный на alignof(std::max_align_t) (C ++ 11). Примерное значение - 16 (GCC / x86_64), и это то, что вам нужно, но это значение, конечно, не гарантируется стандартом.

Если я не ошибаюсь, до C ++ 17 operator new не может напрямую выделить память для выровненных объектов, и std::aligned_storage здесь не помогает. Начиная с C ++ 17, существуют специальные версии operator new, которые принимают информацию о выравнивании, см .: https://en.cppreference.com/w/cpp/memory/new/operator_new.

...