可以将C++智能指针与C的malloc一起使用吗?

32

我的一些代码仍在使用malloc而不是new。原因是我害怕使用new,因为它会抛出异常,而不是返回NULL,我可以轻松地检查它。每次调用new都要包装在try{}catch(){}中也看起来不太好。而使用malloc时,我只需做if (!new_mem) { /* 处理错误 */ }

因此,我有一个问题。我能否将智能指针与malloc一起使用?

像这样:

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

有没有类似这样的东西。

这个可行吗?

谢谢,Boda Cydo。


2
如果你想要在出现异常时返回NULL,可以使用以下代码:Type* bla = new (std::nothrow) Type();。我认为使用std::nothrow比malloc更好,因为后者不会调用构造函数。 - Vitor Py
1
在错误处理代码中,你打算做什么来弥补内存不足的情况?通常的做法是,如果分配成功,则继续执行工作。如果分配失败,则以错误代码退出(这是一种将控制权传递回堆栈顶部的复杂方式)。 - Martin York
10个回答

52
如果您正在使用 shared_ptrunique_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)));

然而,在编写C++代码时,尤其是在处理内存分配失败的情况下,正确使用异常机制比避免使用更加优秀。在大多数情况下,无法在尝试执行内存分配的函数中成功地恢复,因此异常机制可以帮助您在能够处理错误的地方处理它。


6
你也可以直接传递free: auto myPointer = std::unique_ptr<int, void (*)(void*)>(..., free) - chpatrick
4
请注意,如果这样做,您的 unique_ptr 将会变成两倍大。 - James McNellis

13

2
使用nothrow是一种常见的做法吗?(因为我个人在实际实践中并没有看到过使用nothrow的情况。) - bodacydo
11
当你不想抛出异常时,使用new的最常见方法是。但实际上,更常见的做法是仍然使用new并处理异常。如果分配失败,通常也无法为此做太多事情,所以让异常传播是一种比较优雅的正常退出方式。 - jalf

4

最好的解决方案是使用new (std::nothrow) Type。这将像new Type一样运行,但如果失败,则会返回null而不是抛出异常。这比尝试使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);
}

但是使用不抛出异常的 new 操作符会更简单:
if (Type* object = new (std::nothrow) Type)
{        
    std::shared_ptr<Type> ptr(object);
    DoStuff(ptr);
}

1
使用 malloc 的额外优势是允许您使用 realloc: https://dev59.com/NlsX5IYBdhLWcg3whv0n#33706568 - Kyle Strand

2

/* 处理错误 */中写入什么代码?在遇到内存不足的错误时,你能做些什么呢?我只是让应用程序终止并生成了一个调用堆栈(核心转储),这样至少我知道可能会出现问题的地方。

对于C++类和对象,使用malloc分配内存并不是一个好主意,因为它不会确保构造函数被调用,可能导致未初始化的类,甚至会崩溃,如果有虚方法更是如此。

只需使用newdelete,不必担心捕获异常。毕竟,内存耗尽是一个异常情况,不应该在应用程序正常运行时发生。


1
有一些情况下,你是可以处理错误的。例如,内存消耗量较大的数值计算(如模拟)在弱机器上很容易出现内存不足的情况,但此时你可以立即停止并显示错误信息,而不是崩溃。 - Yury

1

一行代码实现:

unique_ptr<char, void (*)(void*)> buffer( (char*)malloc(size), free );

1

这取决于SmartPointer在销毁时的操作。如果您可以指定free作为解除分配器,那么可能会起作用。例如,boost::shared_ptr允许您指定删除器。

我没有足够关注您想要这样做的原因。我同意其他答案,使用nothrow new是一个更好的选择。


1

1

1

使用nothrow

Nothrow常量

此常量值用作operator new和operator new[]的参数,以指示这些函数在失败时不会抛出异常,而是返回空指针。

char* p = new (nothrow) char [1048576];
if (p==NULL) cout << "Failed!\n";
else {
    cout << "Success!\n";
    delete[] p;
}

1

我有一个问题。

如果 "Type" 是一个其构造函数可以抛出异常的类型,那么该怎么办?在这种情况下,仍然需要在 try/catch 块中处理异常。

那么放弃基于异常的方法是个好主意吗?

我认为可以使用抽象工厂/工厂方法设计模式,并将所有的 'new' 放置在相对较少的文件/命名空间/类中,而不是散布在各处。这也有助于将 try/catch 块的使用限制在相对较少的代码中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接