std::unique_ptr menjawab sebagian besar kebutuhan dengan satu pemilik, tetapi ada situasi yang memang tidak bisa direduksi menjadi satu: objek yang dipakai bersama beberapa bagian program tanpa ada satu pemegang utama. Untuk kasus itu pustaka standar menyediakan std::shared_ptr<T> — smart pointer dengan kepemilikan bersama berbasis reference counting. Setiap salinan shared_ptr menambah satu penghitung pemilik; setiap shared_ptr yang berakhir menguranginya; dan begitu hitungan mencapai nol, objek akhirnya dilepas. Chapter ini membedah mekanisme hitung itu lewat program yang bisa diuji, lalu menimbang biayanya supaya kita tahu kapan shared_ptr tepat dan kapan ia kebablasan.
Mekanisme Reference Counting #
Di balik setiap objek yang dibagi, shared_ptr menjaga satu blok kontrol — struktur pembukuan yang memuat penghitung jumlah pemilik (use count) plus data lain yang menyusul di chapter berikutnya. Saat shared_ptr disalin, hitungan naik; saat salah satu salinan keluar dari scope, hitungan turun; saat hitungan menyentuh nol, destructor objek dijalankan dan blok kontrol ikut dilepas. Yang penting dicatat: objek yang dibagi tetap hanya satu — salinan shared_ptr menyalin pembukuan, bukan objeknya. Semua itu berjalan otomatis, dan kita bisa mengamati hitungannya kapan pun lewat use_count().
#include <iostream>
#include <memory>
#include <string>
class Data {
public:
explicit Data(const std::string& label) : label_(label) {
std::cout << label_ << " diciptakan\n";
}
~Data() {
std::cout << label_ << " dibebaskan\n";
}
const std::string& label() const { return label_; }
private:
std::string label_;
};
int main() {
auto a = std::make_shared<Data>("sensor-suhu");
std::cout << "use_count awal : " << a.use_count() << "\n";
{
auto b = a; // salinan: dua pemilik untuk satu objek
std::cout << "use_count dalam blok : " << a.use_count() << "\n";
std::cout << "b membaca : " << b->label() << "\n";
} // b berakhir: jumlah pemilik turun
std::cout << "use_count setelah blok: " << a.use_count() << "\n";
return 0; // a berakhir: count mencapai 0, objek dibebaskan
}Output yang Dihasilkan:
sensor-suhu diciptakan
use_count awal : 1
use_count dalam blok : 2
b membaca : sensor-suhu
use_count setelah blok: 1
sensor-suhu dibebaskanBacaan urut dari output di atas:
- Baris 19 (
std::make_shared<Data>(...)): satu objekDatadan blok kontrolnya dialokasikan; hitungan awalnya 1 karena hanyaayang memiliki. - Baris 23 (
auto b = a;): penyalinan SAH untukshared_ptr— berbeda dariunique_ptr. Hitungan naik menjadi 2, danbmenunjuk objek yang sama, bukan salinan datanya. - Baris 25 (
b->label()): kedua pemilik memakai objek yang sama dengan hak akses yang setara. - Baris 26 (
}):bberakhir bersama bloknya; hitungan turun ke 1. Objek belum dilepas karena masih ada pemilik. - Baris 29 (
return 0;):aberakhir, hitungan menyentuh 0, dan barulah pesandibebaskantercetak.
Biaya yang Kita Bayar #
Mekanisme hitung ini bukan gratis, dan menimbang biayanya membantu kita memilih dengan sadar:
- Memori tambahan:
shared_ptrberukuran dua pointer — satu ke objek, satu ke blok kontrol — dan blok kontrol itu sendiri menempati memori terpisah. - Alokasi ekstra:
make_sharedmemang menyatukan objek dan blok kontrol dalam satu alokasi, tetapi tetap ada lebih banyak data yang dikelola dibandingunique_ptr. - Sinkronisasi: penambahan dan pengurangan hitungan harus aman di lingkungan multithread, sehingga operasi penyalinan membawa biaya atomik.
Itulah alasan aturan praktisnya berbunyi: unique_ptr adalah default, shared_ptr hanya bila kepemilikan memang dibagi secara nyata. Tanda “dibagi nyata” biasanya terlihat dari desain — beberapa bagian program perlu menyimpan rujukan ke objek yang sama tanpa ada yang menjadi pemegang utama, misalnya resource yang di-cache atau konfigurasi yang dipakai lintas modul.
Ada satu kelemahan halus yang menanti di balik kepemilikan bersama: bila dua shared_ptr saling menunjuk, hitungan mereka tidak pernah mencapai nol. Chapter berikutnya membahas std::weak_ptr — observer tanpa kepemilikan yang dirancang khusus untuk membongkar simpul itu.
Referensi: cppreference — std::shared_ptr , cppreference — std::make_shared , cppreference — use_count .