将weak_ptr与unique_ptr配对是一个好主意吗?

时间:2015-05-21 09:05:03

标签: c++ smart-pointers

我知道将weak_ptrsunique_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配对

2 个答案:

答案 0 :(得分:2)

不,您不能将std::weak_ptrstd::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