DMarlon Studio

Lección 7 · 7 de 20

unique_ptr y shared_ptr · C++

Use los punteros inteligentes unique_ptr y shared_ptr de C++ para que la memoria se libere sola: canciones con uno o con varios dueños.

C++ · unique_ptr y shared_ptr en DMarlon Studio: el código del ejemplo y su salida en la terminal

Un puntero inteligente es un objeto que guarda un puntero y aplica RAII: cuando deja de existir, libera la memoria. Ya no hace falta new ni delete: std::make_unique<Song>("Demo take") crea la canción y devuelve un `std::unique_ptr` que la posee. El destructor de Song escribe un mensaje para ver cuándo se libera cada una.

unique_ptr tiene un solo dueño, así que no se puede copiar, pero sí mover: std::move(demo) pasa la propiedad a keeper y deja demo vacío (en una condición, un puntero vacío vale false). keeper.reset() libera la canción en ese mismo momento; si no, se habría liberado al final de main. La flecha funciona como con un puntero normal: demo->title.

`std::shared_ptr` admite varios dueños y lleva la cuenta, que da use_count(). hit y las dos listas, rock y party, comparten la misma canción, así que hay 3 dueños. Al vaciar rock y hacer hit.reset() queda uno, y la canción sigue viva en party.

La canción se libera cuando desaparece su último dueño: el vector party, al terminar main. Por eso «(freed Summer Nights)» sale después de «End of main». Use unique_ptr por defecto, que no cuesta nada frente a un puntero normal, y shared_ptr solo cuando la propiedad sea compartida de verdad.

Código · main.cpp

#include <iostream>#include <memory>#include <string>#include <vector>struct Song {    std::string title;    explicit Song(std::string t) : title(t) {}    ~Song() { std::cout << "  (freed " << title << ")\n"; }};int main() {    // unique_ptr: a single owner that frees the object when it goes away.    std::unique_ptr<Song> demo = std::make_unique<Song>("Demo take");    std::cout << "Unique: " << demo->title << "\n";    std::unique_ptr<Song> keeper = std::move(demo);    // ownership moves; it cannot be copied    std::cout << "demo is " << (demo ? "full" : "empty") << " now\n";    keeper.reset();                                     // freed right here    // shared_ptr: several owners and a count of them.    auto hit = std::make_shared<Song>("Summer Nights");    std::vector<std::shared_ptr<Song>> rock, party;    rock.push_back(hit);    party.push_back(hit);    std::cout << "Owners of " << hit->title << ": " << hit.use_count() << "\n";    rock.clear();    hit.reset();    std::cout << "Left in party: " << party[0]->title << " (owners: " << party[0].use_count() << ")\n";    std::cout << "End of main\n";}   // party is destroyed: its last owner frees the song

Salida · c++ -std=c++17 main.cpp -o main && ./main

Unique: Demo take
demo is empty now
  (freed Demo take)
Owners of Summer Nights: 3
Left in party: Summer Nights (owners: 1)
End of main
  (freed Summer Nights)

Para recordar

  • Cree los objetos con std::make_unique y std::make_shared, no con new.
  • unique_ptr: un solo dueño; se mueve con std::move, no se copia.
  • shared_ptr: varios dueños; el objeto vive mientras quede alguno.
  • reset() suelta el objeto; un puntero vacío vale false.
  • Prefiera unique_ptr salvo que necesite compartir.

Ejercicio

Cree una tercera lista, chill, añádale hit antes de rock.clear() y escriba use_count() después de cada paso. ¿Cuándo se libera ahora la canción?