Setiap variabel yang kita buat menempati memori. C++ memberi kita kontrol langsung atas di mana dan kapan memori dialokasikan, sekaligus kapan dibebaskan. Kontrol ini adalah kekuatan sekaligus sumber bug paling mahal: kebocoran memori, double free, dan dangling pointer. Pada Chapter ini kita akan mempelajari perbedaan stack dan heap, alokasi manual dengan new/delete, serta pola modern RAII dengan smart pointer yang membebaskan memori secara otomatis.

Stack vs Heap #

C++ mengelola dua region memori utama untuk variabel:

RegionKarakteristikCocok untuk
StackCepat, otomatis, ukuran terbatas (~1–8 MB)Variabel lokal, tipe kecil, lifetime singkat
HeapManual, ukuran besar (GB), lebih lambatData besar, lifetime tidak terprediksi

Mekanisme: Alokasi Stack vs Heap #

Saat kita menulis int x = 10;, compiler memesan slot di stack. Saat scope berakhir, slot dilepaskan otomatis tanpa kode tambahan. Sebaliknya, int* p = new int(10); memesan memori di heap dan mengembalikan pointer. Memori heap tidak dibebaskan otomatis — kita harus memanggil delete p; secara eksplisit.

Kode: Stack vs Heap #

cpp
#include <iostream>

struct Buff {
    int data[100];   // 400 byte
};

int main() {
    std::cout << "sizeof(Buff) = " << sizeof(Buff) << '\n';

    Buff b;              // di stack, dibebaskan otomatis
    b.data[0] = 1;
    std::cout << "b.data[0]   = " << b.data[0] << '\n';

    int* heap_int = new int(42);   // di heap
    std::cout << "heap_int    = " << *heap_int << '\n';
    delete heap_int;               // WAJIB hapus manual
    return 0;
}

Output yang Dihasilkan:

text
sizeof(Buff) = 400
b.data[0]   = 1
heap_int    = 42
  1. Baris Buff b;: b dialokasikan di stack. Saat fungsi main berakhir, compiler otomatis membebaskan memorinya.
  2. Baris sizeof(Buff): struct berisi array int[100], total 400 byte. Seluruhnya berada di stack.
  3. Baris int* heap_int = new int(42);: new memesan memori di heap dan mengembalikan pointer. Memori tetap dialokasikan sampai delete dipanggil.
  4. Baris delete heap_int;: membebaskan memori heap. Tanpa baris ini, terjadi memory leak.

Alokasi Manual: new dan delete #

Alokasi manual memberi kendali penuh, tetapi menempatkan beban pembebasan pada programmer. Setiap new harus berpasangan dengan delete; setiap new[] dengan delete[]. Lupa atau salah pasangan menyebabkan bug serius.

Kode: Alokasi Manual #

cpp
#include <iostream>

void pakai_manual() {
    int* p = new int(42);
    std::cout << "manual: " << *p << '\n';
    delete p;            // WAJIB
}

int main() {
    pakai_manual();
    return 0;
}

Output yang Dihasilkan:

text
manual: 42
  1. Baris int* p = new int(42);: memesan 4 byte di heap, mengisi 42, kembalikan pointer.
  2. Baris delete p;: membebaskan memori yang ditunjuk p. Setelah ini, p menjadi dangling — mengaksesnya adalah undefined behavior.

RAII: Resource Acquisition Is Initialization #

RAII adalah idiom C++ yang mengikat resource ke lifetime objek. Saat objek dibuat, resource diakuisisi (alokasi, buka file, lock mutex); saat objek dihancurkan, resource dilepas. Dengan RAII, kita tidak perlu memanggil delete manual — destructor objek melakukannya.

Mekanisme: Destructor sebagai Penjaga #

Setiap kelas bisa mendefinisikan destructor ~Nama(). Destructor dipanggil otomatis saat objek keluar scope — bahkan bila terjadi exception. Inilah pondasi keamanan memori modern C++.

Kode: Destructor dan Scope #

cpp
#include <iostream>

class Logger {
public:
    Logger()  { std::cout << "ctor\n"; }
    ~Logger() { std::cout << "dtor\n"; }
};

int main() {
    {
        Logger log;
        std::cout << "di dalam scope\n";
    }
    std::cout << "keluar scope\n";
    return 0;
}

Output yang Dihasilkan:

text
ctor
di dalam scope
dtor
keluar scope
  1. Baris Logger log;: constructor dipanggil, mencetak ctor.
  2. Baris } (akhir scope dalam): destructor dipanggil otomatis, mencetak dtor. Ini terjadi bahkan bila ada exception di dalam scope.
  3. Baris keluar scope: dicetak setelah destructor — menunjukkan urutan yang benar.

Smart Pointer #

C++11 memperkenalkan smart pointer yang mengelola memori heap secara otomatis via RAII. Dua yang paling penting:

Smart pointerKepemilikanKapan dipakai
std::unique_ptr<T>Eksklusif (satu pemilik)Default untuk heap; paling efisien
std::shared_ptr<T>Berbagi (reference counting)Bila banyak pemilik butuh akses

Mekanisme: unique_ptr vs shared_ptr #

unique_ptr tidak bisa di-copy — hanya di-move. Saat unique_ptr keluar scope, destructor otomatis memanggil delete. shared_ptr memakai reference count; memori baru dibebaskan saat hitungan mencapai nol.

Kode: Smart Pointer #

cpp
#include <iostream>
#include <memory>
#include <vector>

void pakai_unique() {
    auto p = std::make_unique<int>(99);
    std::cout << "unique: " << *p << '\n';
    // tidak perlu delete — dibebaskan otomatis
}

void pakai_vector() {
    auto v = std::make_unique<std::vector<int>>(10, 5);
    std::cout << "vector size: " << v->size() << '\n';
    // dibebaskan otomatis
}

int main() {
    pakai_unique();
    pakai_vector();

    // Shared ownership
    auto shared = std::make_shared<int>(7);
    auto shared2 = shared;
    std::cout << "use_count = " << shared.use_count() << '\n';
    *shared2 = 100;
    std::cout << "shared    = " << *shared << '\n';
    return 0;
}

Output yang Dihasilkan:

text
unique: 99
vector size: 10
use_count = 2
shared    = 100
  1. Baris std::make_unique<int>(99): membuat unique_ptr<int> yang mengelola int di heap. Memori dibebaskan saat p keluar scope.
  2. Baris auto v = std::make_unique<std::vector<int>>(10, 5);: unique_ptr ke vector dengan 10 elemen bernilai 5. Pola ini sering dipakai untuk mengelola container besar secara otomatis.
  3. Baris auto shared2 = shared;: shared_ptr bisa di-copy — kedua pointer sekarang menunjuk objek yang sama. Reference count menjadi 2.
  4. Baris shared.use_count(): mengembalikan jumlah pemilik aktif. Saat kedua pointer keluar scope, count turun ke 0 dan memori dibebaskan.
  5. Baris *shared2 = 100;: modifikasi melalui salah satu shared_ptr terlihat dari yang lain — keduanya menunjuk memori yang sama.

Memory Leak: Apa dan Mengapa #

Memory leak terjadi bila memori heap dialokasikan tetapi tidak pernah dibebaskan. Program tetap berjalan, tetapi konsumsi memori terus tumbuh. Pada program lama atau server yang berjalan berhari-hari, leak dapat menguras seluruh RAM sistem.

Penyebab Umum Leak #

  1. Lupa delete setelah new.
  2. Exception terjadi sebelum delete dipanggil — control flow melompati pembersihan.
  3. Pointer ditimpa dengan nilai baru sebelum delete yang lama.
  4. Cycle reference pada shared_ptr yang saling menunjuk.

Solusi Modern #

RAII + smart pointer menyelesaikan masalah 1, 2, dan 3. Untuk masalah 4 (cycle reference), gunakan std::weak_ptr untuk memutus siklus — akan dibahas pada bab Graph.

Ringkasan #

  • Stack: cepat, otomatis, terbatas. Heap: manual, besar, lebih lambat.
  • Setiap new wajib berpasangan dengan delete; new[] dengan delete[]. Mencampur pasangan adalah undefined behavior.
  • RAII mengikat resource ke lifetime objek — destructor otomatis membebaskan saat scope berakhir, bahkan bila terjadi exception.
  • std::unique_ptr adalah default untuk heap modern — efisien dan aman. std::shared_ptr untuk kepemilikan bersama.
  • Memory leak dihindari dengan RAII dan smart pointer; hindari new/delete manual di kode produksi.