std::shared_ptr 自定义参考计数器
std::shared_ptr custom reference counter
我目前正在编写一个代码,我需要在代码的多个部分之间静态共享一个对象的实例。所以基本上是一个单身人士。该实例将被共享,直到没有人再使用它为止。此时我需要在删除实际实例之前执行一些清理操作。
如果在此之后有人请求共享实例,它将获得一个新创建的实例,依此类推。
所以我写了这样的东西:
template<typename F>
struct static_filter
{
template<typename ...Args>
static std::shared_ptr<F> get(Args... a)
{
return _inst = _inst ?: std::shared_ptr<F>(new F(a...));
}
static void cleanup()
{
// some cleanup operation over inst
_inst.reset();
}
private:
static std::shared_ptr<F> _inst;
};
template<typename F> std::shared_ptr<F> static_filter<F>::_inst{nullptr};
现在我正在寻找一种方法来自动检测何时没有人再使用 _inst。
基本上,每次 _inst 的 use_count() 下降到 1 时,我都想得到一个回调。此时我将能够清理和重置。
我想避免为此实现我自己的引用计数(应用程序到处都在使用 shared_ptr,更改为自定义类型会有点麻烦)。
我尝试在我的实例上使用自定义删除器,大致如下:
template<typename F>
struct static_filter
{
template<typename ...Args>
static std::shared_ptr<F> get(Args... a)
{
return _inst = _inst ?: std::shared_ptr<F>(new F(a..., **static_filter<F>::cleanup**));
}
static void cleanup()
{
// some cleanup operation over inst
_inst.reset();
}
private:
static std::shared_ptr<F> _inst;
};
template<typename F> std::shared_ptr<F> static_filter<F>::_inst{nullptr};
但显然这不起作用,因为我的引用计数器实际上从未达到 0。
有谁知道是否有办法使用 shared_ptr 实现此目的?
谢谢。
你在这里使用 shared_ptr
在语义层面上是错误的。您不想拥有此 static
变量的所有权,而只是通过它进行观察。这是 std::weak_ptr
的用例。有了这个静态变量,你可以观察对象,return 如果它存在并且不干扰它的破坏,当其他人持有对它的引用时 return 它作为 shared_ptr
。
这看起来如下:
template<typename F>
struct static_filter
{
template<typename ...Args>
static std::shared_ptr<F> get(Args... a)
{
// Returns the shared_ptr observed, if it exists and is not expired, empty ptr otherwise
// is atomic
auto shared = _inst.lock();
if(!shared) {
_inst = (shared = std::shared_ptr<F>(new F(a..., **static_filter<F>::cleanup**));
}
return shared;
}
static void cleanup()
{
// Always check if the calling object is really the managed object
// some cleanup operation over inst
_inst.reset();
}
private:
static std::weak_ptr<F> _inst;
};
template<typename F> std::weak_ptr<F> static_filter<F>::_inst{};
请注意,这 不是 线程安全的(您之前的代码也不是线程安全的),因为当指针为空时两个同时调用 get
,都可以看到指针为空,都构造了一个F
类型的新对象。您必须添加一个 std::lock_guard
来避免这种情况。
请注意,即使您可以在 use_count == 1
上添加回调,这本质上也是不安全的。这就是为什么 std::shared_ptr::unique()
从 C++17 开始被弃用并在 C++20 中被删除的原因(参见 here and here)
我目前正在编写一个代码,我需要在代码的多个部分之间静态共享一个对象的实例。所以基本上是一个单身人士。该实例将被共享,直到没有人再使用它为止。此时我需要在删除实际实例之前执行一些清理操作。
如果在此之后有人请求共享实例,它将获得一个新创建的实例,依此类推。
所以我写了这样的东西:
template<typename F>
struct static_filter
{
template<typename ...Args>
static std::shared_ptr<F> get(Args... a)
{
return _inst = _inst ?: std::shared_ptr<F>(new F(a...));
}
static void cleanup()
{
// some cleanup operation over inst
_inst.reset();
}
private:
static std::shared_ptr<F> _inst;
};
template<typename F> std::shared_ptr<F> static_filter<F>::_inst{nullptr};
现在我正在寻找一种方法来自动检测何时没有人再使用 _inst。 基本上,每次 _inst 的 use_count() 下降到 1 时,我都想得到一个回调。此时我将能够清理和重置。
我想避免为此实现我自己的引用计数(应用程序到处都在使用 shared_ptr,更改为自定义类型会有点麻烦)。
我尝试在我的实例上使用自定义删除器,大致如下:
template<typename F>
struct static_filter
{
template<typename ...Args>
static std::shared_ptr<F> get(Args... a)
{
return _inst = _inst ?: std::shared_ptr<F>(new F(a..., **static_filter<F>::cleanup**));
}
static void cleanup()
{
// some cleanup operation over inst
_inst.reset();
}
private:
static std::shared_ptr<F> _inst;
};
template<typename F> std::shared_ptr<F> static_filter<F>::_inst{nullptr};
但显然这不起作用,因为我的引用计数器实际上从未达到 0。
有谁知道是否有办法使用 shared_ptr 实现此目的?
谢谢。
你在这里使用 shared_ptr
在语义层面上是错误的。您不想拥有此 static
变量的所有权,而只是通过它进行观察。这是 std::weak_ptr
的用例。有了这个静态变量,你可以观察对象,return 如果它存在并且不干扰它的破坏,当其他人持有对它的引用时 return 它作为 shared_ptr
。
这看起来如下:
template<typename F>
struct static_filter
{
template<typename ...Args>
static std::shared_ptr<F> get(Args... a)
{
// Returns the shared_ptr observed, if it exists and is not expired, empty ptr otherwise
// is atomic
auto shared = _inst.lock();
if(!shared) {
_inst = (shared = std::shared_ptr<F>(new F(a..., **static_filter<F>::cleanup**));
}
return shared;
}
static void cleanup()
{
// Always check if the calling object is really the managed object
// some cleanup operation over inst
_inst.reset();
}
private:
static std::weak_ptr<F> _inst;
};
template<typename F> std::weak_ptr<F> static_filter<F>::_inst{};
请注意,这 不是 线程安全的(您之前的代码也不是线程安全的),因为当指针为空时两个同时调用 get
,都可以看到指针为空,都构造了一个F
类型的新对象。您必须添加一个 std::lock_guard
来避免这种情况。
请注意,即使您可以在 use_count == 1
上添加回调,这本质上也是不安全的。这就是为什么 std::shared_ptr::unique()
从 C++17 开始被弃用并在 C++20 中被删除的原因(参见 here and here)