Новый в стеке вместо кучи (например, alloca vs malloc)
Можно ли использовать ключевое слово new
для размещения в стеке (ala alloca
) вместо кучи (malloc
)?
Я знаю, что могу взломать свои собственные, но я бы предпочел не делать этого.
Ответы
Ответ 1
Чтобы выделить в стеке, либо объявите свой объект как локальную переменную по значению, либо вы можете использовать alloca для получения указателя, а затем использовать новый оператор:
void *p = alloca(sizeof(Whatever));
new (p) Whatever(constructorArguments);
Однако при использовании alloca и in-place new гарантирует, что память будет освобождена при возврате, вы откажетесь от автоматического вызова деструктора. Если вы просто пытаетесь обеспечить освобождение памяти после выхода из области действия, рассмотрите возможность использования std::auto_ptr<T>
или другого типа интеллектуального указателя.
Ответ 2
Джеффри Хэнтин вполне прав, что вы можете использовать новое место размещения, чтобы создать его в стеке с помощью alloca. Но, серьезно, почему?! Вместо этого просто выполните:
class C { /* ... */ };
void func() {
C var;
C *ptr = &var;
// do whatever with ptr
}
Теперь у вас есть указатель на объект, выделенный в стеке. И это будет правильно уничтожено, когда ваша функция будет существовать.
Ответ 3
Вы можете сделать:
Whatever* aWhatever = new ( alloca(sizeof(Whatever)) ) Whatever;
Вы можете использовать класс RAII для уничтожения, я полагаю (EDIT: см. этот другой ответ для получения дополнительной информации о потенциальных проблемах с этим подходом):
template <class TYPE>
class RAII
{
public:
explicit RAII( TYPE* p ) : ptr(p) {}
~RAII() { ptr->~TYPE(); }
TYPE& operator*() const { return *ptr; }
private:
TYPE* ptr;
}
void example()
{
RAII<Whatever> ptr = new ( alloca(sizeof(Whatever)) ) Whatever;
}
Вы можете использовать макрос, чтобы скрыть alloca.
Отношения
DaveF
Ответ 4
Будьте осторожны при использовании _alloca()
с GCC
GCC имеет ошибку, которая делает _alloca()
несовместимым с обработкой исключений SJLJ на С++ (сообщается, что Dwarf2 работает правильно). Когда исключение выбрасывается из функции выделения памяти, ошибка вызывает повреждение стека до того, как деструкторы будут запущены. Это означает, что любой класс RAII, работающий над выделенным объектом (объектами), должен работать в другой функции для правильной работы. Правильный способ сделать это выглядит следующим образом:
void AllocateAndDoSomething()
{
Foo* pFoo = reinterpret_cast<Foo*>(_alloca(sizeof(Foo)));
new (pFoo) Foo;
// WARNING: This will not work correctly!
// ScopedDestructor autoDestroy(pFoo);
// pFoo->DoSomething();
// Instead, do like this:
DoSomething(pFoo);
}
void DoSomething(Foo* pFoo)
{
// Here, destruction will take place in a different call frame, where problems
// with _alloca() automatic management do not occur.
ScopedDestructor autoDestroy(pFoo);
pFoo->DoSomething();
}