Tiga chapter terakhir memberi kita tiga smart pointer, dan sekarang waktunya menyusunnya menjadi satu sistem keputusan yang bisa dipakai tanpa ragu. Kuncinya adalah konsep ownership semantics: setiap pointer — smart maupun mentah — membawa peran kepemilikan yang harus jelas siapa bertanggung jawab melepas objeknya. std::unique_ptr mewakili kepemilikan eksklusif, std::shared_ptr kepemilikan bersama, std::weak_ptr pengamat tanpa kepemilikan, dan pointer mentah berada di posisi paling sederhana: hanya mengamati, tanpa hak dan tanpa tanggung jawab melepas. Chapter ini merangkum keempatnya, mengujinya lewat konvensi passing antar fungsi, dan menutupnya dengan tabel keputusan untuk kode sehari-hari.
Empat Peran Kepemilikan #
| Alat | Peran kepemilikan | Boleh disalin | Melepas objek? |
|---|---|---|---|
std::unique_ptr<T> | Eksklusif — satu pemilik | Tidak (hanya dipindah) | Ya, saat pemilik berakhir |
std::shared_ptr<T> | Bersama — banyak pemilik | Ya (hitungan naik) | Ya, saat hitungan mencapai 0 |
std::weak_ptr<T> | Observer tanpa kepemilikan | Ya (tidak menambah count) | Tidak |
Pointer mentah T* | Observer tanpa kepemilikan | Ya (hanya alamat) | TIDAK — jangan pernah |
Satu baris tabel itu paling sering dilanggar di kode nyata: pointer mentah tetap sah di C++ modern, tetapi rolenya kini tegas — ia melihat, bukan memiliki. Begitu satu alokasi heap punya “pemilik” resmi berupa smart pointer, pointer mentah lain yang menunjuk objek yang sama diperlakukan seperti weak_ptr: hanya membaca.
Konvensi Passing Antar Fungsi #
Semantik kepemilikan paling terasa pada signature fungsi. Tiga konvensi standar: T* atau T& untuk mengamati, std::unique_ptr<T> by value untuk mengambil alih kepemilikan, dan const std::shared_ptr<T>& untuk ikut memakai objek bersama tanpa menyalin. Program berikut menguji ketiganya berdampingan — dan satu catatan: pemindahan kepemilikan di sini memakai std::move, mekanisme yang dibedah di Chapter Pengantar Move Semantics.
#include <iostream>
#include <memory>
#include <string>
#include <utility>
// T* — hanya mengamati; tidak memiliki, tidak mengubah kepemilikan
void amati(const std::string* teks) {
if (teks != nullptr) {
std::cout << "amati : " << *teks << "\n";
}
}
// unique_ptr by value — kepemilikan PINDAH ke fungsi ini
void konsumsi(std::unique_ptr<std::string> teks) {
std::cout << "konsum: " << *teks << " (kepemilikan kini di fungsi)\n";
}
// const shared_ptr& — ikut memakai tanpa menyalin; use_count tidak naik
void pakaiBersama(const std::shared_ptr<std::string>& teks) {
std::cout << "bagi : " << *teks << " (use_count tetap "
<< teks.use_count() << ")\n";
}
int main() {
auto rahasia = std::make_unique<std::string>("kode akses 1234");
amati(rahasia.get()); // observasi lewat pointer mentah
konsumsi(std::move(rahasia)); // transfer kepemilikan ke fungsi
std::cout << "rahasia kosong? " << (rahasia == nullptr ? "ya" : "tidak")
<< "\n";
auto data = std::make_shared<std::string>("profil pengguna");
pakaiBersama(data);
std::cout << "use_count akhir: " << data.use_count() << "\n";
return 0;
}Output yang Dihasilkan:
amati : kode akses 1234
konsum: kode akses 1234 (kepemilikan kini di fungsi)
rahasia kosong? ya
bagi : profil pengguna (use_count tetap 1)
use_count akhir: 1Yang dibuktikan oleh ketiga konvensi itu:
- Baris 26 (
amati(rahasia.get())): fungsi menerimaconst std::string*— ia bisa membaca, tidak bisa memiliki, dan pointer mentahnya tidak mengubah keadaanrahasiasedikit pun. - Baris 27 (
konsumsi(std::move(rahasia))): parameter by valueunique_ptradalah kontrak pengambilalihan; setelah pemanggilan, kepemilikan berpindah — terbukti darirahasia kosong? ya. - Baris 32 (
pakaiBersama(data)): melewatkanconst std::shared_ptr&tidak menyalin, sehinggause_count tetap 1— ikut memakai tanpa ikut memiliki. - Baris 33 (
use_count akhir: 1): setelah fungsi kembali, keadaan kepemilikan utuh —datamasih satu-satunya pemilik.
Perhatikan kontras baris 2 dan 3: melewatkan unique_ptr by value mengubah siapa pemiliknya, sedangkan const shared_ptr& tidak mengubah apa-apa. Signature fungsi yang tepat membuat maksud kepemilikan terbaca tanpa komentar.
Tabel Keputusan Sehari-hari #
| Kebutuhan | Pilihan | Catatan |
|---|---|---|
| Objek lokal, umur sesuai blok | Variabel biasa di stack | Tidak perlu pointer sama sekali |
| Satu pemilik, umur melebihi blok | std::unique_ptr<T> | Default smart pointer |
| Banyak pemilik sejajar | std::shared_ptr<T> | Sadari biaya blok kontrol |
| Melihat tanpa mempertahankan hidup | std::weak_ptr<T> | Akses lewat lock() |
| Membaca objek yang dijamin hidup | const T& atau T* | Observer, tanpa pelepasan |
| Array dinamis | std::vector atau std::unique_ptr<T[]> | Rujuk Chapter new[] dan delete[] |
Sistem kepemilikan ini baru bisa bekerja penuh bila kepemilikan mudah berpindah — dan itulah mekanisme yang belum kita buka: move. Chapter berikutnya mengenalkan move semantics melalui kasus yang sudah kita pakai dua kali di atas: memindahkan unique_ptr tanpa menyalinnya.
Referensi: cppreference — Memory management library , cppreference — std::unique_ptr .