我知道将weak_ptrs
与unique_ptrs
一起使用听起来很荒谬,但请耐心等待。
我有一组作用于它们的小部件和动画。小部件有一个明确的所有者,他们创建并销毁它们。所有小部件都在一个线程中创建,销毁和动画,因此在动画代码运行时不能销毁一个小部件。正如您所看到的,窗口小部件以某种方式与动画共享,但如果窗口小部件被删除,动画应该停止。
当前的方法是将std::unique_ptr
用于窗口小部件的所有者,并将它们作为动画的原始指针公开。这使得查找/调试悬空指针非常困难。一个提议是在所有者类中更改为std::shared_ptr
并将std::weak_ptrs
公开给动画,但这会在系统中添加一些不需要/不需要的开销。
是否有可能(一个好主意?)在std::unique_ptr
之上创建某种类型的weak_ptr,它只标记指针被删除了?如果是的话,您能否建议我使用单线程使用的最小开销的一些实现。
编辑:
还有一个澄清 - 小部件在一个线程中使用,但应用程序有多个线程。此外,许多动画并行运行,每个动画更新60次/秒。 std::shared_ptr/std::weak_ptr
的开销来自std::shared_ptr
内部使用的(原子)计数器,在这种特殊情况下实际上不需要。
编辑:
我不是问我是否可以std::weak_ptr
使用std::unique_ptr
,我知道这是不可能的。我问是否可以构建与std::weak_ptr
具有类似行为的东西,可以与std::unique_ptr
配对
答案 0 :(得分:2)
不,您不能将std::weak_ptr
与std::unique_ptr
一起使用。您将其设为std::shared_ptr
并公开std::weak_ptr
,就像您说的那样。
就引用计数的开销而言,我非常怀疑这将是您的应用程序的瓶颈,所以只有当它变为(可能永远不会)时,才会对此进行分析和担心。
答案 1 :(得分:0)
当然,这是一个合理的想法。它提供对对象生命周期的控制,同时为从属线程提供检测其消失的机会。
当然,弱对象的lock()
方法需要返回一些本身不允许重新共享的方法。
您可以通过封装现有的shared_ptr和weak_ptr对象来实现此目的。
一个简单的例子:
#include <iostream>
#include <memory>
// some type we're going to use for testing
struct Foo {
~Foo() {
std::cout << "Foo destroyed" << std::endl;
}
void use() const {
std::cout << "using Foo" << std::endl;
}
};
// forward declaration
template<class T> struct weak_object_ptr;
// a pointer that keeps the object alive but is not itself copyable
template<class T>
struct keep_alive_ptr
{
// make it moveable
keep_alive_ptr(keep_alive_ptr&&) = default;
keep_alive_ptr& operator=(keep_alive_ptr&&) = default;
// provide accessors
T& operator*() const {
return *_ptr;
}
T* operator->() const {
return _ptr.get();
}
private:
// private constructor - the only way to make one of these is to lock a weak_object_ptr
keep_alive_ptr(std::shared_ptr<T> ptr)
: _ptr { std::move(ptr) }
{}
// non-copyable
keep_alive_ptr(const keep_alive_ptr&) = delete;
keep_alive_ptr& operator=(const keep_alive_ptr&) = delete;
friend weak_object_ptr<T>;
std::shared_ptr<T> _ptr;
};
// a weak reference to our shared object with single point of ownership
template<class T>
struct weak_object_ptr
{
weak_object_ptr(std::weak_ptr<T> w)
: _weak { std::move(w) }
{}
keep_alive_ptr<T> lock() const {
return keep_alive_ptr<T> { _weak.lock() };
}
private:
std::weak_ptr<T> _weak;
};
// a shared object store and lifetime controller
template<class T>
struct object_controller
{
// helpful universal constructor
template<class...Args>
object_controller(Args&&...args)
: _controller { std::make_shared<T>(std::forward<Args>(args)...) }
{}
weak_object_ptr<T> get_weak() const {
return weak_object_ptr<T> { _controller };
}
void reset() {
_controller.reset();
}
private:
std::shared_ptr<T> _controller;
};
// test
using namespace std;
int main(){
auto foo_controller = object_controller<Foo> {};
auto weak1 = foo_controller.get_weak();
auto weak2 = foo_controller.get_weak();
{
auto strong1 = weak1.lock();
strong1->use();
cout << "trying to destroy Foo\n";
foo_controller.reset();
auto strong2 = weak2.lock();
strong2->use();
cout << "strong2 going out of scope\n";
}
return 0;
}
预期输出(请注意,Foo的破坏最早在法律允许的情况下发生):
using Foo
trying to destroy Foo
using Foo
strong2 going out of scope
Foo destroyed