Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En la programación del lenguaje C++ actual, la biblioteca estándar incluye punteros inteligentes, que se utilizan para asegurarse de que los programas están libres de memoria y de pérdidas de recursos y son seguros ante excepciones.
Usos de los punteros inteligentes
Los punteros inteligentes se definen en el espacio de nombres std del archivo de encabezado <memory>. Son cruciales para el modismo de programación Resource Acquisition Is Initialization(RAII). El objetivo principal de esta expresión es asegurarse de que la adquisición de recursos se produce al mismo tiempo que se inicializa el objeto. Todos los recursos del objeto se crean y se preparan en una línea de código.
En términos prácticos, el principio fundamental de RAII es asignar la propiedad de cualquier recurso asignado en el montón a un objeto alojado en la pila, cuyo destructor contiene el código para eliminar o liberar el recurso, así como cualquier código de limpieza asociado. Dichos objetos incluyen memoria asignada dinámicamente o identificadores de objetos del sistema.
En la mayoría de los casos, cuando inicialices un puntero sin formato o un identificador de recurso para que apunte a un recurso real, pasa ese puntero de inmediato a un puntero inteligente. En C++ moderno, los punteros sin formato solo se utilizan en pequeños bloques de código de alcance limitado, bucles o funciones auxiliares en los que el rendimiento es crítico y no hay posibilidad de confusión respecto a la propiedad.
En el ejemplo siguiente se compara una declaración de puntero sin formato con una declaración de puntero inteligente.
void UseRawPointer()
{
// Using a raw pointer -- not recommended.
Song* pSong = new Song(L"Nothing on You", L"Bruno Mars");
// Use pSong...
// Don't forget to delete!
delete pSong;
}
void UseSmartPointer()
{
// Declare a smart pointer on stack and pass it the raw pointer.
unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars"));
// Use song2...
wstring s = song2->duration_;
//...
} // song2 is deleted automatically here.
Como se muestra en el ejemplo, un puntero inteligente es una plantilla de clase que se declara en la pila y se inicializa mediante un puntero sin procesar que apunta a un objeto asignado en el montón. Una vez que se inicializa el puntero inteligente, se convierte en propietario del puntero sin formato. Este enfoque significa que el puntero inteligente es responsable de eliminar la memoria que especifica el puntero sin procesar.
El destructor de puntero inteligente contiene la llamada a delete. Dado que el puntero inteligente se declara en la pila, se invoca su destructor cuando el puntero inteligente queda fuera de ámbito. Se invoca incluso si se produce una excepción en algún lugar más arriba de la pila.
Acceda al puntero encapsulado mediante los conocidos operadores de puntero: -> y *. La clase de puntero inteligente sobrecarga estos operadores para devolver el puntero bruto encapsulado.
La expresión del puntero inteligente de C++ se parece a la creación de objetos en lenguajes como C#. Cree el objeto y, a continuación, deje que el sistema se ocupe de eliminarlo en el momento correcto. La diferencia es que no se ejecuta en segundo plano ningún recolector de basura por separado. La memoria se administra a través de las reglas estándar de ámbito de C++ para que el entorno de tiempo de ejecución sea más rápido y eficaz.
Importante
Cree siempre punteros inteligentes en una línea de código independiente, nunca en una lista de parámetros. Este enfoque evita una pérdida sutil de recursos debido a ciertas reglas de asignación de listas de parámetros.
En el ejemplo siguiente se muestra cómo se puede utilizar un tipo de puntero inteligente unique_ptr de la biblioteca estándar de C++ para encapsular un puntero a un objeto grande.
class LargeObject
{
public:
void DoSomething(){}
};
void ProcessLargeObject(const LargeObject& lo){}
void SmartPointerDemo()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Pass a reference to a method.
ProcessLargeObject(*pLarge);
} //pLarge is deleted automatically when function block goes out of scope.
En el ejemplo se muestran los pasos básicos siguientes para utilizar punteros inteligentes.
Declare el puntero inteligente como variable automática (local). No use la expresión
newomallocsobre el propio puntero inteligente.En el parámetro de tipo, especifique el tipo al que apunta el puntero encapsulado.
Pase un puntero sin procesar a un objeto al que se le ha aplicado
newen el constructor del puntero inteligente. Algunas funciones utilitarias o constructores de punteros inteligentes llevan a cabo esta operación por usted.Utilice los operadores sobrecargados
->y*para tener acceso al objeto.Deje que el puntero inteligente elimine el objeto.
Los punteros inteligentes están diseñados para ser lo más eficaces posible tanto en términos de memoria como de rendimiento. Por ejemplo, el único miembro de datos de unique_ptr es el puntero encapsulado. Este hecho significa que unique_ptr es exactamente el mismo tamaño que ese puntero, ya sea de cuatro o ocho bytes. Acceder al puntero encapsulado mediante los operadores sobrecargados * y -> del puntero inteligente no es significativamente más lento que acceder directamente a los punteros sin encapsular.
Los punteros inteligentes tienen sus propias funciones miembro, a las que se accede mediante la notación de puntos. Por ejemplo, algunos punteros inteligentes de la biblioteca estándar de C++ disponen de una función miembro reset que cede la propiedad del puntero. Este punto es útil si desea liberar la memoria gestionada por el puntero inteligente antes de que el puntero inteligente salga de su ámbito, como se muestra en el siguiente ejemplo.
void SmartPointerDemo2()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Free the memory before we exit function block.
pLarge.reset();
// Do some other work...
}
Los punteros inteligentes suelen proporcionar una forma de acceder directamente a su puntero bruto. Los punteros inteligentes de la biblioteca estándar de C++ tienen una get función miembro para este propósito.
CComPtr tiene un miembro público de la clase p. Al proporcionar acceso directo al puntero subyacente, puede usar el puntero inteligente para administrar la memoria en su propio código y seguir pasando el puntero no administrado a código que no admite punteros inteligentes.
void SmartPointerDemo4()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Pass raw pointer to a legacy API
LegacyLargeObjectFunction(pLarge.get());
}
Tipos de punteros inteligentes
En la sección siguiente se resumen los distintos tipos de punteros inteligentes disponibles en el entorno de programación de Windows y se describe cuándo utilizarlos.
Punteros inteligentes de la biblioteca estándar de C++
Utilice estos punteros inteligentes como primera opción para encapsular punteros a objetos C++ simples (POCO).
unique_ptrPermite exactamente un único propietario del puntero subyacente. Úselo como opción predeterminada para POCO, a menos que sepa con certeza que necesita un
shared_ptr. Puede moverse a un nuevo propietario, pero no se puede copiar ni compartir. Sustituye aauto_ptr, que está desusado. Comparado conboost::scoped_ptr,unique_ptres pequeño y eficiente. El tamaño es un puntero y admite referencias rvalue para la rápida inserción y recuperación de colecciones de bibliotecas estándar de C++. Archivo de encabezado:<memory>. Para obtener más información, consulte Cómo: Crear y usar instancias de unique_ptr y Clase unique_ptr.shared_ptrPuntero inteligente con recuento de referencias. Úselo cuando desee asignar un puntero sin procesar a varios propietarios. Por ejemplo, puede devolver una copia de un puntero almacenado en un contenedor y conservar el original. El puntero sin formato no se elimina hasta que todos los propietarios de
shared_ptrsalgan de su ámbito o renuncien a la propiedad.El tamaño consta de dos punteros: uno para el objeto y otro para el bloque de control compartido que contiene el recuento de referencias. Archivo de encabezado:
<memory>. Para obtener más información, consulte Cómo: Crear y usar instancias de shared_ptr y Clase shared_ptr.weak_ptrPuntero inteligente para casos especiales para usar con
shared_ptr. Unweak_ptrproporciona acceso a un objeto del que son propietarias una o varias instancias deshared_ptr, pero no participa en el recuento de referencias. Úselo cuando quiera observar un objeto, pero no necesite que siga existiendo. Necesario en algunos casos para romper las referencias circulares entre instancias deshared_ptr.Archivo de encabezado:
<memory>. Para obtener más información, consulte Cómo: Crear y usar instancias de weak_ptr y Clase weak_ptr.
Punteros inteligentes para objetos COM (programación clásica de Windows)
Cuando trabaje con objetos COM, encapsule los punteros de interfaz en un tipo de puntero inteligente adecuado. Active Template Library (ATL) define varios punteros inteligentes para propósitos diferentes. También puede usar el tipo de puntero inteligente _com_ptr_t, que el compilador utiliza cuando crea clases envoltorio a partir de archivos .tlb. Es la mejor opción cuando no desea incluir los archivos de encabezado ATL.
-
Use esta clase a menos que no pueda usar ATL. Realiza el recuento de referencias mediante los métodos
AddRefy deRelease. Para obtener más información, vea Cómo: Crear y usar instancias de CComPtr y CComQIPtr. -
Se parece a
CComPtr, pero también proporciona la sintaxis simplificada para llamar aQueryInterfaceen objetos COM. Para obtener más información, vea Cómo: Crear y usar instancias de CComPtr y CComQIPtr. -
Puntero inteligente a objetos que utilizan
CoTaskMemFreepara liberar memoria. -
Puntero inteligente para interfaces que se obtienen de la tabla global de interfaces (GIT).
-
Se parece a
CComQIPtren cuanto a funcionalidad, pero no depende de las cabeceras de ATL.
Punteros inteligentes ATL para objetos POCO
Además de punteros inteligentes para los objetos COM, ATL también define punteros inteligentes y colecciones de punteros inteligentes para objetos estándar de C++ (POCO). En la programación clásica de Windows, estos tipos son alternativas útiles a las colecciones de bibliotecas estándar de C++, especialmente cuando no se requiere portabilidad de código o cuando no desea mezclar los modelos de programación de la biblioteca estándar de C++ y ATL.
-
Puntero inteligente que garantiza la propiedad exclusiva transfiriendo la propiedad al copiarlo. Puede compararse con la clase
std::auto_ptren desuso. -
Puntero inteligente para objetos asignados mediante la función de C malloc.
-
Puntero inteligente para matrices asignadas mediante
new[]. -
Clase que encapsula una matriz de elementos
CAutoPtr. -
Clase que encapsula los métodos para manipular una lista de nodos de
CAutoPtr.