2015-05-21 44 views
3

我知道这听起来很荒谬,weak_ptrsunique_ptrs,但请忍受我。是否将weak_ptr与unique_ptr配对是一个好主意?

我有一组小部件和动画对它们起作用。小部件有一个明确的所有者,他们创建并销毁它们。所有的小部件都是在一个线程中创建,销毁和动画的,所以在动画代码运行时不能销毁一个小部件。正如你所看到的,这些小部件是以某种方式与动画共享的,但是如果小部件被删除,动画应该停止。

当前的方法是使用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_ptrstd::unique_ptr,我知道这是不可能的。我问,如果它是一个好主意/可以建立类似的行为作为东西是std::weak_ptr可以std::unique_ptr

+0

我想你可以使用一个唯一的指针与一个定制的删除器,可以跟踪不太弱的指针。尽管如此,我看不出如何比'shared_ptr'少开销。我只是使用'shared_ptr',因为那正是你想要的。 –

+0

但相反,您是通过制定自己独特的弱ptr解决方案来增加开销,这是否好? –

+0

'开销来自(原子)计数器,在这种情况下实际上并不需要。'是的,你需要一些(原子)值来查看指针是否有效,原子布尔(标志)与原子计数器没有多大区别。 – KillianDS

回答

2

没有配对,你不能std::unique_ptr使用std::weak_ptr。就像你说的那样,你将它制作为std::shared_ptr并展示std::weak_ptr。就引用计数的开销而言,我非常怀疑这将是你的应用程序的瓶颈,所以只有当它变成(可能永远不会)如此时,才能简介和担心。

+1

我不建议使用std :: weak_ptr,因为这不能使用。我建议构建一些行为类似于std :: weak_ptr的东西,但是在std :: unique_ptr的上下文中 – Felics

+0

@KillianDS我编辑了问题。 – Felics

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

预期输出(注意,富的破坏发生早,因为它是法律允许的):

using Foo 
trying to destroy Foo 
using Foo 
strong2 going out of scope 
Foo destroyed 
相关问题