Я задаюсь вопросом о том, как (с помощью C ++ 11 и, мы надеемся, с помощью обратно (совместимых или повышенных TR1) типов интеллектуальных указателей) достичь:
Один экземпляр класса (ModelController
) владеет ресурсом (InputConsumer
), в то время как другой компонент (InputSender
, который в данном случае является синглтоном) имеет к нему доступ.
Модель InputSender
содержит список ссылок на InputConsumers
которых будет много.
ModelController
может иметь ни одного, одного или многих InputConsumers
и может быть много ModelController
s. InputSender
НЕ в курсе.
Вот что было бы неплохо: способ для InputSender
отслеживать InputConsumers
назначенный ему таким образом, чтобы он мог выяснить для себя, является ли индивид InputConsumers
действительны или нет.
Мне кажется, что weak_ptr
идеально подходит для этой цели, так как их использование требует проверки этого условия.
Если InputSender
перестает отслеживать любой из его weak_ptr
refs, ничего плохого не происходит, соответствующий InputConsumer
s просто испытают радио молчание.
Если ModelController
удален, или если ModelController
удаляет некоторые из его InputConsumer
с, любой InputSender
Те, кто зарегистрировался у них, в следующий раз при попытке получить к ним доступ обнаружат, что их больше нет, и что они могут очиститься без необходимости отправлять сообщение или что-либо делать.
Так что вопрос в том, подходит ли эта ситуация для использования? shared_ptr
а также weak_ptr
? Интересно, если shared_ptr
вполне уместно, потому что InputConsumer
с концептуально находящийся в собственности по их ModelController
s, поэтому они должны быть переменными-членами. Я не знаю, какой смысл это имеет для ModelController
управлять ими только через shared_ptr
, Я не могу сказать, если unique_ptr
работает вместе с weak_ptr
, Должен ли я просто управлять shared_ptr
в ModelController
это ctor / dtor?
Может также существовать известный (не для меня!) Шаблон дизайна, поэтому если кто-то знает о такой вещи, пожалуйста, сообщите мне.
У меня нет большого опыта в общих указателях, но да, это кажется очень подходящим использованием weak_ptr
,
В этом случае вы просто раздражены тем, что:
InputConsumer
непосредственно как члены ModelController
с, так как это тривиальные отношения собственности.shared_ptr
чтобы заставить его работать с weak_ptr
,Я думаю, что это решается с помощью shared_ptr
как псевдоним члена объекта. В соответствии с C ++. Ком:
Кроме того, объекты shared_ptr могут совместно использовать указатель
в то же время указывая на другой объект. Эта способность
известный как псевдонимы (см. конструкторы), и обычно используется для указания на
объекты-участники, в то время как владеют объектом, которому они принадлежат.
Я никогда не делал это сам, но это, кажется, адаптировано к вашей ситуации:
InputConsumer
с членами ModelController
sshared_ptr
для каждого из нихInputSender
с помощью weak_ptr
sРЕДАКТИРОВАТЬ
Вот полный минимальный рабочий пример:
#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, который на самом деле ничего не удаляет.
Остальная часть кода должна быть понятна с комментариями.
Еще один законченный рабочий фрагмент, показывающий динамику 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;
}