Setiap variabel yang kita deklarasikan harus tinggal di suatu tempat di memori, dan di C++ kita diberi dua wilayah utama untuk itu: stack dan heap. Stack adalah wilayah yang dikelola otomatis oleh compiler — cepat, rapi, tetapi ukuran dan umurnya terikat pada blok kode tempat variabel berada. Heap adalah wilayah yang fleksibel: kita bisa memesan memori sebesar apa pun kapan pun, tetapi pembongkarannya menjadi tanggung jawab kita atau pustaka yang kita pakai. Hampir semua topik pada Section ini — new/delete, memory leak, RAII, dan smart pointer — pada akhirnya bermuara pada perbedaan dua wilayah ini. Mari kita mulai dengan memahami karakter keduanya secara utuh.

Stack: Memori yang Dikelola Otomatis #

Stack adalah wilayah memori yang bekerja dengan pola LIFO (last in, first out): yang terakhir masuk, yang pertama keluar. Setiap kali sebuah function dipanggil, runtime menyisipkan satu stack frame di ujung stack — di dalamnya ada parameter, variabel lokal, dan alamat kembali. Begitu function kembali, seluruh frame-nya dicabut sekaligus, dan semua variabel lokal di dalamnya berakhir secara otomatis. Karena itulah kita tidak pernah perlu “melepas” variabel lokal di C++.

  • Otomatis: pembuatan dan pembongkaran variabel lokal ditangani compiler tanpa satu baris kode pun dari kita.
  • Sangat cepat: alokasi hanya berarti menggeser satu penunjuk ujung stack; tidak ada pencarian ruang kosong.
  • Terbatas: ukuran stack kecil dibanding heap — pada Linux umumnya sekitar 8 MB per thread. Rekursi yang terlalu dalam bisa melebihi kapasitasnya dan menghentikan program.

Heap: Memori yang Kita Kelola Sendiri #

Heap adalah wilayah memori bebas yang jauh lebih luas, dikelola oleh alokator memori milik runtime C++. Ketika kita meminta ruang — lewat operator new, lewat smart pointer, atau lewat container seperti std::vector — alokator mencari blok kosong yang cukup, mencatatnya di pembukuan internal, lalu menyerahkan alamatnya kepada kita. Umur memori heap tidak terikat pada blok mana pun: objek yang dialokasikan di heap tetap hidup sampai secara eksplisit dilepas. Fleksibilitas ini berbayar: alokasi lebih lambat daripada stack karena ada pencarian blok dan pembukuan yang harus dikelola, dan kalau kita lupa melepas, memorinya bocor tanpa bunyi.

Diagram di atas memperlihatkan hubungan keduanya: objek std::vector sendiri berada di stack karena ia variabel lokal, tetapi elemen-elemen yang ia simpan berada di heap. Vector adalah contoh sempurna pembagian kerja ini — kita memakai kemudahan stack untuk objek pengelolanya, sementara vector yang berperan mengelola heap untuk kita.

Membuktikan Dua Wilayah dalam Satu Program #

Program berikut menempatkan satu variabel biasa di stack dan satu vector di atas pasangan stack-heap. Output-nya sederhana, tetapi mekanismenya adalah ringkasan dari dua wilayah yang baru kita bahas.

cpp
#include <iostream>
#include <vector>

int main() {
    // Wilayah 1: stack — variabel lokal, dikelola otomatis
    int angka = 42;
    std::cout << "angka (di stack): " << angka << "\n";

    // Wilayah 2: heap — objek vector di stack, elemennya di heap
    std::vector<int> data = {10, 20, 30};
    data.push_back(40);   // elemen baru dialokasikan di heap oleh vector

    std::cout << "elemen (di heap):";
    for (int x : data) {
        std::cout << " " << x;
    }
    std::cout << "\n";
    std::cout << "jumlah elemen    : " << data.size() << "\n";

    // Setelah main() selesai: vector membebaskan elemen heap-nya sendiri,
    // dan angka ikut berakhir bersama blok fungsi.
    return 0;
}

Output yang Dihasilkan:

text
angka (di stack): 42
elemen (di heap): 10 20 30 40
jumlah elemen    : 4

Mari kita telaah baris-baris pentingnya:

  1. Baris 6 (int angka = 42;): angka adalah variabel lokal main(), sehingga ia disimpan di stack frame milik main(). Tidak ada kode pelepasan — ia berakhir otomatis.
  2. Baris 10 (std::vector<int> data = {10, 20, 30};): objek data berada di stack, tetapi vector langsung memesan ruang di heap untuk menampung ketiga elemennya.
  3. Baris 11 (data.push_back(40);): vector meminta alokasi tambahan di heap saat kapasitasnya tidak cukup. Kita tidak melihat satu new pun — vector yang melakukannya untuk kita.
  4. Baris 13-17 (loop cetak): membaca elemen berarti membaca memori heap lewat antarmuka yang aman dari vector.
  5. Baris 22 (return 0;): saat main() berakhir, destructor vector dijalankan dan seluruh elemen heap-nya dibebaskan otomatis.

Kapan Memakai yang Mana #

Aturan praktisnya dapat diringkas dalam tabel berikut:

KebutuhanWilayahAlat yang tepat
Nilai kecil berumur sesuai blokStackVariabel lokal biasa
Data besar atau ukurannya baru tahu saat runtimeHeapstd::vector, std::string
Objek yang harus hidup melebihi scope-nyaHeapSmart pointer (Chapter unique_ptr dan setelahnya)
Data milik bersama beberapa pemilikHeapstd::shared_ptr

Peta dua wilayah ini masih setengah cerita: kapan tepatnya sebuah variabel “lahir” dan “berakhir” ditentukan oleh storage duration. Chapter berikutnya membandingkan automatic storage dan dynamic storage — dua istilah standar untuk umur memori yang sudah kita rasakan di atas.

Referensi: cppreference — std::vector , cppreference — storage duration .