Подтвердить что ты не робот

Вопросы относительно shared_from_this

У меня есть функция, которая принимает 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());
       }
    }   

Спасибо очень заблаговременно.

4b9b3361

Ответ 1

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() указателю на производный класс.

Ответ 2

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

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

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

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

Ответ 3

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

void do_nothing(MyClass*) {}

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

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

Ответ 4

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

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

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

Ответ 5

В дополнение к David Rodríguez - dribeas, общий указатель не рекомендуется google

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

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