Anuncios

Bienvenidos sean a este post, hoy veremos un puntero inteligente.

Anuncios

En el post anterior vimos como mejorar un codigo mediante RAII pero puede suceder una particularidad. Para ello vamos a tomar el codigo que vimos en el post anterior pero lo trabajaremos de la siguiente manera:

#include <iostream>
#include <string>

template<typename T>
class ManejarRecursos
{
public:
        ManejarRecursos(T* p) : ptr{p} {}
        ~ManejarRecursos() { delete ptr; }
        T& operator*() { return *ptr; }
        T* operator->() { return ptr; }
private:
        T* ptr;
};

class Producto
{
public:
        Producto() {}
        ~Producto() {}
        void set_nombre(std::string n) { nombre = n; }
        std::string get_nombre() { return nombre; }
        void set_precio(float p) { precio = p; }
        float get_precio() { return precio; }
        void set_disponible(bool d) { disponible = d; }
        bool get_disponible() { return disponible; }
private:
        std::string nombre;
        float precio;
        bool disponible;
};

void ver_nombre(ManejarRecursos<Producto> v) {
        std::cout << v->get_nombre() << std::endl;
}

int main()
{
        ManejarRecursos<Producto> video{new Producto};
        video->set_nombre("NVIDIA GeForce RTX 4080");
        video->set_precio(1100.0);
        video->set_disponible(true);
        ver_nombre(video);
        if (video->get_disponible()) {
                std::cout << video->get_nombre() << " vale ";
                std::cout << video->get_precio() << " USDD\n";
        } else {
                std::cout << video->get_nombre() << " no esta disponible\n";
        }

        return 0;
}
Anuncios
Anuncios

La primera clase es para definir al RAII que utilizaremos para nuestros punteros. Al ser generico nos permitira manejar cualquier tipo de dato. Sus principales particularidades son la sobrecarga de dos operadores para poder manejar el puntero y el llamado de metodos. Pero su estrella principal es el destructor que se encarga de eliminar el puntero para evitar que podamos dejar informacion huerfana en memoria. La siguiente clase es para poder manejar nuestros productos y esta contendra algunas propiedades, asi como tambien algunos metodos para manejar a estas. A continuacion, si tenemos el primer cambio. En este caso agregamos una funcion que recibe un dato del tipo del RAII y lo usamos para mostrar el nombre que le asignamos.

Anuncios

En el main, primero creamos un objeto del RAII y este sera del tipo Producto. Lo siguiente sera establecer los valores para sus propiedades internas. A diferencia del post anterior le pedimos que nos muestre el valor de nombre mediante la funcion anterior. Para ello pasamos el objeto que generamos anteriormente. Luego tenemos un condicional donde si el producto se encuentra disponible muestra un mensaje con el nombre y el precio. En caso contrario, nos informa que no esta disponible. Compilemos y ejecutemos para ver como es su salida:

$ ./memoria
NVIDIA GeForce RTX 4080
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Abortado
$
Anuncios
Anuncios

Observen que nos mostro el mensaje pero despues se interrumpio al programa. Por que sucedio esto? Este es un inconveniente de usarlo de esta manera, porque todo lo que hagamos siempre apuntara al mismo puntero. Pero reccuerden que cuando dejemos usar unu objeto de esta clase procedemos a eliminar el puntero. Por lo tanto, despues de usar la funcion borramos ese puntero y por ende, nos devolvio ese mensaje de error. Aqui entra en accion el metodo que da titulo al post. Para entender el conccepto vamos a tomar el codigo anterior y haremos las siguiente modificaciones:

#include <iostream>
#include <string>
#include <memory>

template<typename T>
class ManejarRecursos
{
public:
        ManejarRecursos(T* p) : ptr{p} {}
        ~ManejarRecursos() { delete ptr; }
        T& operator*() { return *ptr; }
        T* operator->() { return ptr; }
private:
        T* ptr;
};

class Producto
{
public:
        Producto() {}
        ~Producto() {}
        void set_nombre(std::string n) { nombre = n; }
        std::string get_nombre() { return nombre; }
        void set_precio(float p) { precio = p; }
        float get_precio() { return precio; }
        void set_disponible(bool d) { disponible = d; }
        bool get_disponible() { return disponible; }
private:
        std::string nombre;
        float precio;
        bool disponible;
};

void ver_nombre(ManejarRecursos<Producto> v) {
        std::cout << v->get_nombre() << std::endl;
}

int main()
{
        std::unique_ptr<Producto> video{new Producto};
        video->set_nombre("NVIDIA GeForce RTX 4080");
        video->set_precio(1100.0);
        video->set_disponible(true);
        if (video->get_disponible()) {
                std::cout << video->get_nombre() << " vale ";
                std::cout << video->get_precio() << " USDD\n";
        } else {
                std::cout << video->get_nombre() << " no esta disponible\n";
        }

        return 0;
}
Anuncios

La primera es la incorporacion de la libreria memory. Esta nos permitira poder utilizar a unique_ptr. La siguiente modificacion es en el main. Donde al momento de crear el objeto, en lugar de usar a ManejarRecursos le decimos que utilice a unique_ptr. Como dijimos este es un puntero inteligente pero se lo considera basico. La ultima modificacion fue la eliminacion del llamado a la funcion ver_nombre. Si lo compilan y ejecutan deben tener la siguiente salida:

$ ./memoria
NVIDIA GeForce RTX 4080 vale 1100 USDD
$
Anuncios
Anuncios

Como pueden ver obtuvimos exactamente la misma salida con el uso de RAII y con las mismas conductas. Hablemos un poco sobre unique_ptr. La primera particularidad es que trabaja igual a lo visto en la clase ManejadorRecursos, encargandose tambien de nuestro puntero. Otra particularidad de esta clase es que transforma al elemento al tipo Pointer*. Se lo denomina unique porque provee una semantica de propiedad estricta y es obligado a destruir el objeto adquirido. Y su particularidad mas importante es que no pueden ser copiados, esto es debido a que no posee un constructor de copia u operador de asignacion. Esta es la razon porque se la considera estricta. Pero esto no impide que podemos moverlo a otra clase, y para ello debemos pasar la propiedad completa a otra instancia de puntero unico.

Anuncios

Con lo comentado anteriormente podemos considerar a este como un envoltorio para el puntero del objeto localizado. A su vez, tenemos varios metodos para manejar al unique_ptr. Veamos al primero de ellos:

Producto* v = video.release();
Anuncios

Este metodo libera al puntero de unique_ptr y devuelve la propiedad del mismo pero no lo elimina. Es decir, el objeto ya no pertenece a unique_ptr. Esto nos obliga a que nuevamente somos los responsables de eliminar al puntero una vez que salgamos del rango o scope. Pero podemos retomar su propiedad mediante otro metodo llamado reset. Este llama al operador de eliminacion para el puntero y resetea al puntero para su proximo uso. Tambien tenemos otro metodo muy particular:

std::unique_ptr<Producto> otro{new Producto()};
Producto* v = video.get();
Anuncios

Este nos permite acceder al objeto subyacente pero sin liberar la propiedad del mismo para unique_ptr. Si vemos el ejemplo superior, el puntero donde aplicamos al get apuntara al mismo objeto que es manejado por otro.

Anuncios

En resumen, hoy hemos visto a unique_ptr, que es, para que sirve, como se utiliza, y un ejemplo practico para poder aplicarlo y reemplazar a RAII. Espero les haya resultado de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios
pp258

Donatión

It’s for site maintenance, thanks!

$1.50