如果对 unique_ptr 包裹的指针执行 delete 是处理它的正确做法,则默认删除器是可以的。如果您的指针来自new,那没问题,但在许多其他情况下这是不正确的。
一个简单的例子是FILE *:FILE *是一个不能用delete删除的指针,但是你必须在它上面使用fclose。使用自定义删除器很容易将其包装在 unique_ptr 中并让它负责销毁/移动/...:
namespace detail {
struct file_ptr_deleter {
void operator() (FILE *fp) {
if(fp) fclose(fp);
}
};
}
/// RAII-style wrapper for a FILE*
struct unique_c_file : std::unique_ptr<FILE, detail::file_ptr_deleter> {
using std::unique_ptr<FILE, detail::file_ptr_deleter>::unique_ptr;
operator FILE *() { return get(); }
};
(在这种情况下,我什至继承自 std::unique_ptr 以简化直接在 C API 中使用 unique_c_file)。
其他可能更常见的情况是,如果您有来自库的内存,它们会提供自己的删除功能;例如,您可以使用 BSD 套接字,其中 getaddrinfo 为您提供了一个必须使用 freeaddrinfo 释放的 addrinfo *;即使在这种情况下,也很容易为此创建一个智能指针:
namespace detail {
struct addrinfo_deleter {
void operator()(addrinfo *elem) {
if(elem) freeaddrinfo(elem);
}
};
}
/// unique_ptr to handle an %addrinfo linked list provided by %getaddrinfo
typedef std::unique_ptr<addrinfo, detail::addrinfo_deleter> addrinfo_ptr;
类似地,在 Win32 编程中,许多 API 为您提供已使用 LocalAlloc 分配的内存,因此必须使用 LocalFree 释放内存:
// Custom deleter (for unique_ptr) using Win32 LocalFree
struct LocalFree_deleter { void operator()(void *ptr) { LocalFree(ptr); } };
// ...
std::unique_ptr<LPWSTR[], LocalFree_deleter> argvw(CommandLineToArgvW(cmdline_u16, &argc));
您甚至可以更进一步,处理甚至不是指针的东西,而是不透明的“句柄”:同样在 Win32 中,这很常见,甚至不同类型的句柄需要不同的关闭函数;即使在这种情况下,unique_ptr 和自定义删除器也可以拯救:
template<decltype(CloseHandle) ch_fn = CloseHandle>
struct handle_deleter {
typedef HANDLE pointer;
void operator()(HANDLE obj) {
if(obj != NULL && obj != INVALID_HANDLE_VALUE) ch_fn(obj);
}
};
using unique_handle = std::unique_ptr<HANDLE, handle_deleter<>>;
using unique_find_handle = std::unique_ptr<HANDLE, handle_deleter<FindClose>>;
注意这里handle_deleter提供了一个typedef HANDLE pointer:这个typedef被unique_ptr用作它存储在其内部的类型,在get() & co 中返回。