shared_ptr и указатель this - PullRequest
       41

shared_ptr и указатель this

8 голосов
/ 02 февраля 2011

ОК, я начал использовать общие-указатели и максимально передавать общие-указатели. Нет преобразования в сырые указатели больше. Это хорошо работает, за исключением этого конкретного случая:

Предположим, у нас есть класс, который также является наблюдателем для другого класса, например:

class MyClass : public IObserver
   {
   public:
      MyClass (std::shared_ptr<SomeOtherClass> otherClass);
      void DoSomethingImportant();
   private:
      std::shared_ptr<SomeOtherClass> m_otherClass;
   };

Этот класс используется в моем приложении следующим образом:

std::shared_ptr<MyClass> myInstance(new MyClass(otherInstance));
...
myInstance->DoSomethingImportant();

MyClass получает разделяемый указатель на другой класс и сохраняет его в своем элементе данных m_otherClass. В методе DoSomethingImportant экземпляр MyClass делает много важных вещей, включая регистрацию себя в качестве наблюдателя на m_otherClass, например:

m_otherClass-> registerObserver (это);

Проблема в том, что метод registerObserver определен так:

void registerObserver (наблюдатель std :: shared_ptr);

Ожидается общий указатель, но «this» является необработанным, а не общим указателем.

Я вижу три способа решения этой проблемы:

  • Найдите трюк для преобразования обычного указателя в общий указатель (см. Вопрос преобразование указателя в shared_ptr ), но ответы на этот вопрос предлагают только скопировать указатели общего доступа, а не о том, как на самом деле преобразовать указатель в общий указатель.
  • Передайте разделяемый указатель на себя методу, например: "myInstance-> DoSomethingImportant (myInstance);" что кажется немного глупым.
  • Поместите часть наблюдателя в отдельный класс. Это выглядит как излишнее убийство и может усложнить понимание класса.

Эта проблема делает очевидным, что совместно используемые указатели являются просто дополнением к C ++ (я не думаю, что у вас есть такая же проблема в других языках / средах, таких как C # (или .Net в целом) и Java).

Любые другие предложения или хитрости о том, как справиться с этой ситуацией?

Ответы [ 4 ]

8 голосов
/ 02 февраля 2011

Что вам нужно, это, вероятно, enable_shared_from_this и shared_from_this услуги. Документы здесь

Обратите внимание, что вы не можете использовать shared_from_this, пока конструктор не будет полностью завершен, а объект уже не принадлежит другому shared_ptr.

struct test : boost::enabled_shared_from_this<test>
{
    test() {
       // shared_from_this(this); // error, still not owned by another shared_ptr
    }
    boost::shared_ptr<test> shared() {
       return shared_from_this(this);
    }
};
int main() {
   test * t = new test;
   // boost::shared_ptr<test> p = t->shared(); // error, not yet owned by other shared_ptr
   boost::shared_ptr<test> owner( t ); 
   boost::shared_ptr<test> p = t->shared();     // [*] ok, "owner" owns the object
}

[*] Эта часть примера глупая, вы можете просто скопировать владельца в p вместо вызова метода. Он просто представлен, чтобы заметить, когда можно или не вызывать shared_from_this внутри методов test.

4 голосов
/ 02 февраля 2011

Для модели наблюдателя наблюдаемый объект не становится владельцем наблюдателя, почему бы просто не использовать необработанный указатель? Жизненный цикл наблюдателя должен контролироваться самим наблюдателем.

Используя enable_shared_from_this, вы вводите циклическую зависимость для наблюдателя и его наблюдаемого объекта. Это означает, что если не удалить явно, ресурс никогда не будет освобожден.

0 голосов
/ 02 февраля 2011


Можете ли вы переместить шаг регистрации в отдельный метод?:

shared_ptr<SomeOtherClass> other(new SomeOtherClass());
shared_ptr<MyClass> my(new MyClass());
// register myself to the observer  
other->registerObserver(my);
my->DoSomethingImportant();

Хороший дизайн шаблона наблюдателя может быть реализован с библиотеками boost :: signal и boost :: bind.Я призываю вас взглянуть.

С наилучшими пожеланиями,
Марчин

0 голосов
/ 02 февраля 2011

Как насчет того, чтобы сделать конструктор частным и иметь статический метод конструирования, подобный этому:

class MyClass : public IObserver
   {
   public:
      static std::shared_ptr<MyClass> createObserver(std::shared_ptr<SomeOtherClass> otherClass);
      void DoSomethingImportant();
   private:
      MyClass (std::shared_ptr<SomeOtherClass> otherClass);
      std::shared_ptr<SomeOtherClass> m_otherClass;
   };

Тогда вы можете чисто создать экземпляр наблюдателя в статическом методе и вообще не беспокоиться об указателе this.

...