3

我想知道如何(使用 C++11 并希望使用向后(boost 或 TR1)兼容的智能指针类型)实现:

一个类实例 ( ModelController) 拥有一个资源 ( InputConsumer),而另一个组件 ( InputSender,在本例中是一个单例) 可以访问它。

该模型InputSender包含一个对 的引用列表InputConsumers,其中会有很多。

ModelController可能有 none、one 或 many InputConsumers,并且可能有很多ModelControllers。InputSender不知道。

最好的方法是:一种InputSender跟踪InputConsumers分配给它的方法,它可以自己找出个人InputConsumers是否有效。

在我看来,这weak_ptr是完美的,因为它们的使用需要检查这种情况。

如果InputSender停止跟踪它的任何weak_ptrrefs,则不会发生任何不好的事情,相应InputConsumer的 s 只会经历无线电静默。

如果 aModelController被删除,或者 aModelController删除了它的一些InputConsumers,任何InputSender已经注册到它们的 s 将在下次尝试访问它们时识别它们不再存在,并且可以清理它们,而无需发送消息或做任何事。

所以问题是,这是使用shared_ptrand的合适情况weak_ptr吗?我想知道是否shared_ptr完全合适,因为InputConsumers 在概念上由它们的 s拥有ModelController,所以它们应该是成员变量。我不知道ModelController只通过shared_ptr. 我不知道是否unique_ptrweak_ptr. 我应该只管理ctor/dtorshared_ptr中的 s 吗?ModelController

可能还有一个众所周知的(对我来说不是!)设计模式,所以如果有人知道这样的事情,请告诉我。

4

2 回答 2

1

我在共享指针方面没有很多专业知识,但是是的,这似乎非常适合使用weak_ptr.

在这种情况下,您只会感到恼火:

  1. 您想InputConsumer直接使用 s 作为 s 的成员ModelController,因为它是一种微不足道的所有权关系。
  2. 您被迫使用 ashared_ptr使其与 a 一起使用weak_ptr

我认为这是通过使用 ashared_ptr作为成员对象的别名来解决的。根据C++.com

此外,shared_ptr 对象可以共享指针的所有权,同时指向另一个对象。这种能力称为别名(参见构造函数),通常用于在拥有它们所属的对象时指向成员对象。

我自己从来没有做过,但这似乎适合你的情况:

  • 有sInputConsumer的成员ModelController
  • shared_ptr每个人都有一个别名
  • InputSender在using weak_ptrs中引用它们

编辑

这是一个完整的最小工作示例:

#include <iostream>
#include <memory>

using namespace std;

// A class to try our smart pointers on
struct Foo 
{
    Foo() { cout << "constructing Foo\n"; }
    ~Foo() { cout << "destructing Foo\n"; }
};

// A class that owns some Foo as members
struct Owner
{
    // The actual members
    Foo foo1;
    Foo foo2;

    // A fake shared pointer whose purpose is:
    //   1) to be of type shared_ptr<>
    //   2) to have the same lifetime as foo1 and foo2
    shared_ptr<Owner> self;

    // A fake deleter that actually deletes nothing 
    struct Deleter
    {
        void operator() (Owner *) { cout << "pretend to delete Owner\n"; }
    };

    Owner() : self(this, Deleter()) { cout << "constructing Owner\n"; }
    ~Owner()                        { cout << "destructing Owner\n"; }
};

// A class that holds a reference to a Foo
struct Observer
{
    // A reference to a Foo, as a weak pointer
    weak_ptr<Foo> foo_ptr;

    Observer(const shared_ptr<Foo> & foo_ptr) : foo_ptr(foo_ptr)
    {
        cout << "constructing Observer\n";
    }
    ~Observer() { cout << "destructing Observer\n"; }

    void check()
    {
        if(foo_ptr.expired())
            cout << "foo expired\n";
        else
            cout << "foo still exists\n";
    }   
};  

int main()
{
    // Create Owner, and hence foo1 and foo2
    Owner * owner = new Owner;

    // Create an observer, passing an alias of &(owner->foo1) to ctor
    Observer observer(shared_ptr<Foo>(owner->self, &(owner->foo1)));

    // Try to access owner->foo1 from observer
    observer.check();
    delete owner;
    observer.check();

    return 0;
}

它打印:

constructing Foo
constructing Foo
constructing Owner
constructing Observer
foo still exists
destructing Owner
pretend to delete Owner
destructing Foo
destructing Foo
foo expired
destructing Observer

棘手的部分是能够创建一个weak_ptrto owner->foo1(对于 将是相同的foo2)。为此,我们首先需要一个shared_ptr是 的别名owner->foo1。这只能通过以下方式完成:

shared_ptr<Foo> alias(other_shared_ptr, &(owner->foo1));

其中other_shared_ptrashared_ptr<T>的生命周期至少与 中的一个一样长owner->foo1。为了实现这一点,使用shared_ptr<T>也是成员的ownera 是一个好主意,因为它可以确保生命周期是相同的。最后,我们需要一个有效的非空指针给它,因为我们不想在堆上创建任何东西,我们必须使用一个现有的对象。this是一个很好的候选者,因为我们知道它是有效的,并且只有在其成员被销毁后才会被销毁。因此我们other_shared_ptr的例如是:

shared_ptr<Owner> self(this);

但是,这意味着当self超出范围时,即在 的销毁期间owner,它将调用delete this. 我们希望发生这种删除,否则this会被删除两次(这是未定义的行为,实际上是段错误)。因此,我们还向self实际上不删除任何内容的 Deleter 的构造函数提供。

其余的代码应该是不言自明的注释。

于 2013-08-27T05:08:42.093 回答
0

只是一个更完整的工作片段,显示 std::weak_ptr 动态,也许可以提供更多帮助。(我特别喜欢这个 expired() 语义);

#include<iostream>
#include<memory>
#include<string>

class MessageProcessor{                
};

class Message{
public:

        Message(std::shared_ptr<MessageProcessor>  _msg_proc, int _id){
                proc_weak = std::weak_ptr<MessageProcessor>(_msg_proc);
                proc_shared = _msg_proc;
                id = _id;
        }

        std::weak_ptr<MessageProcessor> proc_weak;
        std::shared_ptr<MessageProcessor> proc_shared;        
        int id;
};

int main(){

        std::shared_ptr<MessageProcessor> proc(new MessageProcessor());

        Message msg(proc,1);

        // Here we have proc with 2 shared_ptr refs: 'proc' and 'msg.proc_shared'
        // As expected 'msg.proc_weak is not expired'
        if( !msg.proc_weak.expired() )
                std::cout << "1) proc_weak is not EXPIRED. proc.use_count() == " << proc.use_count() << std::endl;        

        // make one of shared_ptr ref, point to other place
        msg.proc_shared = std::shared_ptr<MessageProcessor>();        

        // there is still the 'proc' reference        
        if( !msg.proc_weak.expired() )
                std::cout << "2) proc_weak is not EXPIRED (yet). proc.use_count() == " << proc.use_count() << std::endl;

        // 'erase' the last reference
        proc = std::shared_ptr<MessageProcessor>(); 

        // Finally... There is no more refs in shared_pointer!
        if( msg.proc_weak.expired() )
                std::cout << "3) proc_weak has EXPIRED. proc.use_count() == " << proc.use_count() << std::endl;

        return 0; 
}
于 2014-01-30T10:40:33.443 回答