将unique_ptr用于原始指针的正确方法是什么
What is the proper way to use unique_ptr for raw pointers?
有两个用于分配和释放原始指针的函数,我想使用C++轻松完成繁琐的工作。我找到了两种选择来unique_ptr
处理它,但对我来说它们都不好看:
char *raw_alloc();
void raw_free(char *ptr);
int main (int argc, char *argv[])
{
//First option:
{
std::unique_ptr<char, decltype(&raw_free)> ptr (raw_alloc(), raw_free);
printf ("size1: %lun", sizeof(ptr) / sizeof(char *));
}
//Second option:
{
struct deleter { void operator()(char *ptr) { raw_free(ptr); } };
std::unique_ptr<char, deleter> ptr (raw_alloc());
printf ("size2: %lun", sizeof(ptr) / sizeof(char *));
}
}
输出说第一个指针是第二个指针的两倍;当然,保留指向释放函数的指针需要空间。
同时,第二个选项要求我为我的类型创建存根删除器。当然,我可以编写一个函数模板来为我执行此操作:
template <typename T, void (*D)(T*)>
auto my_unique (T *ptr) {
struct deleter { void operator()(T *ptr) { D(ptr); } };
return unique_ptr<T,deleter>(ptr);
};
但是为什么unique_ptr
不能为我做这件事,而只是接受删除器函数作为第二个模板参数呢?熟练C++人如何处理原始指针?
我通常使用第二种方法,但我会将Deleter
类保留在全局范围内,靠近raw_alloc
和raw_free
的声明。定义一个raw_unique_ptr
速记可能会有所帮助:
char *raw_alloc();
void raw_free(char *ptr);
struct raw_releter
{ void operator()(char * const ptr) const noexcept { raw_free(ptr); } };
using raw_unique_ptr = std::unique_ptr<char, raw_releter>;
,std::unique_ptr
包含与不包含删除器时不同的实现。由于带有 deletor 的版本将删除器存储为传递的模板类型,因此实际问题归结为从构造函数推导类模板参数。(请参阅此线程)
正如您所提到的,第一个选项比第二个选项占用更多的空间,但是我不认为这是一个问题,因为编译器可以优化很多。
使用my_unique()
实现是减小std::unique_ptr
大小的正确方法,因为它不会将 ptr 存储到函数中,但它假定一个真正的函数指针,而它不包括使用 lambda 作为删除器。(这不是问题,尽管这是这种方法的局限性)
另一种方法是使用 std::shared_ptr
,它不需要知道析构函数的外观,但是它总是存储删除器,并且会占用大量内存,而不必知道删除器的实际实现。
在我看来,当std::unique_ptr
在本地时,我会使用类似于您的选项 1 或 2 的东西。如果这是 API 的一部分,则很难选择实现,并且所有三个选项(选项 1、2 或 std::shared_ptr)都很有趣。我会选择std::shared_ptr
,因为您可以将其与其他具有自己的删除器的 API 结合使用。(考虑一个以JSON或XML转换数据的工厂,这两个工厂都是将char*清理到生成的流中的不同函数)
与std::shared_ptr
类似的另一种方法是创建一个 std::unique_ptr<T, std::function<void(T*)>>
,其中包含由于std::function
而导致的性能开销,并且与原始选项相比很可能是较大的内存开销。
您可以使用重载的特定于类的 new 和 delete 运算符将原始指针包装到类中:
char* raw_alloc()
{
// Don't allocate anything, just a demonstration of approach
return (char*)"Hello, World!";
}
void raw_free(char* ptr)
{
cout << ptr << endl;
}
class WrappedRawPointer
{
public:
static void* operator new(std::size_t)
{
return raw_alloc();
}
static void operator delete(void* ptr, std::size_t)
{
raw_free((char*)ptr);
}
};
int main() {
// Will print "Hello, World!" twice: once for explicit
// printing and once for deleter.
auto ptr = unique_ptr<WrappedRawPointer>(new WrappedRawPointer());
cout << (const char*) ptr.get() << endl;
return 0;
}
我相信这种方法更好,因为它解耦了分配/释放逻辑,但同时不会造成任何重大的性能损失。
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- C++从另一个类访问公共静态向量的正确方法是什么
- 在 c++ 中拥有一组结构的正确方法是什么?
- 通过JNI传递数据数组的最快方法是什么
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 使用不同的CRT将新的C++代码与旧的(二进制)组件隔离开来的最佳方法是什么
- 当无法使用模板和宏时,生成类型变体C++代码的最简单方法是什么?
- 在另一个类视图中添加最多2个图表的正确方法是什么
- 在C++中样板"冷/never_inline"错误处理技术的最佳方法是什么?
- 在 c++ 中对类中的 c 字符串动态数组进行排序的最佳方法是什么?
- 在C++中包含原型文件的正确方法是什么?
- 在 OpenCV C++ 中估计基本矩阵之前对相应点进行归一化的正确方法是什么?
- 在PostgreSQL中根据它们的ID选择大量行的最快方法是什么?
- 在OSX上使用CMake将Adobe的XMP工具包构建为共享库的最简单方法是什么?
- 将一系列整数放入类的最佳方法是什么?
- 从长整整转换为uint64_t的推荐方法是什么?
- 将此布尔值传递给此函数的最有效方法是什么?
- 通过比较C++中的行在 txt 文件中搜索的最简单方法是什么?