如何仅使用shared_ptr的deleter?

3

我想使用shared_ptr的删除器功能,但不想使用shared_ptr本身。也就是说,当shared_ptr超出作用域时,我想调用一个函数,并且删除器不需要传递任何指针。

我有以下代码,但感觉很简陋。

shared_ptr<int> x(new int, [&](int *me) { delete me; CloseResource(); }); 

有没有一种方法可以不将任何指针与shared_ptr关联起来?

更新:根据许多建议,使用unique_ptr的方式如下:

unique_ptr<int, std::function<void(int*)>> x(new int, [&](int *me) {delete me; CloseResource();});

实话说,它看起来比shared_ptr版本更糟糕,尽管它有多么“好”。

更新:对于那些喜欢使用我制作的这个简单的作用域关闭函数调用器的人,我让它变得更简单了,您甚至都不需要分配或释放对象:

shared_ptr<int> x(NULL, [&](int *) { CloseResource(); });

5
为什么需要使用共享指针,而不是只依赖于对象本身超出作用域的情况? - Mad Physicist
1
首先,如果你不使用shared_ptr的共享生命周期特性,使用unique_ptr会更高效。其次,如果你可以使用Boost库,可以使用Boost.ScopeExit来实现这个功能。第三,如果你不能使用Boost,你可以很容易地自己编写一个类来实现这个功能,以避免使用unique_ptr/shared_ptr的笨拙方法。你可以参考这个链接:https://dev59.com/PnA65IYBdhLWcg3wuRF8。 - Cornstalks
4
“没有shared_ptr部分”是什么意思?你是否对多个客户端使用共享资源感兴趣?因为如果不是,这是一个可怕的想法,因为shared_ptr实际上相当复杂,涉及原子操作、动态块分配等等,这不是一顿免费的饭菜。 - Adrian Lis
1
@stu 因为它不是设计成作用域保护器... 它是用于资源管理的。但无论如何。 - Adrian Lis
3
如果你想这样使用它,那么没有人会阻止你,只是指出这很愚蠢。如果你真的只想要作用域卫士,unique_ptr可能更好,因为它不会动态分配控制块的内存,也不会为资源引用计数产生原子增量。 - Adrian Lis
显示剩余14条评论
2个回答

3

看起来你想做的是将“删除”职责交给“别人”,这样你就不用再担心了。如果是这样,使用带有自定义删除器的unique_ptr(而不是shared_ptr)即可:

struct Foo final {};
Foo* New() { return new Foo; }
void Delete(Foo* p) { delete p; }

int main()
{
    auto p = New();
    std::unique_ptr<Foo, decltype(&Delete)> up(p, &Delete);
}

有一些类似的解决方案在这里列出(链接);如果没有更多关于您实际API的信息(例如,是否可以使用HANDLE是真正指针的技巧),就不会有更多的进展。 请参阅更多内容:最简单和最整洁的c++11 ScopeGuard,其中包括一个建议的std::unique_resource链接(链接)


0

如果你想要像RAII那样的语义结构配合一个lambda函数,也许这种方式会起到作用吧?

namespace details {
  enum class ScopeExitDummy { };
}


template<typename T>
class scope_exit_t
{
public:
  inline scope_exit_t(T&& codeChunk_)
    : m_codeChunk(std::forward<T>(codeChunk_)) {
  }

  inline scope_exit_t(scope_exit_t<T>&& rhs_)
    : m_codeChunk(std::move(rhs_.m_codeChunk))
  {
  }

  ~scope_exit_t() {
    m_codeChunk();
  }

private:
  T m_codeChunk;
};

template<typename T>
inline scope_exit_t<T> operator+(details::ScopeExitDummy, T&& functor_) {
  return scope_exit_t<T>{std::forward<T>(functor_)};
}

#define AtScopeExit auto TW_UNIQUE_VARIABLE(_scopeExit) = details::ScopeExitDummy{} + [&]

使用方法如下:

AtScopeExit {
  CloseResource();
};

注意:TW_UNIQUE_VARIABLE只是一个宏,用于生成一个唯一的变量名,以便代码不会与手写的声明发生冲突。
#define TW_STR_CONCAT_IMPL(x, y) x##y
#define TW_STR_CONCAT(x, y) TW_STR_CONCAT_IMPL(x, y)

#ifdef __COUNTER__
  #define TW_UNIQUE_VARIABLE(prefix) TW_STR_CONCAT(prefix, __COUNTER__)
#else
  #define TW_UNIQUE_VARIABLE(prefix) TW_STR_CONCAT(prefix, __LINE__)
#endif

是的,我可以创建一个模板类,但我试图避免这样做,因为我所需的所有功能已经存在于shared_ptr中。为什么要重复造轮子呢? - stu
4
共享指针不只是在销毁时调用删除器而设计的 - 它具有更多功能。它不是在重新发明轮子,而是在努力让你停止使用那个轮子制造意大利面。 - Adrian Lis
计算机的用途远不止于玩游戏和制作电子表格。难道不是每个人都喜欢重复利用吗?我只是在重复利用现有功能的一部分,而不是全部。真让我惊讶的是,居然没有一个STL解决方案能够解决这个现实世界的问题,而不涉及像这样的不好的做法。 - stu
2
@学生:实际上,答案是“使用shared_ptr/unique_ptr在作用域退出时调用函数很容易,但这样做介于黑客技巧和拼凑之间,最好是创建一个专门设计用于此任务的自定义类。”这有点像把螺丝刀当成锤子来使用……它能起作用(我以前也这样做过),如果你急需解决问题,这样做还算合理,但最好还是使用正确的工具,尤其是当你轻而易举地可以拿到一个适当的锤子时。 - Cornstalks
1
@stu: 这是因为每个人都会编写自己的作用域保护器。这只需要4行代码。实际上,编写一个自定义的作用域保护器比使用shared_ptr更容易。 - Mooing Duck
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接