preguntas sobre compartido de este


Tengo una función que toma un shared_ptr<MyClass>. En alguna función miembro memfun de MyClass, necesito pasar this a esa función. Pero si escribo

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

Asumo que después de que la llamada haya terminado, el recuento de referencias alcanzará 0 y this se intentará destruir, lo cual es malo.

Entonces recordé que hay esta clase enable_shared_from_this con la función shared_from_this.

Así que ahora voy a usar lo siguiente: {[15]]}

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

Preguntas son:

1) ¿Es absolutamente imposible utilizar la funcionalidad sin derivar de enable_shared_from_this?
2) ¿Derivar de enable_shared_from_this significa que llamar a memfun en un objeto con duración de almacenamiento automático resultará en algo malo? Por ejemplo,

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

3) Si derivo de MyClass, ¿se heredará correctamente la funcionalidad enable_shared_from_this o necesito derivar de nuevo? Es decir,

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

¿Esto está bien? O correcto es el ¿siguiendo?

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

Muchas Gracias por adelantado.

Author: Armen Tsirunyan, 2011-03-08

5 answers

1) Depende de lo que quieres decir con "haz esto"en cuanto a si puedes o no. Siempre puede construir un shared_ptr a partir de un puntero raw como this, pero no compartirá el recuento de referencias con otra instancia shared_ptr que se construyó por separado a partir de un puntero raw. Por lo tanto, necesitará usar un deleter personalizado en una u otra instancia para evitar eliminaciones dobles, pero a menos que tenga mucho cuidado, puede terminar con instancias colgando shared_ptr debido a que el objeto se elimina a través de una, pero aún así accesible desde otro.

shared_from_this le permite garantizar que si tiene una instancia shared_ptr en su objeto, puede construir otra sin copiar la primera, y que estas instancias compartirán el recuento de referencias. Podría lograr esto almacenando un weak_ptr como miembro de la clase, y estableciendo ese valor cuando asigne por primera vez un shared_ptr a su objeto.

2) Llamar a shared_from_this() requiere que haya al menos una instancia shared_ptr ya apuntando a su objeto. Si lo usa en un objeto automático sin una instancia shared_ptr con un deleter personalizado, entonces obtendrá cosas malas sucediendo.

3) Si derivas de tu clase entonces la funcionalidad enable_shared_from_this te dará un shared_ptr a la clase base (la que derivó de enable_shared_from_this). Entonces podría usar static_pointer_cast o dynamic_pointer_cast para convertir el resultado de shared_from_this() a un puntero a la clase derivada.

 26
Author: Anthony Williams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-27 09:56:22

La pregunta importante aquí es por qué la función toma el argumento a través de un shared_ptr. ¿Almacena el puntero internamente para su uso posterior? ¿Solo lo utiliza durante la duración de la llamada? ¿Por qué se diluye la propiedad entre la persona que llama y el destinatario?

Algunas respuestas sugieren que proporcione un deleter no-op si va a pasar un objeto asignado a la pila a la función, pero si la función está almacenando el shared_ptr para su uso posterior, podría ser el caso de que en el momento en que se acerca a él, el objeto asignado localmente ya no está en la pila y se activa UB. Tener el deleter no-op shared_ptr permitirá la llamada, pero la semántica no será correcta.

Si la función no almacena el shared_ptr para su uso posterior, ¿cuál fue la decisión de diseño que llevó a esa API? Si puede cambiar la función (y no hay ninguna razón inminente), haga que reciba el argumento por referencia y tendrá una interfaz más amigable que no impone un shared_ptr para no motivo.

Si al final determina que puede garantizar que el objeto en la pila estará vivo durante toda la duración del proceso desencadenado por esa llamada a la función, entonces y solo entonces use el deleter no-op.

 11
Author: David Rodríguez - dribeas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-08 13:40:14

1) no, no es imposible hacer esto sin shared_from_this. Simplemente puede construir un shared_ptr con un deleter no-op:

void do_nothing(MyClass*) {}

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

Viendo que en realidad no parece necesitar shared_from_this después de todo, voy a omitir las siguientes dos partes de su pregunta.

 4
Author: John Zwinck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-08 13:11:58

Si tiene un objeto con almacenamiento automático y una función que requiere shared_ptr y sabe que la vida útil de su objeto será lo suficientemente larga para la duración de la función y que no almacena el shared_ptr en ningún lugar, entonces puede pasarlo con un deleter no-op.

Esto es útil para objetos estáticos. Si realmente tiene almacenamiento automático local, debe preguntarse por qué la función está tomando shared_ptr. ¿Los almacena?

Hay otro constructor menos conocido a shared_ptr para un objeto que es miembro de otro objeto contado por referencia. En realidad, puede crear un shared_ptr con el shared_ptr del objeto externo y el puntero del objeto interno.

 2
Author: CashCow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-08 13:14:06

Además de David Rodríguez-dribeas, el puntero compartido no es recomendado por google

Mantiene el recuento de referencia internamente, por lo que para que funcione correctamente, se utilizan InterlockedIncrement y InterlockedDecrement, estas dos funciones son realmente más lentas que normal ++ y --.

Debe comprobar que la propiedad de este objeto realmente necesita ser compartida con otros, según mi experiencia, el puntero compartido podría evitarse en la mayoría de los casos.

 -1
Author: Chang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-30 09:16:26