使用带有uniqueptr的自定义deleter

using custom deleter with unique_ptr

本文关键字:自定义 deleter uniqueptr      更新时间:2023-10-16

使用shared_ptr,您可以使用自定义删除程序,如:

auto fp = shared_ptr<FILE>( fopen("file.txt", "rt"), &fclose );
fprintf( fp.get(), "hellon" );

并且这将记住CCD_ 1该文件,而不管该函数如何退出
然而,重新计数局部变量似乎有点过头了,所以我想使用unique_ptr:

auto fp = unique_ptr<FILE>( fopen("file.txt", "rt"), &fclose );

然而,这并不能编译。

这是缺陷吗?有没有简单的解决方法?我是不是错过了一些琐碎的事情?

应该是

unique_ptr<FILE, int(*)(FILE*)>(fopen("file.txt", "rt"), &fclose);

自http://en.cppreference.com/w/cpp/memory/unique_ptr

或者,由于您使用C++11,因此可以使用decltype

std::unique_ptr<FILE, decltype(&fclose)>

上面的答案虽然目的是可以的,但在实践中编译和工作是错误的,因为没有指定允许您使用标准库函数的地址。允许C++库实现提供不同的重载或多个参数(带有默认参数)。标准只允许调用库函数。因此,您需要将对fclose的调用封装在自己的函数实现或lambda中,例如

unique_ptr<FILE, int(*)(FILE*)>(fopen("file.txt", "rt"),
   [](FILE *fp)->int{ if(fp) return ::fclose(fp); return EOF;});

或等待的unique_resourcehttps://wg21.link/p0052要实现标准化,但即使在那里也需要使用lambda或deleter函数(对象),请参阅p0052的最新版本。

请注意,在实际程序中,您可能需要检查fclose的返回值并对其采取行动,这在析构函数中可能会很尴尬:您无法返回值,并且从析构函数抛出异常是个坏主意。类似的考虑可能适用于也可能不适用于其他类型的指针。

有了这个警告,另一种方法是将deleter指定为函子:

struct file_deleter {
    void operator()(std::FILE* fp) { std::fclose(fp); }
};
using unique_file = std::unique_ptr<std::FILE, file_deleter>;

类型别名允许您简单地写:

unique_file f{ std::fopen("file.txt", "rt") };

这比每次创建指针时都必须传递一个额外的指针或lambda更符合人体工程学。函子类型的使用也意味着unique_ptr不必为deleter携带单独的指针,这可以相对于其他方法节省空间。要看到这一点,我使用以下代码:

int main()
{
    std::unique_ptr<FILE, decltype(&fclose)> f1{ nullptr, &fclose };
    std::unique_ptr<std::FILE, void(*)(std::FILE*)> f2{
        nullptr, [](std::FILE* p) { std::fclose(p); } };
    unique_file f3{ nullptr };
    std::FILE* f4{ nullptr };
    std::cout << "sizeof(f1) = " << sizeof(f1) << 'n';
    std::cout << "sizeof(f2) = " << sizeof(f2) << 'n';
    std::cout << "sizeof(f3) = " << sizeof(f3) << 'n';
    std::cout << "sizeof(f4) = " << sizeof(f4) << 'n';
}

使用MSVC构建x64目标,我得到以下输出:

sizeof(f1) = 16
sizeof(f2) = 16
sizeof(f3) = 8
sizeof(f4) = 8

在这个特定的实现中,对于使用函子的情况,unique_ptr的大小与原始指针的大小相同,这对于其他方法是不可能的。