новое в стеке, а не в куче (например, alloca vs malloc)
-
19-08-2019 - |
Вопрос
Есть ли способ использовать new
ключевое слово для выделения в стеке (аля alloca
) вместо кучи (malloc
) ?
Я знаю, что могу взломать свою собственную, но не хочу.
Решение
Чтобы разместить в стеке, либо объявите свой объект как локальную переменную по значению , либо вы можете фактически использовать alloca для получения указателя, а затем использовать оператор new на месте:
void *p = alloca(sizeof(Whatever));
new (p) Whatever(constructorArguments);
Однако, используя alloca и in-place, new гарантирует, что память освобождается при возврате, вы прекращаете автоматический вызов деструктора. Если вы просто пытаетесь обеспечить освобождение памяти при выходе из области действия, попробуйте использовать std::auto_ptr<T>
или другой тип интеллектуального указателя.
Другие советы
Джеффри Хантин совершенно прав, что вы можете использовать новое размещение, чтобы создать его в стеке с помощью alloca. Но серьезно, почему ?! Вместо этого просто сделайте:
class C { /* ... */ };
void func() {
C var;
C *ptr = &var;
// do whatever with ptr
}
Теперь у вас есть указатель на объект, размещенный в стеке. И он будет должным образом уничтожен, когда ваша функция существует.
Вы можете сделать:
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.
С уважением Дэйвф
Будьте осторожны при использовании _alloca()
с GCC
В GCC есть ошибка, которая делает <=> несовместимой с обработкой исключений SJLJ в C ++ (сообщается, что 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();
}