我想知道如何(使用C ++ 11并希望与向后(boost或TR1)兼容的智能指针类型)实现:
一个类实例(ModelController
)拥有一个资源(InputConsumer
),而另一个组件(InputSender
,在这种情况下是一个单例)可以访问它。
模型InputSender
包含InputConsumers
的引用列表,其中有许多引用。
ModelController
可能没有,一个或多个InputConsumers
,并且可能有许多ModelController
。 InputSender
无法识别。
以下是最好的方法:InputSender
跟踪分配给它的InputConsumers
的方式,以便能够自行查明个人InputConsumers
是否有效或不。
在我看来weak_ptr
对于这个目的是完美的,因为它们的使用需要检查这种情况。
如果InputSender
停止跟踪任何weak_ptr
引用,则不会发生任何不良事件,相应的InputConsumer
只会遇到无线电静默。
如果ModelController
被删除,或者ModelController
删除了部分InputConsumer
,则已注册的任何InputSender
将在下次识别他们试图访问他们不再存在的东西,并且可以清理,而无需发送消息或做任何事情。
所以问题是,这是使用shared_ptr
和weak_ptr
的合适情况吗?我想知道shared_ptr
是否完全合适,因为InputConsumer
在ModelController
概念上是拥有,因此它们应该是成员变量。我不知道ModelController
仅通过shared_ptr
管理它们有多大意义。我无法判断unique_ptr
是否与weak_ptr
一起使用。我应该只管理shared_ptr
的ctor / dtor中的ModelController
吗?
可能还有一个众所周知的(不是我!)设计模式,所以如果有人知道这样的事情请告诉我。
答案 0 :(得分:1)
我在共享指针方面没有很多专业知识,但是,这似乎是weak_ptr
的恰当用法。
在这种情况下,你只是恼火:
InputConsumer
直接用作ModelController
的成员,因为它是一个微不足道的所有权关系。shared_ptr
使其与weak_ptr
一起使用。我认为通过使用shared_ptr
作为成员对象的别名来解决这个问题。根据{{3}}:
此外,shared_ptr对象可以通过指针共享所有权 同时指向另一个对象。这种能力是 称为别名(参见构造函数),通常用于指向 成员对象拥有他们所属的对象。
我自己从未这样做过,但这似乎适应了你的情况:
InputConsumer
ModelController
成员
shared_ptr
InputSender
s weak_ptr
中引用它们
修改强>
这是一个完整的最小工作示例:
#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_ptr
到owner->foo1
(对于foo2
会是相同的)。为此,我们首先需要一个shared_ptr
的别名owner->foo1
。这只能通过以下方式完成:
shared_ptr<Foo> alias(other_shared_ptr, &(owner->foo1));
其中other_shared_ptr
是shared_ptr<T>
,其有效期至少与owner->foo1
的有效期一样长。要实现这一点,使用同时也是shared_ptr<T>
成员的owner
是一个好主意,因为它可以确保生命周期相同。最后,我们需要一个有效的非空指针来提供它,并且因为我们不想在堆上创建任何东西,所以我们必须使用现有的对象。 this
是一个很好的候选者,因为我们知道它是有效的,只有在其成员被销毁后才被销毁。因此我们的other_shared_ptr
就是:
shared_ptr<Owner> self(this);
但是,这意味着当self
超出范围时,即在owner
销毁期间,它会调用delete this
。我们不希望发生此删除,否则this
将被删除两次(这是未定义的行为,实际上是段错误)。因此,我们还向self
的构造函数提供了一个实际上不删除任何内容的Deleter。
其余的代码应该用注释自我解释。
答案 1 :(得分: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;
}