使用自定义删除器增强智能指针

时间:2015-01-15 12:02:21

标签: c++ boost smart-pointers

我可以理解boost::shared_ptr在调用自定义删除函数之前不会验证NULL,但我怎样才能实现这一点?这将帮助我避免为fclose或任何没有(正确)指定行为的函数编写哑包装器。

我的提升:#define BOOST_VERSION 104500。这不是C ++ 11(因为我使用了boost)。

问题与:make shared_ptr not use delete

有关

示例代码:

static inline
FILE* safe_fopen(const char* filename, const char* mode)
{
      FILE* file = NULL;
      (void)fopen_s(&file, filename, mode);
      return file;
}

static inline
void safe_fclose(FILE* file)
{
      if (file)
         BOOST_VERIFY(0 == fclose(file));
}

...

boost::shared_ptr<FILE> file( safe_fopen(FILE_DOWNLOAD, "rb"), safe_fclose);
...
//    now it is created => open it once again
file.reset( safe_fopen(FILE_DOWNLOAD, "rb"), safe_fclose);

编辑

我的问题最初有关于使用shared_ptr的第二部分:为什么将删除器作为函数参数而不是模板参数?显然,答案就在这里:Why does unique_ptr take two template parameters when shared_ptr only takes one? C ++ 11答案是unique_ptr,但是为什么boost没有提供 - 我们永远不会知道。

2 个答案:

答案 0 :(得分:1)

似乎是empty(非拥有)shared_ptr和null之间的区别。

Nulled将会(并且应该)调用删除器(它可能意味着特定类型的资源句柄。实际上,值&#34; 0&#34;可能甚至不是特殊的。)

空的将调用删除器,但带有nullptr_t参数。因此,您通常可以创建一个包装任何adhoc删除器的包装器(无论是内联lambda还是像&::free这样的函数指针):

template <typename F>
OptionalDeleter<F> make_optional_deleter(F&& f) { return OptionalDeleter<F>(std::forward<F>(f)); }

int main() {
    auto d = make_optional_deleter([](void*){std::cout << "Deleting\n";});

    using namespace std;
    {
        shared_ptr<int> empty(std::nullptr_t{}, d);
    } // deleter not called, empty

    {
        shared_ptr<int> thing(static_cast<int*>(nullptr), d);
    } // deleter called, thing not empty
}

将打印

Empty, not deleting
Deleting

看起来你非常&#34; &#34;,忠实地将原始意图传递给底层API。这是一个GoodThing™,因为如果shared_ptr<>做出了#34;任意&#34;例外情况,在删除&#34; NULL&#34; (或默认值)值是有意义的,不应跳过。

现在,选择是你的,应该是。

当然,如果资源句柄值是&#34; NULL&#34;那么你可以使用非常类似的包装策略,就像我刚刚展示的那样,通常会跳过底层的API调用。或者无效。

<强> Live On Coliru

#include <memory>
#include <iostream>

template<typename F>
struct OptionalDeleter final {
    explicit OptionalDeleter(F&& f) : _f(std::forward<F>(f)) {}

    template <typename... Ts>
        void operator()(Ts&&... args) const { _f(std::forward<Ts>(args)...); }

    void operator()(std::nullptr_t)   const { std::cout << "Empty, not deleting\n"; }
  private:
    F _f;
};

template <typename F>
OptionalDeleter<F> make_optional_deleter(F&& f) { return OptionalDeleter<F>(std::forward<F>(f)); }

int main() {
    auto d = make_optional_deleter([](void*){std::cout << "Deleting\n";});

    using namespace std;
    {
        shared_ptr<int> empty(std::nullptr_t{}, d);
    } // deleter not called, empty

    {
        shared_ptr<int> thing(static_cast<int*>(nullptr), d);
    } // deleter called, thing not empty
}

答案 1 :(得分:1)

有一种想法可能不是试图保护析构函数,而是应该强制构造失败,从而避免在破坏期间检查无效。

static inline
FILE* safe_fopen(const char* filename, const char* mode)
{
      FILE* file = NULL;
      (void)fopen_s(&file, filename, mode);
      if (!file)
         throw std::exception(...); // check errno
      return file;
}

然而,这无法帮助修复未初始化的boost::shared_ptr,如果由于某种原因仍然会发生这种情况。

make shared_ptr not use delete不同 我认为由于功能的性质,你只是坚持使用这些较长的包装器。