Chapter Stack dan Heap menunjuk heap sebagai wilayah fleksibel, dan sekarang saatnya kita memegang tombolnya secara langsung. Operator new memesan ruang di heap untuk satu objek, menjalankan constructor-nya, lalu mengembalikan pointer bertipe ke objek yang baru itu. Operator delete melakukan kebalikannya: menjalankan destructor, mengembalikan memori ke alokator, dan menghabiskan umur objek tersebut. Dua operator ini adalah pasangan wajib — setiap new berhak atas tepat satu delete, dan setiap kelalaian di salah satu sisinya menimbulkan masalah yang akan kita buktikan di chapter-chapter berikutnya.

Mekanisme new dan delete #

Sintaksnya sengaja dibuat ringkas: new T(...) mengalokasikan dan meng-inisialisasi sekaligus, sedangkan delete p; melepas. Perhatikan bahwa new mengembalikan pointer — bukan nilai — karena objeknya berada di alamat yang tidak kita ketahui sebelumnya. Objek heap tidak terikat blok: ia tetap hidup melintasi scope, menunggu delete yang satu-satunya bisa mengakhirinya. Dengan itu pula, destructor-nya tidak dijalankan di akhir blok, melainkan tepat pada baris delete dieksekusi.

cpp
#include <iostream>
#include <string>

class Profil {
public:
    explicit Profil(const std::string& nama) : nama_(nama) {
        std::cout << "Profil " << nama_ << " diciptakan\n";
    }
    ~Profil() {
        std::cout << "Profil " << nama_ << " dibebaskan\n";
    }
    void sapa() const {
        std::cout << "Halo, " << nama_ << "!\n";
    }
private:
    std::string nama_;
};

int main() {
    int* angka = new int(7);          // alokasi satu int di heap
    std::cout << "Nilai heap: " << *angka << "\n";
    *angka = *angka * 3;
    std::cout << "Setelah dikali: " << *angka << "\n";
    delete angka;                     // wajib: satu new, satu delete
    angka = nullptr;

    Profil* profil = new Profil("Budi");
    profil->sapa();                   // akses anggota lewat operator ->
    delete profil;                    // destructor Profil berjalan di sini
    profil = nullptr;

    return 0;
}

Output yang Dihasilkan:

text
Nilai heap: 7
Setelah dikali: 21
Profil Budi diciptakan
Halo, Budi!
Profil Budi dibebaskan

Mari kita bedah mekanismenya baris demi baris:

  1. Baris 20 (int* angka = new int(7);): new int(7) memesan 4 byte di heap, mengisinya dengan 7, dan mengembalikan int* yang kita simpan di angka.
  2. Baris 21-23: *angka mendereferensikan pointer sehingga kita membaca dan mengubah nilai heap layaknya variabel biasa.
  3. Baris 24 (delete angka;): memori dikembalikan ke alokator. Baris berikutnya, angka = nullptr;, menandai dengan jelas bahwa pointer itu kini kosong — pola pencegahan dari Chapter Dangling Pointer.
  4. Baris 27-28 (new Profil("Budi") … ->): objek class di heap diakses lewat operator ->; constructor-nya berjalan saat new, dan terbukti dari urutan output.
  5. Baris 29 (delete profil;): destructor Profil berjalan TEPAT di baris ini — bukan di akhir main(). Itulah perbedaan umur objek heap versus objek otomatis.

Double Delete: Melepas yang Sudah Dilepas #

delete hanya boleh dipanggil satu kali per alokasi. Memanggilnya dua kali disebut double delete, dan hasilnya adalah undefined behavior: alokator menerima perintah melepas memori yang sudah tidak lagi milik kita. Kondisi pembukuan heap bisa terkorup, dua objek lain bisa mendapat alamat yang sama, dan program bisa “seolah berjalan normal” sebelum rusak di tempat yang jauh dari lokasi kejadiannya:

cpp Kode bermasalah — JANGAN dijalankan tanpa sanitizer
int* p = new int(5);
delete p;
delete p;   // UNDEFINED BEHAVIOR: memori yang sama dilepas dua kali

AddressSanitizer yang terpasang di g++ 14.2 bisa menangkap kesalahan ini secara nyata. Program kecil di atas dikompilasi dengan -fsanitize=address, dan sanitizer langsung menghentikan eksekusinya pada delete kedua:

text Contoh output AddressSanitizer (alamat memori bervariasi)
=================================================================
==19993==ERROR: AddressSanitizer: attempting double-free on 0x502000000010 in thread T0:
    #0 0x7f4ab44c0678 in operator delete(void*, unsigned long)
    #1 0x5566265962d6 in main

0x502000000010 is located 0 bytes inside of 4-byte region [0x502000000010,0x502000000014)
freed by thread T0 here:
    #0 0x7f4ab44c0678 in operator delete(void*, unsigned long)
    #1 0x5566265962c0 in main

SUMMARY: AddressSanitizer: double-free in operator delete(void*, unsigned long)

Laporan di atas menunjukkan tiga fakta sekaligus: alamat yang di-delete dua kali, jejak pembebasan pertama, dan jejak alokasi semula. Alamat dan angka prosesnya berbeda di setiap mesin, tetapi polanya selalu sama — dan pola inilah yang akan kita cari saat debugging. Pada chapter Memory Leaks kita memakai sanitizer yang sama untuk kasus kebalikannya: memori yang tidak pernah dilepas.

Bila Alokasi Gagal #

Heap bukan wilayah tanpa batas. Bila permintaan alokasi tidak bisa dipenuhi — ukurannya terlalu besar atau memori benar-benar habis — new tidak mengembalikan nullptr, melainkan melempar exception bertipe std::bad_alloc. Mekanisme exception sendiri akan kita gunakan pada Chapter Exception-Safe Resource Management dan dibahas lengkap pada Section Error Handling; untuk saat ini cukup dicatat bahwa program yang tidak menangani std::bad_alloc akan berhenti dengan pesan terminate. Ada pula varian new(std::nothrow) yang mengembalikan nullptr alih-alih melempar, tetapi di kode aplikasi modern kita jarang memakainya.

new dan delete tunggal menyelesaikan satu objek, tetapi kebutuhan nyata sering berupa serangkaian objek dengan jumlah yang baru diketahui saat runtime. Chapter berikutnya memperluas pasangan ini menjadi new[] dan delete[] — termasuk aturan kurung siku yang tidak boleh tertukar.

Referensi: cppreference — new expression , cppreference — delete expression , cppreference — std::bad_alloc .