C ++ New против Malloc для динамической памяти массива объектов - PullRequest
3 голосов
/ 14 марта 2012

У меня есть класс Bullet, который принимает несколько аргументов для его построения. Тем не менее, я использую динамический массив памяти для их хранения. Я использую C ++, поэтому я хочу соответствовать его стандарту, используя оператор new для выделения памяти. Проблема в том, что новый оператор запрашивает аргументы конструктора, когда я выделяю массив, которого у меня нет в то время. Я могу сделать это, используя malloc, чтобы получить правильный размер, а затем заполнить там форму, но это не то, что я хочу использовать :) любые идеи?

pBulletArray = (Bullet*) malloc(iBulletArraySize * sizeof(Bullet)); // Works
pBulletArray = new Bullet[iBulletArraySize]; // Requires constructor arguments

Спасибо.

Ответы [ 6 ]

5 голосов
/ 14 марта 2012

1) std::vector

A std::vector действительно является правильным C ++ способом сделать это.

std::vector<Bullet> bullets;
bullets.reserve(10); // allocate memory for bullets without constructing any

bullets.push_back(Bullet(10.2,"Bang")); // put a Bullet in the vector.
bullets.emplace_back(10.2,"Bang"); // (C++11 only) construct a Bullet in the vector without copying. 

2) new [] оператор

Также возможно сделать это с new, но вам действительно не следует. Управление ресурсами с помощью new / delete вручную - сложная задача, похожая на шаблонное метапрограммирование, поскольку ее лучше оставить разработчикам библиотек, которые будут использовать эти функции для создания эффективных библиотек высокого уровня для вас. Фактически, чтобы сделать это правильно, вы, в основном, будете реализовывать внутренние компоненты std::vector.

Когда вы используете оператор new для выделения массива, каждый элемент в массиве инициализируется по умолчанию. Ваш код может работать, если вы добавите конструктор по умолчанию в Bullet:

class Bullet {
public:
    Bullet() {} // default constructor
    Bullet(double,std::string const &) {}
};

std::unique_ptr<Bullet[]> b = new Bullet[10]; // default construct 10 bullets

Затем, когда у вас есть реальные данные для Bullet, вы можете назначить их одному из элементов массива:

b[3] = Bullet(20.3,"Bang");

Обратите внимание на использование unique_ptr для обеспечения надлежащей очистки и исключительной безопасности. Делать эти вещи вручную сложно и подвержено ошибкам.


3) operator new

Оператор new инициализирует свои объекты в дополнение к выделению для них места. Если вы хотите просто выделить место, вы можете использовать operator new.

std::unique_ptr<Bullet,void(*)(Bullet*)> bullets(
    static_cast<Bullet*>(::operator new(10 * sizeof(Bullet))),
    [](Bullet *b){::operator delete(b);});

(Обратите внимание, что unique_ptr гарантирует, что хранилище будет освобождено, но не более. В частности, если мы создаем какие-либо объекты в этом хранилище, мы должны вручную уничтожить их и сделать это безопасным способом.)

bullets теперь указывает на объем памяти, достаточный для массива Bullet с. Вы можете построить массив в этом хранилище:

new (bullets.get()) Bullet[10];

Однако конструкция массива снова использует инициализацию по умолчанию для каждого элемента, чего мы пытаемся избежать.

AFAIK C ++ не определяет какой-либо четко определенный метод построения массива без конструирования элементов. Я полагаю, что это во многом потому, что это было бы бесполезным для большинства (всех?) Реализаций C ++. Поэтому, хотя следующее технически не определено, на практике оно довольно четко определено.

bool constructed[10] = {}; // a place to mark which elements are constructed

// construct some elements of the array
for(int i=0;i<10;i+=2) {
    try {
        // pretend bullets points to the first element of a valid array. Otherwise 'bullets.get()+i' is undefined
        new (bullets.get()+i) Bullet(10.2,"Bang");
        constructed = true;
    } catch(...) {}
}

Это будет создавать элементы массива без использования конструктора по умолчанию. Вам не нужно создавать каждый элемент, только те, которые вы хотите использовать. Однако при уничтожении элементов вы должны помнить, чтобы уничтожать только те элементы, которые были построены.

// destruct the elements of the array that we constructed before
for(int i=0;i<10;++i) {
    if(constructed[i]) {
        bullets[i].~Bullet();
    }
}

// unique_ptr destructor will take care of deallocating the storage 

Выше приведен довольно простой случай. Сделать нетривиальное использование исключения этого метода безопасным, не заключая его в класс, сложнее. Заключение этого в класс в основном равносильно реализации std::vector.


4) std::vector

Так что просто используйте std::vector.

5 голосов
/ 14 марта 2012

Вы не можете.

И если вы действительно хотите соответствовать стандартам C ++, вы должны использовать std::vector.

К вашему сведению, это, вероятно, будет даже дороже, чем вы пытаетесь достичь. Если бы вы могли сделать это, new вызвал бы конструктор. Но так как вы все равно модифицируете объект позже, начальная конструкция бесполезна.

1 голос
/ 14 марта 2012

Это возможно делать то, что вы хотите - ищите «оператор новый», если вы действительно хотите знать, как. Но это почти наверняка плохая идея. Вместо этого используйте std :: vector, который позаботится обо всех неприятных для вас деталях. Вы можете использовать std :: vector :: reserve для выделения всей памяти, которую будете использовать заранее.

0 голосов
/ 15 марта 2012

Если то, что вам действительно нужно, это просто быстрое выделение / освобождение, то вам следует обратиться к «пулам памяти». Я бы порекомендовал использовать boost , а не пытаться накатить свою собственную. В частности, вы, вероятно, захотите использовать « object_pool ».

0 голосов
/ 14 марта 2012

Обычно C ++ new работает, выделяя память для экземпляра класса, а затем вызывает конструктор для этого экземпляра. Вы уже распределили память для своих экземпляров.

Вы можете вызвать только конструктор для первого экземпляра следующим образом:

new((void*)pBulletArray) Bullet(int foo);

Вызов конструктора второго будет выглядеть так (и так далее)

new((void*)pBulletArray+1) Bullet(int bar);

если конструктор Bullet принимает int.

0 голосов
/ 14 марта 2012
Bullet** pBulletArray = new Bullet*[iBulletArraySize];

Затем заполните pBulletArray:

for(int i = 0; i < iBulletArraySize; i++)
{
   pBulletArray[i] = new Bullet(arg0, arg1);
}

Только не забудьте впоследствии освободить память, используя delete.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...