Задать вопрос
@Acaunt

Как переделать код согласно современным стандартам?

Мне необходимо создать динамический массив из классов у которых имеется только конструктор с параметрами.

Упрощённый пример класса
class MyClass {
private:
    const int a;
public:
    MyClass() = delete;
    MyClass(MyClass&&) = delete;
    MyClass(const MyClass&) = delete;
    MyClass(int a) : a(a) {};
    ~MyClass() = default;
    MyClass& operator=(MyClass&&) = delete;
    MyClass& operator=(const MyClass&) = delete;
    operator int() const { return a; }
};


Я нашел пример как это сделать:

Пример
#include <iostream>

int main() {
    // Инициализация динамический массив
    MyClass* arr = (MyClass*) operator new (sizeof(MyClass) * 10);
    
    for (int i = 0; i < 10; ++i) {
        new (&arr[i]) MyClass(i);
    }
    
    // Что-то сделали с массивом
    for (int i = 0; i < 10; ++i) {
        std::cout << arr[i] << std::endl;
    }
    
    // Очистили память
    for (int i = 0; i < 10; ++i) {
        arr[i].~MyClass();
    }
    
    operator delete(arr);
    
    return 0;
}


Всё работает, но современные стандарты рекомендуют не использовать в коде new и delete. Я пробовал использовать std::unique_ptr и std::make_unique, но в них нельзя передать параметры для инициализации класса.

Впринципе я знаю, что могу вместо этого использовать std::list::emplace_back, но тогда это лишает меня быстрого доступа к любым элементам массива через operator[].

Что можно сделать? Или просто не обращать внимания на рекомендацию и делать как в примере?

(Ещё я не уверен в правильности очистки памяти)
  • Вопрос задан
  • 189 просмотров
Подписаться 1 Простой 6 комментариев
Решения вопроса 1
@Mercury13
Программист на «си с крестами» и не только
Вы с неинициализированной памятью работаете почти правильно, но…

Первое. Надо указать выравнивание класса.
MyClass* arr = (MyClass*) operator new (sizeof(MyClass) * 10,
                                        std::align_val_t(alignof(MyClass)));
. . .
operator delete(arr, std::align_val_t(alignof(MyClass)));


Второе. А нельзя инкапсулировать подобную работу с памятью в класс, чтобы конструктор выделял память и деструктор отдавал? Я сделал вот такое.
#include <iostream>

class MyClass {
private:
    const int a;
public:
    MyClass() = delete;
    MyClass(MyClass&&) = delete;
    MyClass(const MyClass&) = delete;
    MyClass(int a) : a(a) {};
    ~MyClass() = default;
    MyClass& operator=(MyClass&&) = delete;
    MyClass& operator=(const MyClass&) = delete;
    operator int() const { return a; }
};

template <class T>
class DynArray {
public:
    static constexpr std::align_val_t ALIGNMENT { alignof(T) };
    DynArray (size_t n)
        : maxSize(n),
          d((T*) operator new (sizeof(T) * n, ALIGNMENT )) {}
    ~DynArray() {
        std::destroy_n(d, currSize);
        operator delete(d, ALIGNMENT);
    }
    template <class... Args>
        T& add(Args&&... args);
    T& operator [] (size_t i) { return d[i]; }
    const T& operator [] (size_t i) const { return d[i]; }
private:
    size_t currSize = 0, maxSize = 0;
    T* d;
};

template <class T> template <class... Args>
T& DynArray<T>::add(Args&&... args)
{
    if (currSize >= maxSize)
        throw std::logic_error("[DynArray::add] Overflow");
    auto& where = d[currSize++];
    new (&where) MyClass(std::forward<Args>(args)...);
    return where;
}

int main() {
    DynArray<MyClass> da(10);

    for (int i = 0; i < 10; ++i) {
        da.add(i);
    }

    // Что-то сделали с массивом
    for (int i = 0; i < 10; ++i) {
        std::cout << da[i] << '\n';
    }

    return 0;
}


Ну и третье — в коде есть, но забыл указать — разбирайте API неинициализированной памяти Си++17.

ЧЕТВЁРТОЕ. Есть один класс, который не перемещает. Поскольку начиная с Си++17 весь STL внутри работает с неинициализированной памятью, получается довольно мило — но хранится ли оно непрерывно, мы никак не узнаем.
std::deque<MyClass> dq;
    for (int i = 0; i < 10; ++i) {
        dq.emplace_back(i + 42);
    }
    // Что-то сделали с массивом
    for (int i = 0; i < 10; ++i) {
        std::cout << dq[i] << '\n';
    }
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Вам точно надо, чтобы значение класса нельзя было перемещать? Если вы только разрешите конструктор и оператор перемещения, то можно будет засунуть ваши элементы в обычный std::vector.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы