вопросы относительно shared_from_this - PullRequest
24 голосов
/ 08 марта 2011

У меня есть функция, которая принимает shared_ptr<MyClass>.В некоторых функциях-членах memfun из MyClass мне нужно передать this этой функции.Но если я напишу

void MyClass:memfun()
{
   func(shared_ptr<MyClass>(this))
}

, я предполагаю, что после завершения вызова счетчик ссылок достигнет 0 и попытка уничтожения this будет плохой, что плохо.

Тогда я вспомнил, что там этот класс enable_shared_from_this с функцией shared_from_this.

Так что теперь я собираюсь использовать следующее:

class MyClass: public enable_shared_from_this<MyClass>
{
    void MyClass:memfun()
    {
       func(shared_from_this());
    }
};

Вопросы:

1) Абсолютно невозможно использовать функциональность, не производную от enable_shared_from_this?
2) Означает ли получение из enable_shared_from_this, что вызов memfun для объекта с автоматическим хранением приведет к чему-то плохому?Например,

 int main()
 { 
    MyClass m;   //is this OK?
    m.memfun();  // what about this?
 }

3) Если я наследую от MyClass, будет ли функциональность enable_shared_from_this правильно унаследована или мне нужно будет произвести его заново?То есть

class MyCoolClass: public Myclass
{
   void someCoolMember
   {
      someCoolFuncTakingSharedPtrToMyCoolClass(shared_from_this());
   }
}

Это нормально?Или правильно следующее?

 class MyCoolClass: public Myclass, public enable_shared_from_this<MyCoolClass>
    {
       void someCoolMember
       {
          someCoolFuncTakingSharedPtrToMyCoolClass(enable_shared_from_this<MyCoolClass>::shared_from_this());
       }
    }   

Заранее большое спасибо.

Ответы [ 5 ]

26 голосов
/ 27 июня 2011

1) Это зависит от того, что вы подразумеваете под «сделать это», относительно того, можете ли вы это сделать. Вы всегда можете создать shared_ptr из необработанного указателя, такого как this, но он не разделит счетчик ссылок с другим экземпляром shared_ptr, который был отдельно создан из необработанного указателя. Таким образом, вам нужно будет использовать пользовательское средство удаления для одного или другого экземпляра, чтобы избежать двойного удаления, но если вы не позаботитесь об этом, вы можете получить висячие экземпляры shared_ptr из-за того, что объект удален через один, но все еще доступен из другого .

shared_from_this позволяет вам гарантировать, что если у вас есть один экземпляр shared_ptr для вашего объекта, то вы можете создать другой, не копируя первый, и что эти экземпляры будут использовать общее количество ссылок. Этого можно достичь, сохранив weak_ptr в качестве члена класса и установив это значение при первом выделении shared_ptr для вашего объекта.

2) Для вызова shared_from_this() требуется, чтобы хотя бы один экземпляр shared_ptr уже указывал на ваш объект. Если вы используете его на автоматическом объекте без экземпляра shared_ptr с пользовательским средством удаления, то вы получите плохие вещи.

3) Если вы унаследованы от вашего класса, то функциональность enable_shared_from_this даст вам shared_ptr для базового класса (тот, который получен из enable_shared_from_this). Затем вы можете использовать static_pointer_cast или dynamic_pointer_cast для приведения результата shared_from_this() к указателю на производный класс.

11 голосов
/ 08 марта 2011

Важный вопрос здесь заключается в том, почему функция принимает аргумент через shared_ptr.Хранит ли он указатель внутри для последующего использования?Используется ли он только на время разговора?Почему право владения между вызывающим и вызываемым объектом разводится?

В некоторых ответах предлагается, чтобы вы предоставили средство удаления без операции, если собираетесь передать объект, выделенный в стеке, в функцию, но если функция на самом деле хранитshared_ptr для последующего использования, может случиться так, что к тому времени, когда он доберется до него, локально выделенный объект больше не будет в стеке, и вы активируете UB.Наличие безоперационного удалителя shared_ptr разрешит вызов, но семантика не будет правильной.

Если функция не хранит shared_ptr для дальнейшего использования, каково было дизайнерское решение, которое привело кэтот API?Если вы можете изменить функцию (и нет надвигающейся причины), сделайте так, чтобы она получала аргумент по ссылке, и у вас будет более дружественный интерфейс, который не навязывает shared_ptr без причины.

Если наВ конце вы определяете, что можете гарантировать, что объект в стеке будет живым в течение всей продолжительности процесса, вызванного этим вызовом функции, тогда и только тогда используйте средство удаления без операции.

4 голосов
/ 08 марта 2011

1) Нет, без shared_from_this это невозможно. Вы можете просто создать shared_ptr с удалением без операции:

void do_nothing(MyClass*) {}

void MyClass:memfun()
{
    func(shared_ptr<MyClass>(this, do_nothing));
}

Видя, что на самом деле вам, похоже, не нужно shared_from_this, я пропущу следующие две части вашего вопроса.

2 голосов
/ 08 марта 2011

Если у вас есть объект с автоматическим хранением и функция, которая требует shared_ptr, и вы знаете, что время жизни вашего объекта будет достаточно продолжительным для продолжительности функции и что он нигде не хранит shared_ptr, тогда вы можете передать это с безоперационным удалителем.

Это полезно для статических объектов. Если он действительно имеет локальное автоматическое хранилище, вам нужно спросить себя, почему функция принимает shared_ptr. Хранит ли он их?

Существует еще один менее известный конструктор для shared_ptr для объекта, который является членом другого объекта с подсчетом ссылок. На самом деле вы можете создать shared_ptr с shared_ptr из внешнего объекта и указателем из внутреннего объекта.

0 голосов
/ 30 июня 2011

В дополнение к Давиду Родригесу - dribeas, общий указатель не рекомендуется google

Он поддерживает внутренний счетчик ссылок, поэтому для правильной работы используются InterlockedIncrement и InterlockedDecrement, этидве функции действительно работают медленнее, чем обычно ++ и -.

Вы должны проверить, действительно ли нужно владеть этим объектом, если нужно, чтобы другим было предоставлено совместное использование, согласно моему опыту, в большинстве случаев можно избежать общего указателя.

...