2012-03-26 5 views
4

Вопрос о boost::shared_ptr здесь:Соответствующее использование для boost :: shared_ptr?

У меня 3 класса.

A - это некий Основной класс, который несет ответственность за все.

B - это класс, который имеет функции для выполнения определенной работы.

Dispatcher - это класс, который обтекает отдельную нить, которая получает работу от Instaces of B, выполненных в этой теме.

Так что это выглядит так: A имеет экземпляр Dispatcher. Теперь на случай A генерирует экземпляр B и передает его диспетчеру.

Важной частью является то, что B необходимо позвонить по телефону A::callback(), когда все будет готово. Поэтому B получает ссылку на А в его конструктор (см код ниже)

A.hpp

class A : public boost::enable_shared_from_this<A> 
{ 
public: 
    A(); 
    void sendB(); 
    void callback(); 
private: 
    Dispatcher m_Dispatcher; 
}; 

B.hpp

class B 
{ 
public: 
    B(boost::shared_ptr<A> ptr); 
    boost::shared_ptr<A> m_PointerToA; 
/* Some other functions */ 
}; 

Dispatcher.hpp

class Dispatcher 
{ 
public: 
    void run(); 
    void dispatch(boost::shared_ptr<B> b); 
private: 
    void doWork(); 
    boost::thread m_Thread; 
}; 

A.cpp

A::A() 
{ 
    m_Dispatcher.run(); 
} 
void A::sendB() 
{ 
    boost::shared_ptr ptr_B; 
    ptr_B.reset(new B(this->shared_from_this); 
    m_Dispatcher.dispatch(ptr_B); 
} 

B.cpp

B::B(boost::shared_ptr<A> ptr) : 
    : m_PointerToA(ptr) 
{ 
} 

main_example.cpp

int main() 
{ 
    A instanceA; 
    while(true) 
    { 
      instanceA.sendB(); 
      /* Do some other stuff */ 
    } 
    return 0; 
} 

Так что мой вопрос:

Разумно использовать повышение :: shared_ptr для этой цели?

Я не уверен, правильно ли стоит shared_ptr. Моя проблема в том, что я не знаю, что происходит, когда я вызываю конструктор из B и передаю ему указатель this. Теперь, согласно shared_ptr, я бы предположил, что m_PointerToA занимает вид владения A. Но это будет означать, что когда работа в Dispatcher будет выполнена, и мой экземпляр B будет удален, он также удалит ссылку на m_PointerToA, которая на самом деле означает, что она убивает сам объект, несмотря на то, что в главном случае есть фактический экземпляр A петля.

Update:

Добавлен некоторый код и обновляется сам вопрос, чтобы сделать его более ясным.

+0

Вы не хотите использовать 'shared_ptr' через границы потоков. Это одно место, где действительно синтаксическая семантика 'std :: auto_ptr'; как только вы передадите указатель на другой поток, вы не сможете получить к нему доступ в исходном потоке. –

+0

'boost :: shared_ptr' фактически потокобезопасен, в том смысле, что вы можете полагаться на подсчет ссылок, чтобы быть в безопасном состоянии. – Ylisar

+0

@JamesKanze Но указатель, который я передаю, на самом деле является указателем 'this'. Так что фактически я ничего не делаю с «указателем» в частности. Но, конечно, некоторый код в экземпляре из 'this' по-прежнему работает в основном потоке. На самом деле я просто хотел использовать 'shared_ptr', поэтому у меня не возникало никаких проблем, теряя ссылки. У меня также не было бы проблем с использованием необработанного указателя ... – Toby

ответ

1

Да, это просто копировать/назначать shared_ptr, это только увеличит количество ссылок.

В вашем примере, shared_from_this() создаст (здесь: временный) shared_ptr из weak_ptr, который проведет по this (исх графа 1), поэтому при назначении/копирования построить m_PointerToA, счетчик ссылок будет временно увеличить до 2 перед возвратом ctor, и временный объект будет уничтожен, снова уменьшив счетчик ссылок до 1 (shared_ptr «знает» одного экземпляра вашего объекта B).

Итак, да, если B удален, в этом случае он уничтожит A (когда счетчик ссылок будет сброшен до 0).

Ваше беспокойство

Это означало бы, если мой Инстанции B удаляется, он будет также удалить m_PointerToA, который также будет убить моего экземпляра. Конечно, мой оригинальный экземпляр A находится в другом месте.

только показывает, что если вы планируете/необходимость/намерены сохранить указатель на экземпляр A для дальнейшего использования, вы должны делать это с shared_ptr, а вместо исходного указателя. Если у вас есть контроль над интерфейсом A «s, самый простой способ будет named constructor так:

class A : public boost::enable_shared_from_this<A> { 
    public: 
     static boost::shared_ptr<A> create(); 

     void initClassB(); 
     // .... 
    private: 
     A(); 
     A(const A & other); 
     A& operator=(const A & rhs); 

}; 

boost::shared_ptr<A> A::create() { 
    return boost::shared_ptr<A>(new A()); 
} 

Тогда, даже если ваш экземпляр B удаляется, экземпляр A будет еще выжить, потому что счетчик ссылок shared_ptr по-прежнему (по крайней мере) 1.

+0

Хорошо, я понимаю, почему ваше решение действительно удостоверится, что A не будет удалено, потому что счетчик ссылок будет, по крайней мере, равен 1. Но тогда я хотел бы знать: кто-то (например, основная функция или другой класс) должен вызвать 'A :: create()', поэтому, если вместо использования вашего решения эта основная функция просто создаст нормальный экземпляр AI, он может потерять его, а 'B' будет удален? Таким образом, A также будет удален, несмотря на то, что существует реальный экземпляр этого объекта? – Toby

+0

Не уверен, что вы подразумеваете под «несмотря на то, что существует реальный экземпляр этого объекта». 'Shared_ptr' уничтожит экземпляр, если ссылка опустится до 0. Если в' B' есть только один 'shared_ptr',' A', да, уничтожение B уничтожит этот экземпляр 'A'. Если вы ссылаетесь на A где-то с помощью необработанного указателя, нет способа предотвратить это. Но именно поэтому вы должны использовать интеллектуальные указатели (не использовать необработанные указатели для объектов, управляемых интеллектуальными указателями). –

+0

В приведенном выше примере основная функция не сможет создать «нормальный» экземпляр (необработанный указатель) «A», потому что нет доступного/доступного конструктора (ваш компилятор сообщит об ошибке при попытке). Единственный способ построить экземпляр 'A' - это вызвать' A :: create() ' –

4

Нет ничего особенного в этом дизайне.Однако я предпочел бы вместо этого использовать boost::function<> & boost::bind. Это дает вам лучшую гибкость для обратного вызова и не связывает B с A до A. Конечно, вы по-прежнему должны быть разными обычными оговорками.

+0

+1. Хороший совет. Кроме того, если у вас есть идеальный контроль над временем жизни ваших экземпляров, вы можете просто сохранить * ссылку * на свой 'A'. Если вам, конечно, не нужно назначать «B». – ereOn

+0

Да, я также думал о том, чтобы сделать это немного более общим, но в моей первой попытке я просто хотел, чтобы он работал. Таким образом, нет реальной проблемы с использованием boost :: shared_ptr? – Toby

Смежные вопросы