想想一个返回必须为free
d的东西的C函数,例如POSIX strdup()
.我想在C++ 11中使用该函数并避免任何泄漏,这是正确的方法吗?
#include <memory>
#include <iostream>
#include <string.h>
int main() {
char const* t { "Hi stackoverflow!" };
std::unique_ptr<char, void(*)(void*)>
t_copy { strdup(t), std::free };
std::cout << t_copy.get() << " <- this is the copy!" <<std::endl;
}
Run Code Online (Sandbox Code Playgroud)
假设它有意义,可以使用与非指针相似的模式吗?例如,POSIX的函数open
返回int
?
小智 60
你所拥有的东西极有可能在实践中发挥作用,但并非严格正确.您可以使其更有可能按以下方式工作:
std::unique_ptr<char, decltype(std::free) *>
t_copy { strdup(t), std::free };
Run Code Online (Sandbox Code Playgroud)
原因是std::free
不保证函数类型void(void*)
.它保证在传递a时是可调用的void*
,并且在这种情况下返回void
,但是至少有两个与该规范匹配的函数类型:一个具有C链接,一个具有C++链接.大多数编译器都不关注它,但为了正确,你应该避免对它做出假设.
然而,即使这样,这也不是严格正确的.正如@PeterSom在评论中指出的那样,C++允许实现例如创建std::free
一个重载函数,在这种情况下,你和我的使用std::free
都是模棱两可的.这不是授予的特定权限std::free
,它是几乎任何标准库函数的权限.要避免此问题,需要自定义函数或仿函数(如在他的答案中).
假设它有意义,可以使用与非指针相似的模式吗?
不是unique_ptr
,这是指针真正具体.但是你可以创建自己的类,类似于unique_ptr
,但不会对被包装的对象做出假设.
Pet*_*Som 29
原始问题(和hvd的答案)引入了一个每指针开销,所以这样unique_ptr
的大小是派生的大小的两倍std::make_unique
.另外,我会直接制定decltype:
std::unique_ptr<char, decltype(&std::free)>
t_copy { strdup(t), &std::free };
Run Code Online (Sandbox Code Playgroud)
如果有许多这些C-API派生指针,额外的空间可能会成为阻碍采用安全C++ RAII的C++代码包装现有POSIX样式API的负担free()
.可能出现的另一个问题是,当您char const
在上述情况下使用时,会出现编译错误,因为您无法自动将其转换char const *
为参数类型free(void *)
.
我建议使用专用的删除器类型,而不是动态构建的删除器类型,这样空间开销就会消失,所需const_cast
的也不是问题.然后可以轻松地使用模板别名来包装C-API派生的指针:
struct free_deleter{
template <typename T>
void operator()(T *p) const {
std::free(const_cast<std::remove_const_t<T>*>(p));
}
};
template <typename T>
using unique_C_ptr=std::unique_ptr<T,free_deleter>;
static_assert(sizeof(char *)==
sizeof(unique_C_ptr<char>),""); // ensure no overhead
Run Code Online (Sandbox Code Playgroud)
现在的例子变成了
unique_C_ptr<char const> t_copy { strdup(t) };
Run Code Online (Sandbox Code Playgroud)
我将建议该机制应该在C++核心指南支持库中提供(可能具有更好的命名),因此它可以用于转换到现代C++(成功打开)的代码库.
T.C*_*.C. 10
假设它有意义,可以使用与非指针相似的模式吗?例如,对于POSIX的函数open,返回一个int?
是的,可以做到.你需要的是满足"指针"类型的NullablePointer
需求:
struct handle_wrapper {
handle_wrapper() noexcept : handle(-1) {}
explicit handle_wrapper(int h) noexcept : handle(h) {}
handle_wrapper(std::nullptr_t) noexcept : handle_wrapper() {}
int operator *() const noexcept { return handle; }
explicit operator bool() const noexcept { return *this != nullptr; }
friend bool operator!=(const handle_wrapper& a, const handle_wrapper& b) noexcept {
return a.handle != b.handle;
}
friend bool operator==(const handle_wrapper& a, const handle_wrapper& b) noexcept {
return a.handle == b.handle;
}
int handle;
};
Run Code Online (Sandbox Code Playgroud)
请注意,我们在这里使用-1作为空句柄值,因为这是open()
失败时返回的值.将这些代码模板化以使其接受其他句柄类型和/或无效值也非常容易.
然后
struct posix_close
{
using pointer = handle_wrapper;
void operator()(pointer fd) const
{
close(*fd);
}
};
int
main()
{
std::unique_ptr<int, posix_close> p(handle_wrapper(open("testing", O_CREAT)));
int fd = *p.get();
}
Run Code Online (Sandbox Code Playgroud)
演示.
小智 7
假设它有意义,可以使用与非指针相似的模式吗?例如,对于POSIX的函数open,返回一个int?
当然,在unique_ptr上使用霍华德的Hinnant 教程,我们可以看到一个激励人心的例子:
// For open
#include <sys/stat.h>
#include <fcntl.h>
// For close
#include <unistd.h>
// For unique_ptr
#include <memory>
int main()
{
auto handle_deleter = [] (int* handle) {
close(*handle);
};
int handle = open("main.cpp", O_RDONLY);
std::unique_ptr<int, decltype(handle_deleter)> uptr
{ &handle, handle_deleter };
}
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用函子而不是lambda:
struct close_handler
{
void operator()(int* handle) const
{
close(*handle);
}
};
int main()
{
int handle = open("main.cpp", O_RDONLY);
std::unique_ptr<int, close_handler> uptr
{ &handle };
}
Run Code Online (Sandbox Code Playgroud)
如果我们使用typedef和"factory"函数,可以进一步减少这个例子.
using handle = int;
using handle_ptr = std::unique_ptr<handle, close_handler>;
template <typename... T>
handle_ptr get_file_handle(T&&... args)
{
return handle_ptr(new handle{open(std::forward<T>(args)...)});
}
int main()
{
handle_ptr hp = get_file_handle("main.cpp", O_RDONLY);
}
Run Code Online (Sandbox Code Playgroud)
只是为了更新答案:C++20 允许在未评估的上下文中使用 lambda(例如decltype
),因此不再需要编写一个结构来调用std::free
以利用空基优化。下面就可以了。
std::unique_ptr<T, decltype([](void *p) { std::free(p); })> ptr;
Run Code Online (Sandbox Code Playgroud)
技巧在于这样的 lambda 是默认可构造的。
归档时间: |
|
查看次数: |
9886 次 |
最近记录: |