有两个用于分配和释放原始指针的函数,我想使用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: %lu\n", 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: %lu\n", 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 ++人员如何处理原始指针?
答案 0 :(得分:1)
我通常使用第二种方法,但我会将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>;
答案 1 :(得分:0)
您可以使用重载的特定于类的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;
}
我认为这种方法更好,因为它解除了分配/解除分配逻辑,但同时也没有造成任何重大的性能损失。
答案 2 :(得分:0)
std::unique_ptr
包含一个不同的实现,用于何时不包含删除操作,而不包含删除操作。由于带有deletor的版本将删除器存储为传递的模板类型,实际问题归结为从构造函数中推导出类模板参数。 (见this thread)
正如您所提到的,第一个选项比第二个选项占用更多空间,但是我不会认为这是一个问题,因为编译器可以进行相当多的优化。
使用my_unique()
的实现是一种减小std::unique_ptr
大小的正确方法,因为它不会将ptr存储到函数中,但是它假设一个真正的函数指针,而它并没有使用lambdas作为删除者。 (这不是问题,尽管它是这种方法的限制)
另一种方法是使用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
的性能开销,并且与原始选项相比,很可能是大量内存开销