Punteros inteligentes pueden ser utilizados de manera implícita como punteros?

votos
0

Se punteros inteligentes considerados como punteros? Y por lo tanto puede que se utiliza implícitamente como punteros?

Digamos que tengo la siguiente clase:

class MyClass {
    //...
    std::shared_ptr<AnotherClass> foo() { /*whatever*/ };
    void bar(AnotherClass* a) { /*whatever too*/ };
    //...
}

Entonces puedo usar MyClassla siguiente manera?

// m is an instance of MyClass
m.bar(m.foo());
Publicado el 09/10/2019 a las 19:00
fuente por usuario
En otros idiomas...                            


3 respuestas

votos
3

No, no se pueden utilizar intercambiables. Que se obtendría un error de compilación en su ejemplo. Pero siempre se puede obtener el puntero prima por shared_ptr::get().

Respondida el 09/10/2019 a las 19:02
fuente por usuario

votos
2

¡NO! Sería un tremendo API . Sí, usted puede fácilmente poner en práctica dentro shared_ptr, pero sólo porque usted podría no significa que usted debe.

¿Por qué es tan mala idea? La interfaz basada en la llanura-puntero de barno retiene una instancia del puntero compartido. Si barsucede para almacenar el puntero prima en algún lugar y luego la salida, no hay nada que garantice que el puntero se había almacenado no llegará a ser colgado en el futuro. La única manera de garantizar que sería mantener una instancia del puntero compartida, no el puntero prima (que es el punto central de shared_ptr!).

Pero hay algo peor: el siguiente código es un comportamiento indefinido si foo()devuelve una instancia de puntero que sólo tenía una referencia a la hora foo()de volver (por ejemplo, si fooes una simple fábrica de nuevos objetos):

AnotherClass *ptr = m.foo().get();
// The shared_ptr instance returned by foo() is destroyed at this point
m.bar(ptr); // undefined behavior: ptr is likely a dangling pointer here

Estas son las opciones; considerar los enumerados anteriormente antes de recurrir a sus sucesores.

  • Si bar(AnotherClass *)es una API externa, entonces usted necesita para envolverlo en una forma segura, es decir, el código que habría llamado Original::bardebe llamar MyWrapped::bar, y la envoltura debe hacer lo que sea gestión de vida es necesario. Supongamos que existe startUsing(AnotherClass *)y finishUsing(AnotherClass *), y el código espera que el puntero siga siendo válida entre startUsingy finishUsing. Su envoltura sería:

    class WithUsing {
      std::unique_ptr<AnotherClass> owner; /* or shared_ptr if the ownership is shared */
      std::shared_ptr<User> user;
    public:
      WithUsing(std::unique_ptr<AnotherClass> owner, std::Shared_ptr<User> user) :
        owner(std::move(owner)), user(std::move(user)) {
        user.startUsing(owner.get());
      }
      void bar() const {
        user.bar(owner.get());
      }
      ~WithUsing() {
        user.finishUsing(owner.get());
      }
    };
    

    A continuación, utilizar WithUsingcomo un identificador para el Userobjeto, y cualquier uso que se haría a través de ese mango, asegurando la existencia del objeto.

  • Si AnotherClasses copiable y es muy barato para copiar (por ejemplo, se compone de un puntero o dos), luego pasarlo por valor:

    void bar(AnotherClass)
    
  • Si la implementación de barno necesita cambiar el valor, que puede ser definido para tomar un const-valor (la declaración puede ser sin la constque no importa allí):

    void bar(const AnotherClass a) { ... }
    
  • Si barno almacena un puntero, entonces no pase un puntero: pasar una referencia const por defecto, o una referencia no const si es necesario.

    void bar(const AnotherClass &a);
    void bar_modifies(AnotherClass &a);
    
  • Si tiene sentido para invocar bar"sin objeto" (también conocido como "nulo"), entonces:

    1. Si pasa AnotherClasspor el valor es correcto, a continuación, utilizar std::optional:

      void bar(std::optional<AnotherClass> a);
      
    2. De lo contrario, si se AnotherClasstoma la propiedad, que pasa unique_ptrfunciona bien, ya que puede ser nulo.

    3. De lo contrario, pasa shared_ptrfunciona bien, ya que puede ser nulo.

  • Si foo()crea un nuevo objeto (frente a devolver un objeto que ya existe), se debe regresar unique_ptrtodos modos, no una shared_ptr. Funciones de fábrica deben regresando punteros únicos: eso es idiomática C ++. Hacer lo contrario es confuso, ya que el retorno de una shared_ptr está destinado a expresar la propiedad compartida existente .

    std::unique_ptr<AnotherClass> foo();
    
  • Si bardebe tomar posesión del valor, entonces debería ser aceptar un puntero único - que es el lenguaje de "Me haré cargo de la gestión de la vida útil de ese objeto":

    void bar(std::unique_ptr<const AnotherClass> a);
    void bar_modifies(std::unique_ptr<AnotherClass> a);
    
  • Si bardebe conservar la propiedad compartida, entonces debería estar tomando shared_ptr, y se le convierte inmediatamente al unique_ptrregresar de foo()a una responsabilidad compartida:

    struct MyClass {
      std::unique_ptr<AnotherClass> foo();
      void bar(std::shared_ptr<const AnotherClass> a);
      void bar_modifies(std::shared_ptr<AnotherClass> a);
    };
    
    void test() {
      MyClass m;
      std::shared_ptr<AnotherClass> p{foo()};
      m.bar(p);
    }
    

shared_ptr(const Type)y shared_ptr(Type)compartirán la propiedad, que proporcionan una vista constante y una vista modificable del objeto, respectivamente. shared_ptr<Foo>También se puede convertir en shared_ptr<const Foo>(pero no a la inversa, que tendría que utilizar const_pointer_castpara ello (con precaución). Siempre debe por defecto para acceder a objetos como constantes, y sólo se trabaja con tipos que no son constantes cuando hay una necesidad explícita de la misma.

Si un método no modifica algo, que sea auto-documento de ese hecho al tener que aceptar una referencia / puntero al const somethinglugar.

Respondida el 09/10/2019 a las 19:10
fuente por usuario

votos
2

Punteros inteligentes se utilizan para asegurarse de que se elimina un objeto si ya no se usa (referenciados).

puntero inteligente están ahí para gestionar toda la vida del puntero que poseen / acción.

Se puede pensar en una envoltura que tiene un puntero dentro. Por tanto, la respuesta es no. Sin embargo se puede acceder al puntero de su propiedad a través de get()método.

Tenga en cuenta que no es tan difícil hacer referencia colgante si se utiliza getel método, por lo que si lo usa tenga un cuidado especial.

Respondida el 09/10/2019 a las 19:08
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more