Episode ini membahas penanganan error modern: exceptions dengan throw dan catch, stack unwinding, jenis exception standar dan custom, debugging dengan GDB dan sanitizers, serta static analysis dengan clang-tidy dan cppcheck.

Program di dunia nyata akan gagal: file tidak ada, jaringan putus, input salah. Cara kalian menangani kegagalan menentukan apakah program tetap bisa diandalkan atau berhenti diam-diam. C++ menyediakan dua pendekatan: return code ala C dan exception yang lebih modern.
Episode 11 membahas penanganan error secara menyeluruh — throw, try, dan catch, bagaimana stack unwinding bekerja, jenis exception standar, pembuatan exception custom, debugging dengan GDB dan sanitizers, serta static analysis dengan clang-tidy dan cppcheck yang menangkap bug sebelum program dijalankan.
Ketika terjadi kondisi tidak normal, kode melempar exception dengan throw. Kode yang berpotensi gagal dibungkus try, dan penanganan dilakukan di catch. Eksekusi melompat langsung ke catch yang cocok — kode di antara keduanya dilewati:
cat > exc.cpp <<'EOF'
#include <iostream>
#include <stdexcept>
double bagi(double a, double b) {
if (b == 0.0) {
throw std::runtime_error("pembagian dengan nol");
}
return a / b;
}
int main() {
try {
std::cout << bagi(10, 0) << "\n";
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
}
}
EOF
g++ -std=c++20 exc.cpp -o exc
./excthrow std::runtime_error("pembagian dengan nol") melempar exception dengan pesan, dan catch (const std::exception& e) menangkapnya. e.what() mengembalikan pesan error. Multiple catch menangani tipe berbeda; tangkap yang lebih spesifik sebelum yang lebih umum. Selalu lempar exception by value dan tangkap by const reference — melempar pointer tidak membawa informasi tipe dan rawan bocor.
Saat exception dilempar, semua objek di stack antara titik lempar dan titik tangkap dihancurkan — ini disebut stack unwinding. Destructor setiap objek tetap dipanggil, sehingga resource yang dipegang RAII dibebaskan dengan benar. Inilah alasan pola RAII dari episode 7 begitu penting: meski terjadi exception, tidak ada kebocoran.
<stdexcept> menyediakan hirarki exception siap pakai yang semuanya turunan std::exception: std::runtime_error untuk error runtime, std::logic_error untuk pelanggaran logika, std::invalid_argument untuk argumen tidak valid, std::out_of_range untuk akses di luar batas, dan std::bad_alloc yang dilempar saat alokasi memori gagal. Tangkap yang spesifik dulu:
cat > multi.cpp <<'EOF'
#include <iostream>
#include <stdexcept>
#include <vector>
int main() {
std::vector<int> v{1, 2, 3};
try {
std::cout << v.at(10) << "\n";
} catch (const std::out_of_range& e) {
std::cerr << "Di luar batas: " << e.what() << "\n";
} catch (const std::exception& e) {
std::cerr << "Error lain: " << e.what() << "\n";
}
}
EOF
g++ -std=c++20 multi.cpp -o multi
./multiv.at(10) melempar std::out_of_range saat indeks melebihi ukuran — berbeda dari v[10] yang tidak memeriksa batas. Perhatikan urutan: out_of_range ditangkap sebelum std::exception umum.
Untuk error khusus domain, buat exception sendiri dengan menurunkan std::exception dan meng-override what():
cat > custom.cpp <<'EOF'
#include <iostream>
#include <exception>
#include <string>
class SaldoTidakCukup : public std::exception {
public:
explicit SaldoTidakCukup(double saldo)
: pesan_("Saldo tidak cukup, tersedia " +
std::to_string(saldo)) {}
const char* what() const noexcept override {
return pesan_.c_str();
}
private:
std::string pesan_;
};
int main() {
try {
throw SaldoTidakCukup(10.5);
} catch (const SaldoTidakCukup& e) {
std::cerr << e.what() << "\n";
}
}
EOF
g++ -std=c++20 custom.cpp -o custom
./customSaldoTidakCukup(10.5) membawa data konteks — saldo — dan menyusun pesan yang informatif. what() ditandai noexcept sesuai kontrak standar.
GDB adalah debugger baris perintah untuk Linux. Kalian bisa menetapkan breakpoint, menjalankan baris per baris, dan memeriksa nilai variabel. Build dengan -g agar simbol debug tersimpan:
cat > bug.cpp <<'EOF'
#include <iostream>
int main() {
int total = 0;
for (int i = 1; i <= 5; ++i) {
total += i;
}
std::cout << "Total: " << total << "\n";
}
EOF
g++ -g -std=c++20 bug.cpp -o bug
gdb -batch -ex 'break main' -ex 'run' -ex 'next' -ex 'print total' ./bugPerintah gdb -batch -ex 'break main' -ex 'run' -ex 'print total' ./bug menjalankan GDB tanpa mode interaktif: berhenti di main, menjalankan, lalu mencetak variabel total. Bendera -g saat kompilasi menyimpan informasi debug.
AddressSanitizer mendeteksi use-after-free, buffer overflow, dan kebocoran memori. Aktifkan lewat bendera compiler, lalu jalankan seperti biasa:
g++ -fsanitize=address -g -std=c++20 cek.cpp -o cek
./cek-fsanitize=address memasang pemeriksaan memori saat runtime. Jika program menyentuh memori tidak valid, ASan melaporkan lokasi persisnya dengan stack trace. Ini alat paling efektif untuk mengejar bug memori sebelum dipakai di produksi.
Static analysis memeriksa kode tanpa menjalankannya. clang-tidy memberikan peringatan gaya dan bug potensial, sedangkan cppcheck mendeteksi masalah seperti use-after-return dan memori bocor. Keduanya adalah bagian penting dari pipeline kualitas:
clang-tidy bug.cpp -- -std=c++20
cppcheck --enable=all --std=c++20 bug.cppPerintah clang-tidy bug.cpp -- -std=c++20 menganalisis file dengan opsi kompilasi yang sama. cppcheck --enable=all mengaktifkan semua pemeriksaan. Biasakan menjalankan keduanya di CI — episode 19 akan mengotomatiskan ini.
Tip
-Wall -Wextra untuk peringatan kompilasi, sanitizer untuk error runtime, dan clang-tidy atau cppcheck untuk analisis statis.Inti yang harus dibawa pulang:
throw melempar exception, try dan catch menanganinya.std::exception umum.std::exception dan override what().-g untuk GDB dan -fsanitize=address untuk deteksi bug memori.Di episode 12 selanjutnya kita akan membahas concurrency dan multithreading — std::thread, std::mutex, dan std::lock_guard, condition variables, std::future dan std::promise beserta std::async, thread safety dengan data race dan deadlock, serta pembuatan thread pools untuk beban kerja paralel.