Uso de punteros inteligentes para los miembros de la clase


Estoy teniendo problemas para entender el uso de punteros inteligentes como miembros de la clase en C++11. He leído mucho sobre punteros inteligentes y creo que entiendo cómo unique_ptr y shared_ptr/weak_ptr trabajo en general. Lo que no entiendo es el uso real. Parece que todo el mundo recomienda usar unique_ptr como el camino a seguir casi todo el tiempo. Pero cómo implementaría algo como esto:

class Device {
};

class Settings {
    Device *device;
public:
    Settings(Device *device) {
        this->device = device;
    }

    Device *getDevice() {
        return device;
    }
};    

int main() {
    Device *device = new Device();
    Settings settings(device);
    // ...
    Device *myDevice = settings.getDevice();
    // do something with myDevice...
}

Digamos que me gustaría reemplazar los punteros con punteros inteligentes. A unique_ptr no funcionaría debido a getDevice(), ¿verdad? ¿Entonces ese es el momento en que uso shared_ptr y weak_ptr? No hay forma de usar unique_ptr? Me parece que para la mayoría de los casos shared_ptr tiene más sentido a menos que esté usando un puntero en un alcance realmente pequeño?

class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(std::shared_ptr<Device> device) {
        this->device = device;
    }

    std::weak_ptr<Device> getDevice() {
        return device;
    }
};

int main() {
    std::shared_ptr<Device> device(new Device());
    Settings settings(device);
    // ...
    std::weak_ptr<Device> myDevice = settings.getDevice();
    // do something with myDevice...
}

¿Es ese el camino a seguir? Muchas gracias!

Author: michaelk, 2013-03-27

2 answers

A unique_ptr no funcionaría debido a getDevice(), ¿verdad?

No, no necesariamente. Lo que es importante aquí es determinar la política de propiedad apropiada para su objeto Device, es decir, quién será el propietario del objeto señalado por su puntero (inteligente).

¿Va a ser la instancia del objeto Settings solo? ¿El objeto Device tiene que ser destruido automáticamente cuando el objeto Settings se destruye, o si sobrevive ese objeto?

En el primer caso, std::unique_ptr es lo que necesitas, ya que hace a Settings el único propietario (único) del objeto puntiagudo, y el único objeto que es responsable de su destrucción.

Bajo esta suposición, getDevice() debe devolver un puntero simple observing (los punteros observing son punteros que no mantienen vivo el objeto apuntado). El tipo más simple de puntero de observación es un puntero raw:

#include <memory>

class Device {
};

class Settings {
    std::unique_ptr<Device> device;
public:
    Settings(std::unique_ptr<Device> d) {
        device = std::move(d);
    }

    Device* getDevice() {
        return device.get();
    }
};

int main() {
    std::unique_ptr<Device> device(new Device());
    Settings settings(std::move(device));
    // ...
    Device *myDevice = settings.getDevice();
    // do something with myDevice...
}

[NOTA 1: Puede que te estés preguntando por qué estoy usando punteros crudos aquí, cuando todo el mundo sigue diciendo que los punteros crudos son malos, inseguros y peligrosos. En realidad, esa es una advertencia preciosa, pero es importante ponerlo en el contexto correcto: los punteros raw son malos cuando se usan para realizar la gestión manual de memoria, es decir, asignar y desasignar objetos a través de new y delete. Cuando se utiliza puramente como un medio para lograr la semántica de referencia y pasar alrededor de punteros no propietarios, observando, no hay nada intrínsecamente peligroso en punteros crudos, excepto tal vez por el hecho de que uno debe tener cuidado de no desreferenciar un puntero colgante. - NOTA FINAL 1]

[NOTA 2: Como surgió en los comentarios, en este caso particular donde la propiedad es única y el objeto poseído siempre está garantizado para estar presente (es decir, el miembro de datos interno device nunca va a ser nullptr), function getDevice() podría (y tal vez debería) devolver una referencia en lugar de un puntero. Mientras esto es cierto, decidí devolver un puntero raw aquí porque quería decir que esta era una respuesta corta que se podría generalizar para el caso en el que device podría ser nullptr, y para mostrar que los punteros raw están bien siempre y cuando uno no los use para la gestión manual de la memoria. - NOTA FINAL 2]


La situación es radicalmente diferente, por supuesto, si su objeto Settings debería no tener la propiedad exclusiva del dispositivo. Este podría ser el caso, por ejemplo, si el la destrucción del objeto Settings no debe implicar la destrucción del objeto apuntado Device también.

Esto es algo que solo usted como diseñador de su programa puede decir; por el ejemplo que proporciona, es difícil para mí decir si este es el caso o no.

Para ayudarlo a averiguarlo, puede preguntarse si hay otros objetos aparte de Settings que tienen derecho a mantener vivo el objeto Device siempre y cuando sostengan un puntero a él, en lugar de ser sólo observadores pasivos. Si ese es el caso, entonces necesita una política de propiedad compartida , que es lo que ofrece std::shared_ptr:

#include <memory>

class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(std::shared_ptr<Device> const& d) {
        device = d;
    }

    std::shared_ptr<Device> getDevice() {
        return device;
    }
};

int main() {
    std::shared_ptr<Device> device = std::make_shared<Device>();
    Settings settings(device);
    // ...
    std::shared_ptr<Device> myDevice = settings.getDevice();
    // do something with myDevice...
}

Observe que weak_ptr es un puntero observing, no un puntero propietario - en otras palabras, no mantiene vivo el objeto apuntado si todos los demás punteros propietarios al objeto apuntado salen del alcance.

La ventaja de weak_ptr sobre un puntero raw regular es que puede saber con seguridad si weak_ptr está colgando o no (es decir, si está apuntando a un objeto válido, o si el objeto al que se apuntó originalmente ha sido destruido). Esto se puede hacer llamando a la función miembro expired() en el objeto weak_ptr.

 166
Author: Andy Prowl,
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
2013-04-07 10:25:59
class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(const std::shared_ptr<Device>& device) : device(device) {

    }

    const std::shared_ptr<Device>& getDevice() {
        return device;
    }
};

int main()
{
    std::shared_ptr<Device> device(new Device());
    Settings settings(device);
    // ...
    std::shared_ptr<Device> myDevice(settings.getDevice());
    // do something with myDevice...
    return 0;
}

week_ptr se utiliza solo para bucles de referencia. El gráfico de dependencias debe ser gráfico acycldirected. En los punteros compartidos hay 2 recuentos de referencia: 1 para shared_ptrs, y 1 para todos los punteros (shared_ptr y weak_ptr). Cuando se eliminan todos los shared_ptrs, el puntero se elimina. Cuando se necesita puntero de weak_ptr, lock se debe utilizar para obtener el puntero, si existe.

 0
Author: Naszta,
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
2013-03-26 23:06:04