Declaración y usos de unique_ptr en C++

Shikha Chaudhary 16 febrero 2024
  1. Declaración y usos de unique_ptr en C++
  2. Punteros únicos en C++
  3. Conclusión
Declaración y usos de unique_ptr en C++

Los punteros tienen muchos propósitos útiles en C++. Desde ahorrar espacio en los programas hasta ayudar a implementar estructuras de datos, los punteros son una herramienta muy importante para los lenguajes de programación como C y C++.

Este artículo discutirá los conceptos de uno de esos punteros, el puntero único. La biblioteca estándar de C++ proporciona este puntero como una de las implementaciones de los punteros inteligentes.

¿Se pregunta qué son estos punteros inteligentes ahora? Sigue leyendo; Tenemos todo cubierto para ti.

Declaración y usos de unique_ptr en C++

Ya sabemos que los punteros se utilizan para almacenar direcciones de otras variables. Aunque estos punteros son útiles en muchos lugares, tienen un problema importante.

Mira el bloque de código dado. La mayoría de las veces, declaramos un puntero de la siguiente manera y luego lo usamos pero nunca lo eliminamos.

void demo() {
  int *d = new int;
  *d = 10;
}

Esto lleva al problema de las fugas de memoria. Ocurre cuando una sección de memoria no se desasigna o libera después de su uso.

Por lo tanto, la memoria que no está disponible para ningún otro programa ni está libre genera un desperdicio de espacio y ralentiza el rendimiento. Esto no es más que una pérdida de memoria.

Cuando los programas son más complejos, esto puede incluso llevar a que toda la memoria del montón se vuelva inútil. Por lo tanto, a C++ 11 se le ocurrió el concepto de punteros inteligentes para contrarrestar este problema.

Punteros inteligentes en C++

Los lenguajes como Java y C# tienen un mecanismo a través del cual la memoria no utilizada se desasigna automáticamente. Por lo tanto, el programador no necesita desasignar punteros, de lo cual se encargará el sistema de recolección de basura.

Para C++, esto se logra mediante el uso de punteros inteligentes. Los punteros inteligentes no son más que clases simples que envuelven los punteros sin formato y sobrecargan los operadores como * y ->.

Los objetos de una clase de puntero inteligente parecen ser los mismos que los punteros normales, pero a diferencia de los punteros normales, ellos mismos pueden desasignar el espacio de memoria no utilizado. Cuatro tipos de punteros inteligentes se definen dentro del encabezado <memoria> de la biblioteca estándar de C++, de los cuales unique_ptr es uno.

Ahora, estamos listos para comenzar a aprender sobre el puntero único de C++.

Punteros únicos en C++

Se desarrolló un unique_ptr para reemplazar el auto_ptr de C++. A diferencia de los punteros sin procesar, unique_ptr es un contenedor para punteros sin procesar y garantiza la propiedad exclusiva y sin pérdida de memoria.

Declaración de unique_ptr en C++

El siguiente código muestra cómo declaramos un puntero único en C++.

std::unique_ptr<Type> p(new Type);

Aquí, si queremos crear un puntero único que apunte a un número entero, digamos 10, así es como cambiaría la sintaxis anterior.

std::unique_ptr<int> p(new int(10));

La línea de código anterior significa que un puntero único, p, apunta al valor 10. Debe comprender cómo funciona esta declaración básica.

La expresión nueva crea el puntero p en esta línea de código anterior. Pero el significado cambia si cambiamos la misma declaración a la que se da a continuación.

std::unique_ptr<int> p2(x);

Ahora aquí, el puntero se almacena en la variable x. Conceptualmente, las cosas son iguales.

Estamos haciendo un unique_ptr en ambos lugares, pero no es recomendable usar el segundo método.

Usando el segundo método, podemos hacer algo como esto:

std::unique_ptr<int> p2(x);
std::unique_ptr<int> p3(x);
//....
std::unique_ptr<int> p6(x);

Esto significa que más de un puntero único posee el mismo objeto, lo que va en contra de la semántica de un puntero único, como verá en las siguientes secciones.

Además, en C++14, tenemos una opción para usar make_unique para declarar un unique_ptr como este:

unique_ptr<int> d = make_unique<int>(10);

Esto crea un puntero único d que apunta al valor 10.

Veamos ahora las diversas características de unique_ptr una por una.

Eliminación automática de C++ unique_ptr

Con los punteros sin procesar, los programadores a menudo se olvidan de eliminar el puntero después de usarlo, lo que genera el problema de pérdida de memoria. El unique_ptr ahorra esta molestia ya que destruye el objeto que contiene cuando el unique_ptr sale del alcance.

El objeto se destruye cuando se destruye el unique_ptr, o se asigna otro puntero al unique_ptr. Esto se hace con la ayuda de get_deleter() (ptr), que utiliza el operador delete para destruir el objeto y desasignar la memoria.

Por lo tanto, cuando usa un unique_ptr, no tiene que eliminar los objetos asignados dinámicamente, y el destructor de unique_ptr se encargará de eso.

A continuación se muestra un código ilustrado que muestra el alcance fuera del cual se destruye el puntero.

void demo() { unique_ptr<int> demo(new int); }
< -- -- -- -- -- -- -- -- -- -- -- -- -scope of the pointer ends here

El objeto se destruye cuando el control sale de estas llaves.

Propiedad exclusiva de C++ unique_ptr

La siguiente y muy importante característica de unique_ptr proporciona la propiedad exclusiva de un objeto y no se puede copiar. Esto significa que solo un unique_ptr puede apuntar simultáneamente a un solo objeto.

Esta es también la razón por la que no podemos copiar un unique_ptr. A menudo, con punteros sin procesar, la asignación normal lleva a copiar el puntero.

Pero este no es el caso con unique_ptr. Mira este ejemplo.

Aquí tenemos dos punteros únicos, p1 y p2, y copiamos o asignamos el primer puntero al segundo.

#include <iostream>
#include <memory>

using namespace std;

int main() {
  unique_ptr<int> p1(new int);
  unique_ptr<int> p2 = p1;
  cout << "Success";
  return 0;
}

Producción :

use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]'
    8 |     unique_ptr<int> p2 = p1;

Puede ver que obtenemos un error de tiempo de compilación y la declaración de impresión no se ejecuta. Esto muestra claramente que no podemos copiar un unique_ptr en C++, que simultáneamente apunta a un solo objeto.

Ahora, si hubiéramos hecho esto con un puntero sin procesar, la instrucción print se habría ejecutado con éxito. Esto se hace a continuación.

#include <iostream>
using namespace std;

int main()

{
  int *ptr1;
  int *ptr2;
  ptr2 = ptr1;
  cout << "Success";
  return 0;
}

Producción :

Success

Esta vez, los punteros se copian porque son punteros en bruto. Básicamente, así es como funciona unique_ptr cuando se trata de duplicación.

Transferencia de propiedad del C++ unique_ptr

Como se vio anteriormente, no podemos cambiar la propiedad de un unique_ptr simplemente copiándolos. La solución aquí es usar la función move() proporcionada por la biblioteca estándar de C++.

Estamos tomando el mismo código donde intentamos copiar un unique_ptr usando el operador de asignación. Sin embargo, esta vez usaremos la función mover() durante esta tarea.

#include <iostream>
#include <memory>

using namespace std;

int main() {
  unique_ptr<int> p1(new int);
  unique_ptr<int> p2 = move(p1);  // using the move() function
  cout << "Success";
  return 0;
}

Producción :

Success

Ahora, como hemos usado la función move(), no obtenemos ningún error, y la sentencia print también se ejecuta.

Así es como podemos transferir la propiedad de un objeto a otro unique_ptr, pero en cualquier caso, el objeto al que apunta un unique_ptr permanece uno a la vez.

Estas fueron las principales características de los unique_ptr que los diferencian de los punteros en bruto.

Consulte esta documentación para obtener más información sobre los punteros únicos.

Conclusión

En este artículo, analizamos el concepto del puntero único en C++. Un puntero único es un puntero inteligente conocido por sus funcionalidades de propiedad exclusiva y eliminación automática.

Aprendimos cómo se declara y cómo aún podemos transferir la propiedad de un puntero único usando la función move(). Espero que pueda usar el puntero único cuando sea necesario ahora que conoce sus funciones.

Artículo relacionado - C++ Pointer