Можно ли использовать интеллектуальные указатели С++ вместе с C malloc?
В моем коде по-прежнему используется malloc
вместо new
. Причина в том, что я боюсь использовать new
, потому что он генерирует исключение, а не возвращает NULL
, с которым я могу легко проверить. Обертка каждого вызова new
в try{}catch(){}
также не выглядит так хорошо. Если при использовании malloc
я могу просто сделать if (!new_mem) { /* handle error */ }
.
Поэтому у меня есть вопрос. Могу ли я использовать интеллектуальные указатели вместе с malloc
?
Что-то вроде:
SmartPointer<Type> smarty = malloc(sizeof(Type));
Что-то вроде этого.
Возможно ли это?
Спасибо, Бода Сидо.
Ответы
Ответ 1
Если вы используете shared_ptr
или unique_ptr
, вы можете указать пользовательский делектор. Например,
struct free_delete
{
void operator()(void* x) { free(x); }
};
Это можно использовать с shared_ptr
следующим образом:
std::shared_ptr<int> sp((int*)malloc(sizeof(int)), free_delete());
Если вы используете unique_ptr
, делетер является частью типа unique_ptr
, поэтому делетеру необходимо указать в качестве аргумента шаблона:
std::unique_ptr<int, free_delete> up((int*)malloc(sizeof(int)));
Однако лучше использовать исключения правильно, а не избегать их, при написании С++, особенно в отношении сбоев распределения. В большинстве случаев вы не можете успешно восстановить из отказа распределения в функции, пытающейся выполнить выделение, поэтому исключения могут помочь вам справиться с ошибкой, где вы действительно можете ее обрабатывать.
Ответ 2
Вы можете использовать ключевое слово nothrow с новым оператором, который вернет NULL, а не генерирует исключение. Подробнее см. Ссылку ниже:
http://www.cplusplus.com/reference/std/new/nothrow/
Ответ 3
Лучшим решением является использование new (std::nothrow) Type
. Это будет действовать точно так же, как new Type
, но даст нулевое значение, а не бросает, если он терпит неудачу. Это будет намного проще, чем пытаться заставить malloc
вести себя как new
.
Если вы действительно должны использовать malloc
, тогда не забудьте правильно построить и уничтожить объект:
void* memory = malloc(sizeof(Type));
Type* object = new (memory) Type;
object->~Type();
free(object); // or free(memory)
Вы можете использовать это с помощью некоторых умных указателей, указав его пользовательский деактивировать:
void malloc_deleter(Type* object)
{
object->~Type();
free(object);
}
if (void* memory = malloc(sizeof(Type)))
{
Type* object = new (memory) Type;
std::shared_ptr<Type> ptr(object, malloc_deleter);
DoStuff(ptr);
}
Но это было бы намного проще, если бы не метать новое:
if (Type* object = new (std::nothrow) Type)
{
std::shared_ptr<Type> ptr(object);
DoStuff(ptr);
}
Ответ 4
Это зависит от того, что SmartPointer делает при уничтожении. Если вы можете указать free
как дезактиватор, это может сработать. Например, boost:: shared_ptr позволяет указать дебетер.
Я не обращал достаточного внимания на вашу причину этого. Я согласен с другими ответами, что использование метода nothrow new
является гораздо лучшей идеей.
Ответ 5
Можно использовать malloc со смарт-указателями (вам нужно указывать возвращаемое значение на целевой тип указателя, хотя и предоставлять пользовательский деаллокатор). Но лучшим вариантом является использование nothrow
версии оператора new
.
http://www.cplusplus.com/reference/std/new/nothrow/
Ответ 6
Какой код идет в /* handle error */
? Есть ли что-нибудь, что вы действительно можете сделать с ошибкой вне памяти? Я просто разрешаю приложению заканчивать с стеком вызовов (core dump), поэтому у меня есть идея, по крайней мере, одного возможного места, которое может вызвать проблемы.
Использование malloc
для выделения памяти для классов и объектов С++ не является хорошей идеей, потому что не будет гарантировать, что конструкторы будут вызваны, возможно, оставив вас с неинициализированными классами, которые могут даже сбой, если у них есть виртуальные методы.
Просто используйте new
и delete
, и не беспокойтесь о том, чтобы поймать исключение, после того, как закончится нехватка памяти. Это исключительный случай и не должен происходить в обычных прогонах приложения.
Ответ 7
Возможно, вы захотите попробовать "размещение нового". См. Что используется для "размещения нового" ?
Ответ 8
Используйте nothrow.
Постоянная Nothrow
Это постоянное значение используется как аргумент для оператора new и оператора new [], чтобы указать, что эти функции не должны исключать сбой, но возвращает нулевой указатель вместо этого.
char* p = new (nothrow) char [1048576];
if (p==NULL) cout << "Failed!\n";
else {
cout << "Success!\n";
delete[] p;
}
Ответ 9
У меня есть вопрос.
Что произойдет, если "Тип" - это тип, конструктор которого может выбрасывать? В этом случае все еще нужно обрабатывать исключения в блоке try/catch.
Итак, стоит ли отказаться от подхода, основанного на исключении?
Я бы сказал, что можно использовать шаблон Abstract Factory/Factory Method и иметь все "новые в относительно меньшем наборе файлов/пространств имен/классов, а не их разбросать по всему месту. Это также может помочь ограничить использование блока try/catch относительно менее коротким кодом.