Los punteros inteligentes en C++ permiten retrasar la creación de objetos, evitando la inicialización obligatoria en el constructor. Esto mejora la seguridad al gestionar recursos dinámicos de forma automática, reduciendo riesgos como fugas de memoria o eliminaciones dobles.
Obteniendo referencias compartidas con shared_from_this()
En C++11, la función shared_from_this(), disponible al heredar de std::enable_shared_from_this, devuelve un std::shared_ptr que apunta a la instancia actual. Esto es esencial cuando un objeto necesita compartir su propia referencia de manera controlada, por ejemplo, al pasarlo como argumento a funciones que esperan punteros inteligentes.
Un ejemplo adaptado podría verse así:
#include <memory>
class MiClase : public std::enable_shared_from_this<MiClase>
{
public:
MiClase() {}
~MiClase() {}
std::shared_ptr<MiClase> obtenerPunteroSeguro()
{
return shared_from_this();
}
};
Usar punteros crudos (como this) en estos escenarios es problemático: el receptor podría eliminar el objeto prematuramente, dejando shared_ptr inválidos. Crear un nuevo shared_ptr desde this directamente también falla, ya que genera múltiples contadores de referencia independientes, causando eliminaciones duplicadas.
Inicialización eficiente con make_shared()
La función make_shared() asigna e inicializa un objeto en memoria dinámica, devolviendo un std::shared_ptr que lo gestiona. Esto ofrece una alternativa segura y optimizada al uso manual de new.
Ejemplo de uso con diferentes tipos:
auto cadena1 = std::make_shared<std::string>(8, 'a');
auto cadena2 = std::make_shared<std::string>("ejemplo");
auto cadena3 = std::make_shared<std::string>();
Consideraciones importantes:
make_shared()es una función plantilla que deduce el tipo a partir de los argumentos o mediante especificación explícita.- Los parámetros se transmiten directamente al constructor del objeto, permitiendo flexibilidad en la inicialización.
- Suele ser más eficiente, ya que reduce el número de asignaciones de memoria separadas.
Detalles de implementación
Internamente, make_shared() delega en allocate_shared(), que utiliza un asignador para la gestión de memoria. Una representación simplificada del mecanismo podría ser:
template<typename Tipo, typename... Args>
std::shared_ptr<Tipo> crear_compartido(Args&&... argumentos)
{
return std::allocate_shared<Tipo>(std::allocator<Tipo>(), std::forward<Args>(argumentos)...);
}
Este diseño garantiza una asignación de memoria segura y eficiente, integrándose con las características de gestión de recursos del lenguaje.