Подтвердить что ты не робот

Можно ли использовать интеллектуальные указатели С++ вместе с C malloc?

В моем коде по-прежнему используется malloc вместо new. Причина в том, что я боюсь использовать new, потому что он генерирует исключение, а не возвращает NULL, с которым я могу легко проверить. Обертка каждого вызова new в try{}catch(){} также не выглядит так хорошо. Если при использовании malloc я могу просто сделать if (!new_mem) { /* handle error */ }.

Поэтому у меня есть вопрос. Могу ли я использовать интеллектуальные указатели вместе с malloc?

Что-то вроде:

SmartPointer<Type> smarty = malloc(sizeof(Type));

Что-то вроде этого.

Возможно ли это?

Спасибо, Бода Сидо.

4b9b3361

Ответ 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, и не беспокойтесь о том, чтобы поймать исключение, после того, как закончится нехватка памяти. Это исключительный случай и не должен происходить в обычных прогонах приложения.

Ответ 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 относительно менее коротким кодом.