Program nyata tidak selalu berjalan mulus — file mungkin tidak ada, koneksi jaringan putus, atau input pengguna tidak valid. Exception handling adalah mekanisme terstruktur untuk menangani kondisi error tanpa membuat kode penuh if-else pengecekan error. C++ memiliki sistem exception yang unik karena tidak membedakan antara checked dan unchecked exception (berbeda dengan Java), dan sangat mengandalkan pola RAII untuk keamanan resource.
Konsep Dasar: try, catch, throw
#include <iostream>
#include <stdexcept>
using namespace std;
double bagi(double a, double b) {
if (b == 0) {
throw runtime_error("Pembagian dengan nol tidak diizinkan!");
}
return a / b;
}
int main() {
try {
cout << bagi(10, 2) << endl; // Output: 5
cout << bagi(10, 0) << endl; // Melempar exception
cout << "Baris ini tidak akan dieksekusi" << endl;
}
catch (const runtime_error& e) {
// e.what() mengembalikan pesan error
cout << "Error: " << e.what() << endl;
// Output: Error: Pembagian dengan nol tidak diizinkan!
}
cout << "Program berlanjut setelah exception ditangani." << endl;
return 0;
}
Hierarki std::exception
C++ menyediakan hierarki exception standar dalam <stdexcept>:
std::exception
├── std::logic_error
│ ├── std::invalid_argument // Argumen tidak valid
│ ├── std::domain_error // Argumen di luar domain matematika
│ ├── std::length_error // Ukuran melebihi batas
│ └── std::out_of_range // Indeks di luar batas
└── std::runtime_error
├── std::range_error // Hasil di luar rentang
├── std::overflow_error // Overflow aritmatika
└── std::underflow_error // Underflow aritmatika
#include <stdexcept>
#include <vector>
void validasi_usia(int usia) {
if (usia < 0 || usia > 150)
throw invalid_argument("Usia harus antara 0 dan 150");
}
void akses_vector(const vector<int>& v, size_t indeks) {
if (indeks >= v.size())
throw out_of_range("Indeks " + to_string(indeks) + " di luar batas");
cout << v[indeks] << endl;
}
int main() {
try {
validasi_usia(-5);
}
catch (const invalid_argument& e) {
cout << "Argumen tidak valid: " << e.what() << endl;
// Output: Argumen tidak valid: Usia harus antara 0 dan 150
}
vector<int> data = {10, 20, 30};
try {
akses_vector(data, 10);
}
catch (const out_of_range& e) {
cout << "Out of range: " << e.what() << endl;
// Output: Out of range: Indeks 10 di luar batas
}
return 0;
}
Menangkap Beberapa Exception
void proses_data(const string& input) {
if (input.empty())
throw invalid_argument("Input tidak boleh kosong");
if (input.length() > 100)
throw length_error("Input terlalu panjang (max 100 karakter)");
if (input == "error")
throw runtime_error("Data tidak dapat diproses");
cout << "Data berhasil diproses: " << input << endl;
}
int main() {
vector<string> test_cases = { "", "hello", string(101, 'x'), "error" };
for (const string& tc : test_cases) {
try {
proses_data(tc);
}
catch (const invalid_argument& e) {
cout << "[invalid_argument] " << e.what() << endl;
}
catch (const length_error& e) {
cout << "[length_error] " << e.what() << endl;
}
catch (const exception& e) { // Catch semua exception dari std
cout << "[exception] " << e.what() << endl;
}
catch (...) { // Catch SEMUA exception (termasuk non-std)
cout << "Exception tidak dikenal!" << endl;
}
}
return 0;
}
// Output:
// [invalid_argument] Input tidak boleh kosong
// Data berhasil diproses: hello
// [length_error] Input terlalu panjang (max 100 karakter)
// [exception] Data tidak dapat diproses
Urutan catch penting! Tangkap exception yang lebih spesifik terlebih dahulu. Jika
catch(const exception&)ditulis sebelumcatch(const invalid_argument&), exceptioninvalid_argumentakan ditangkap oleh blok pertama karenainvalid_argumentadalah turunan dariexception.
Custom Exception Class
#include <stdexcept>
#include <string>
class KesalahanValidasi : public std::runtime_error {
private:
std::string field;
int kode;
public:
KesalahanValidasi(const std::string& field, const std::string& pesan, int kode = 400)
: std::runtime_error(pesan), field(field), kode(kode) {}
std::string getField() const { return field; }
int getKode() const { return kode; }
};
class KesalahanOtorisasi : public std::runtime_error {
public:
explicit KesalahanOtorisasi(const std::string& aksi)
: std::runtime_error("Tidak memiliki izin untuk: " + aksi) {}
};
struct FormRegistrasi {
std::string username;
std::string email;
std::string password;
};
void validasi_registrasi(const FormRegistrasi& form) {
if (form.username.length() < 3)
throw KesalahanValidasi("username", "Username minimal 3 karakter");
if (form.email.find('@') == std::string::npos)
throw KesalahanValidasi("email", "Format email tidak valid");
if (form.password.length() < 8)
throw KesalahanValidasi("password", "Password minimal 8 karakter");
}
int main() {
FormRegistrasi form = { "AB", "bukan_email", "123" };
try {
validasi_registrasi(form);
}
catch (const KesalahanValidasi& e) {
std::cout << "Validasi gagal pada field '" << e.getField()
<< "': " << e.what() << std::endl;
// Output: Validasi gagal pada field 'username': Username minimal 3 karakter
}
return 0;
}
noexcept — Berjanji Tidak Melempar Exception
// noexcept: memberi tahu compiler fungsi ini tidak akan melempar exception
double hitung_luas(double r) noexcept {
return 3.14159 * r * r;
}
// Move constructor/assignment biasanya noexcept
class Buffer {
char* data;
size_t ukuran;
public:
Buffer(Buffer&& lain) noexcept // Move constructor
: data(lain.data), ukuran(lain.ukuran) {
lain.data = nullptr;
lain.ukuran = 0;
}
// ...
};
RAII dan Exception Safety
RAII (Resource Acquisition Is Initialization) adalah pola yang menjamin resource dibersihkan bahkan saat exception:
#include <fstream>
#include <iostream>
// ✅ RAII — fstream otomatis ditutup saat scope berakhir
void tulis_log_aman(const string& pesan) {
ofstream file("log.txt", ios::app); // Dibuka di constructor
if (!file.is_open())
throw runtime_error("Tidak bisa membuka log.txt");
file << pesan << "\n"; // Tulis data
// File OTOMATIS ditutup saat 'file' keluar scope (meski ada exception)
// Destructor ofstream memanggil close()
}
// ❌ Tanpa RAII — resource leak saat exception
void tulis_log_berbahaya(const string& pesan) {
FILE* file = fopen("log.txt", "a");
// Jika ada exception di sini, fclose tidak pernah dipanggil!
fprintf(file, "%s\n", pesan.c_str());
fclose(file); // Mungkin tidak pernah tercapai
}
Pertanyaan yang Sering Diajukan
Mengapa menangkap exception by reference (const exception&) bukan by value?
Menangkap by value menyebabkan slicing — jika kamu melempar invalid_argument dan menangkapnya sebagai exception by value, informasi spesifik dari invalid_argument hilang. Menangkap by const reference menghindari slicing dan penyalinan yang tidak perlu, sekaligus memungkinkan polimorfisme (pemanggilan virtual function what() yang tepat).
Kapan menggunakan exception vs return code (errno, bool, optional)?
Gunakan exception untuk kondisi yang benar-benar luar biasa dan tidak diharapkan terjadi dalam alur normal — file tidak ada, network error, validasi yang seharusnya sudah divalidasi sebelumnya. Gunakan return code atau std::optional untuk kondisi yang sering terjadi dan merupakan bagian dari alur normal — pencarian yang tidak menemukan hasil, konversi yang mungkin gagal.
Apa itu stack unwinding?
Saat exception dilempar, C++ secara otomatis menghancurkan semua objek lokal (destruktor dipanggil) dari titik throw mundur ke catch yang sesuai. Ini disebut stack unwinding dan inilah mengapa RAII bekerja — destruktor selalu dipanggil meski ada exception, menjamin resource selalu dibersihkan.
Apakah try/catch berdampak pada performa?
Blok try sendiri hampir tidak ada overhead di hardware modern — overhead terjadi hanya saat exception benar-benar dilempar. Itulah mengapa exception harus digunakan untuk kondisi yang benar-benar exceptional, bukan untuk flow control normal. Jangan gunakan exception untuk logika “kembalikan -1 jika tidak ditemukan”.
Kesimpulan
| Konsep | C++ | C# | Java |
|---|---|---|---|
| Syntax | try/catch/throw | try/catch/finally | try/catch/finally |
| Catch all | catch(...) | catch(Exception e) | catch(Exception e) |
| Resource cleanup | RAII (destructor) | using statement | try-with-resources |
| Checked exception | Tidak ada | Tidak ada | Ada (unik Java) |
| Custom exception | Extends std::exception | Extends Exception | Extends Exception |
| noexcept | Ada | Tidak ada | throws deklarasi |
Artikel sebelumnya: Inheritance dan Polymorphism di C++ — hierarki class di C++.
Langkah selanjutnya: File I/O di C++ — cara membaca dan menulis file menggunakan stream C++.