Управление общим ресурсом с использованием std :: weak_ptr

Я задаюсь вопросом о том, как (с помощью C ++ 11 и, мы надеемся, с помощью обратно (совместимых или повышенных TR1) типов интеллектуальных указателей) достичь:

Один экземпляр класса (ModelController) владеет ресурсом (InputConsumer), в то время как другой компонент (InputSender, который в данном случае является синглтоном) имеет к нему доступ.

Модель InputSender содержит список ссылок на InputConsumersкоторых будет много.

ModelController может иметь ни одного, одного или многих InputConsumersи может быть много ModelControllers. InputSender НЕ в курсе.

Вот что было бы неплохо: способ для InputSender отслеживать InputConsumers назначенный ему таким образом, чтобы он мог выяснить для себя, является ли индивид InputConsumers действительны или нет.

Мне кажется, что weak_ptr идеально подходит для этой цели, так как их использование требует проверки этого условия.

Если InputSender перестает отслеживать любой из его weak_ptr refs, ничего плохого не происходит, соответствующий InputConsumers просто испытают радио молчание.

Если ModelController удален, или если ModelController удаляет некоторые из его InputConsumerс, любой InputSenderТе, кто зарегистрировался у них, в следующий раз при попытке получить к ним доступ обнаружат, что их больше нет, и что они могут очиститься без необходимости отправлять сообщение или что-либо делать.

Так что вопрос в том, подходит ли эта ситуация для использования? shared_ptr а также weak_ptr? Интересно, если shared_ptr вполне уместно, потому что InputConsumerс концептуально находящийся в собственности по их ModelControllers, поэтому они должны быть переменными-членами. Я не знаю, какой смысл это имеет для ModelController управлять ими только через shared_ptr, Я не могу сказать, если unique_ptr работает вместе с weak_ptr, Должен ли я просто управлять shared_ptrв ModelControllerэто ctor / dtor?

Может также существовать известный (не для меня!) Шаблон дизайна, поэтому если кто-то знает о такой вещи, пожалуйста, сообщите мне.

3

Решение

У меня нет большого опыта в общих указателях, но да, это кажется очень подходящим использованием weak_ptr,

В этом случае вы просто раздражены тем, что:

  1. Вы хотели бы использовать InputConsumerнепосредственно как члены ModelControllerс, так как это тривиальные отношения собственности.
  2. Вы вынуждены использовать shared_ptr чтобы заставить его работать с weak_ptr,

Я думаю, что это решается с помощью shared_ptr как псевдоним члена объекта. В соответствии с C ++. Ком:

Кроме того, объекты shared_ptr могут совместно использовать указатель
в то же время указывая на другой объект. Эта способность
известный как псевдонимы (см. конструкторы), и обычно используется для указания на
объекты-участники, в то время как владеют объектом, которому они принадлежат.

Я никогда не делал это сам, но это, кажется, адаптировано к вашей ситуации:

  • Есть InputConsumerс членами ModelControllers
  • Иметь псевдоним shared_ptr для каждого из них
  • Ссылка на них в InputSender с помощью 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_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

Другие решения

Еще один законченный рабочий фрагмент, показывающий динамику 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;
}
0

По вопросам рекламы [email protected]