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!
2 answers
A
unique_ptr
no funcionaría debido agetDevice()
, ¿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
.
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_ptr
s, y 1 para todos los punteros (shared_ptr
y weak_ptr
). Cuando se eliminan todos los shared_ptr
s, el puntero se elimina. Cuando se necesita puntero de weak_ptr
, lock
se debe utilizar para obtener el puntero, si existe.
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