我想知道如何(使用 C++11 并希望使用向后(升压或 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
一起工作.我应该只管理ModelController
的 ctor/dtor 中的shared_ptr
吗?
也可能有一个众所周知的(不是我!(设计模式,所以如果有人知道这样的事情,请告诉我。
我在共享指针方面没有很多专业知识,但是是的,这似乎是对weak_ptr
的非常合适的使用。
在这种情况下,您只是对以下情况感到恼火:
- 您希望直接使用
InputConsumer
作为ModelController
s 的成员,因为这是一个微不足道的所有权关系。 - 您被迫使用
shared_ptr
使其与weak_ptr
一起使用。
我认为这可以通过使用shared_ptr
作为成员对象的别名来解决。根据C++.com:
此外,shared_ptr对象可以共享指针的所有权 同时指向另一个对象。这种能力是 称为别名(请参阅构造函数(,通常用于指向 成员对象,同时拥有它们所属的对象。
我自己从来没有做过,但这似乎适合你的情况:
- 拥有
InputConsumer
ModelController
的成员 - 为它们中的每一个都有一个别名
shared_ptr
- 使用
weak_ptr
在InputSender
中引用它们
编辑
这是一个完整的最小工作示例:
#include <iostream>
#include <memory>
using namespace std;
// A class to try our smart pointers on
struct Foo
{
Foo() { cout << "constructing Foon"; }
~Foo() { cout << "destructing Foon"; }
};
// 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 Ownern"; }
};
Owner() : self(this, Deleter()) { cout << "constructing Ownern"; }
~Owner() { cout << "destructing Ownern"; }
};
// 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 Observern";
}
~Observer() { cout << "destructing Observern"; }
void check()
{
if(foo_ptr.expired())
cout << "foo expiredn";
else
cout << "foo still existsn";
}
};
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
棘手的部分是能够创建一个要owner->foo1
的weak_ptr
(对于foo2
也是如此(。为此,我们首先需要一个别名shared_ptr
owner->foo1
.这只能通过以下方式完成:
shared_ptr<Foo> alias(other_shared_ptr, &(owner->foo1));
其中other_shared_ptr
是一个shared_ptr<T>
,其寿命至少与owner->foo1
的寿命一样长。为此,使用也是owner
成员的shared_ptr<T>
是一个好主意,因为它可以确保生命周期相同。最后,我们需要一个有效的非空指针来提供给它,并且由于我们不想在堆上创建任何内容,因此我们必须使用现有对象。 this
是一个很好的候选者,因为我们知道它是有效的,只有在其成员被摧毁后才会被销毁。因此,我们的other_shared_ptr
例如:
shared_ptr<Owner> self(this);
但是,这意味着当self
超出范围时,即在销毁owner
期间,它将调用delete this
。我们不希望发生这种删除,否则this
将被删除两次(这是未定义的行为,实际上是段错误(。因此,我们还向self
的构造函数提供了一个实际上不会删除任何内容的删除器。
代码的其余部分应该与注释不言自明。
只是一个更完整的工作片段,显示 std::weak_ptr 动态,也许可以提供更多帮助。(我特别喜欢这个过期的((语义(;
#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;
}