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

sp2*_*nny 10 c++ smart-pointers

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

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

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

auto fp = unique_ptr<FILE>( fopen("file.txt", "rt"), &fclose );
Run Code Online (Sandbox Code Playgroud)

但是,这不会编译.

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

For*_*veR 14

应该

unique_ptr<FILE, int(*)(FILE*)>(fopen("file.txt", "rt"), &fclose);
Run Code Online (Sandbox Code Playgroud)

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

或者,因为您使用C++ 11,您可以使用 decltype

std::unique_ptr<FILE, decltype(&fclose)>
Run Code Online (Sandbox Code Playgroud)


Pet*_*Som 6

上面的答案在意图上是可以的,实际上在编译和工作中是错误的,因为未指定允许您使用标准库函数的地址。允许C ++库实现提供不同的重载或更多参数(带有默认参数)。该标准仅允许调用库函数。因此,您需要将对fclose的调用包装在您自己的函数实现或lambda中,例如

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

或等待unique_resourcehttps://wg21.link/p0052成为标准化,但即使在那里,你需要使用拉姆达或有删除功能(对象),看到最近p0052的版本。

  • 您可以通过显式转换函数类型来区分重载集。 (3认同)

小智 6

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

解决了这个问题后,另一种方法是将删除器指定为函子:

struct file_deleter {
    void operator()(std::FILE* fp) { std::fclose(fp); }
};

using unique_file = std::unique_ptr<std::FILE, file_deleter>;
Run Code Online (Sandbox Code Playgroud)

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

unique_file f{ std::fopen("file.txt", "rt") };
Run Code Online (Sandbox Code Playgroud)

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

unique_file f{ std::fopen("file.txt", "rt") };
Run Code Online (Sandbox Code Playgroud)

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

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';
}
Run Code Online (Sandbox Code Playgroud)

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