使用带有unique_ptr的自定义删除器

时间:2014-10-14 12:34:03

标签: c++ smart-pointers

使用shared_ptr,您可以使用自定义删除器,例如:

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

这将记住fclose文件,无论函数如何退出。
但是,重新计算局部变量似乎有点过分,所以我想使用unique_ptr

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

然而,这不会编译。

这是缺陷吗?有一个简单的解决方法吗?我错过了一些微不足道的东西吗?

3 个答案:

答案 0 :(得分:13)

应该是

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)>

答案 1 :(得分:5)

上述答案虽然其意图是正确的,但在实践中编译和工作是错误的,因为没有指定允许您获取标准库函数的地址。允许C ++库实现提供不同的重载或更多参数(使用默认参数)。只有调用库函数才能被标准认可。因此,您需要在自己的函数实现或lambda中包含对fclose的调用,例如

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

或等待https://wg21.link/p0052unique_resource变为标准化,但即使在那里你需要使用lambda或删除函数(对象),请参阅更新版本的p0052。

答案 2 :(得分:1)

请注意,在真实的程序you may want to check and act upon the return value of fclose中,它在析构函数中可能很笨拙:您不会返回值和throwing exceptions from destructors is a bad idea。类似的考虑因素可能适用于也可能不适用于其他类型的指针。

避免这种警告,另一种方法是将删除程序指定为函子:

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不必携带用于删除程序的单独指针,这相对于其他方法可以节省空间。为此,我使用以下代码:

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';
}

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

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

在此特定实现中,对于使用仿函数的情况,unique_ptr与原始指针的大小相同,而其他方法则无法实现。