简体   繁体   English

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

[英]using custom deleter with unique_ptr

With shared_ptr you can use a custom deleter, like:使用 shared_ptr 您可以使用自定义删除器,例如:

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

and this will remember to fclose the file regardless of how the function exits.无论函数如何退出,这都会记住fclose文件。
However, it seems a bit overkill to refcount a local variable, so I want to use unique_ptr :但是,引用局部变量似乎有点矫枉过正,所以我想使用unique_ptr

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

however, that does not compile.但是,这不会编译。

Is this a defect?这是缺陷吗? Is there a simple workaround?有简单的解决方法吗? Im I missing something trivial?我错过了一些微不足道的东西吗?

Should be应该

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

since http://en.cppreference.com/w/cpp/memory/unique_ptr由于http://en.cppreference.com/w/cpp/memory/unique_ptr

or, since you use C++11, you can use decltype或者,由于您使用 C++11,您可以使用decltype

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

The above answer while its intent is OK and in practice compiles and works is wrong, because it is not specified that you are allowed to take the address of a standard library function.上面的答案虽然它的意图是好的并且在实践中编译和工作是错误的,因为它没有指定允许您获取标准库函数的地址。 A C++ library implementation is allowed to provide different overloads or more parameters (with default arguments).允许 C++ 库实现提供不同的重载或更多参数(带有默认参数)。 Only calling the library function is sanctioned by the standard.只有调用库函数才受到标准的认可。 Therefore, you need to wrap the call to fclose in your own function implementation or lambda, such as因此,你需要在你自己的函数实现或者 lambda 中包装对 fclose 的调用,比如

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

or wait for unique_resource of https://wg21.link/p0052 to become standardized, but even there you need to use the lambda or a deleter function (object), see the more recent versions of p0052.或等待https://wg21.link/p0052 的unique_resource变得标准化,但即使在那里您也需要使用 lambda 或删除器函数(对象),请参阅 p0052 的更新版本。

Note that in a real program you may want to check and act upon the return value of fclose , which could be awkward from within a destructor: you don't get to return a value and throwing exceptions from destructors is a bad idea .请注意,在实际程序中,您可能想要检查fclose的返回值并对其采取行动,这在析构函数中可能会很尴尬:您无法返回值,并且从析构函数中抛出异常是一个坏主意 Similar considerations may or may not apply for other types of pointer.类似的考虑可能适用于也可能不适用于其他类型的指针。

With that caveat out of the way, an alternative approach would be to specify the deleter as a functor:有了这个警告,另一种方法是将删除器指定为函子:

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

using unique_file = std::unique_ptr<std::FILE, file_deleter>;

The type alias allows you to simply write:类型别名允许您简单地编写:

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

This is more ergonomic than having to pass an additional pointer or a lambda every time you create a pointer.这比每次创建指针时都必须传递额外的指针或 lambda 更符合人体工程学。 The use of a functor type also means that the unique_ptr does not have to carry around a separate pointer for the deleter, which allows for space savings relative to the other approaches.使用函子类型还意味着 unique_ptr 不必为删除器携带单独的指针,这与其他方法相比可以节省空间。 To see this, I use the following code:为了看到这一点,我使用以下代码:

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

Using MSVC building for an x64 target, I get the following output:使用 MSVC 构建 x64 目标,我得到以下输出:

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

In this specific implementation, for the case using the functor the unique_ptr is the same size as a raw pointer, which is not possible for the other approaches.在这个特定的实现中,对于使用函子的情况, unique_ptr与原始指针的大小相同,这对于其他方法是不可能的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM