Pada Chapter Inheritance kita selalu menulis : public dan melupakan sisanya. Padahal kata di titik dua itu adalah access specifier yang punya tiga pilihan — public, protected, dan private — dan pilihan tersebut mengubah status akses seluruh member dasar di dalam turunan. Inheritance public menjaga antarmuka dasar tetap utuh; inheritance protected menyembunyikannya dari luar; inheritance private menutupnya total, menjadikan dasar sekadar “bahan mentah” internal. Chapter ini memetakan tiga kombinasi tersebut, menunjukkan efeknya lewat kode yang diuji, dan menuntun kita memilih kapan masing-masing layak dipakai.
Aturan Dasarnya Sederhana #
Mekanismenya bisa diringkas satu kalimat: saat mewarisi, status akses setiap member dasar “diturunkan” paling rendah sampai tingkat inheritance yang dipilih. Member public yang diwarisi lewat private menjadi private di turunan; member protected yang diwarisi lewat public tetap protected. Kompasnya: akses final = akses asli yang dibatasi oleh mode inheritance, dan private dasar selalu tak terlihat di turunan apa pun mode-nya.
Tabel berikut merangkum sembilan kombinasi yang ada:
| Akses di class dasar | Diwarisi via public | Diwarisi via protected | Diwarisi via private |
|---|---|---|---|
public | public | protected | private |
protected | protected | protected | private |
private | tidak terlihat | tidak diwariskan aksesnya | tidak diwariskan aksesnya |
Dua hal menonjol dari tabel itu. Pertama, kolom public hanya memindahkan status apa adanya — tidak ada yang naik atau turun; inilah kenapa inheritance public adalah bentuk default untuk hubungan is-a. Kedua, baris terakhir: member private dasar tetap ada di dalam object turunan (memorinya ikut dibangun), tetapi kode turunan tidak dapat menyentuhnya langsung — hanya method dasar yang berhak.
Contoh: Tiga Mode dalam Satu Program #
Program berikut mendefinisikan satu class dasar dengan member private, protected, dan public, lalu mewarisinya dalam tiga mode berbeda agar kita melihat efek nyatanya:
#include <iostream>
class MesinCetak {
private:
int suhuInternal = 60; // private: tak pernah terlihat oleh turunan
protected:
int jumlahCetak = 0; // protected: bebas diakses di dalam turunan
public:
void tampilStatus() const {
std::cout << "jumlah cetak: " << jumlahCetak
<< ", suhu: " << suhuInternal << "\n";
}
};
// Inheritance public: akses member dasar tetap seperti aslinya
class PrinterToko : public MesinCetak {
public:
void cetak() {
++jumlahCetak; // OK: protected bisa diubah dari dalam turunan
std::cout << "mencetak dokumen ke-" << jumlahCetak << "\n";
}
};
// Inheritance protected: public dasar turun menjadi protected
class MesinSewa : protected MesinCetak {
public:
void servis() {
++jumlahCetak;
tampilStatus(); // di dalam class: tetap boleh
}
};
// Inheritance private: seluruh member dasar menjadi private
class MesinOEM : private MesinCetak {
public:
using MesinCetak::tampilStatus; // using declaration: buka kembali satu member
void produksi() {
++jumlahCetak;
}
};
int main() {
PrinterToko printer;
printer.tampilStatus(); // OK: public dasar tetap public di turunan
printer.cetak();
MesinSewa sewa;
sewa.servis(); // di dalam: akses penuh ke member dasar
MesinOEM oem;
oem.tampilStatus(); // OK: hanya karena dibuka lewat using declaration
oem.produksi();
return 0;
}Output yang Dihasilkan:
jumlah cetak: 0, suhu: 60
mencetak dokumen ke-1
jumlah cetak: 1, suhu: 60
jumlah cetak: 0, suhu: 60Mari kita bedah:
- Baris 5-7 (tiga tingkat akses):
suhuInternalprivate,jumlahCetakprotected,tampilStatus()public — tiga tingkat yang akan kita lacak pergerakannya ke setiap turunan. - Baris 16 (
: public MesinCetak):PrinterTokomewarisi apa adanya. Di dalamcetak(),jumlahCetakmasih protected (baris 19 sah); darimain(),printer.tampilStatus()tetap public (baris 44 sah). - Baris 25 (
: protected MesinCetak):MesinSewamenurunkan semua member dasar maksimal sampai protected. Methodservis()di baris 27-30 bebas memakai member dasar — batas ini hanya mengikat kode di luar class. - Baris 34 (
: private MesinCetak):MesinOEMmenutup semuanya. Meski begitu badan class masih bebas memakai warisan:produksi()di baris 37-39 mengubahjumlahCetaktanpa keluhan compiler. - Baris 36 (
using MesinCetak::tampilStatus;): using declaration membuka satu member tertentu kembali ke public. Ini kunci praktis inheritance private — sembunyikan semua, lalu izinkan yang memang diperlukan, tanpa membuka sisanya. - Baris 51 (
oem.tampilStatus()): satu-satunya pemanggilan luar yang berhasil padaMesinOEM— bukan karena mode private memaafkan, melainkan karena member itu sudah dibuka eksplisit.
Dua percobaan yang gagal membuat batasnya lebih tegas. Memanggil sewa.tampilStatus() dari main() (mode protected) ditolak compiler:
error: 'void MesinCetak::tampilStatus() const' is inaccessible within this context
note: declared here
void tampilStatus() const { std::cout << "x\n"; }Dan dari dalam turunan mana pun, member private dasar tetap tak tersentuh — ++suhuInternal di dalam PrinterToko juga ditolak:
error: 'int MesinCetak::suhuInternal' is private within this context
note: declared private here
int suhuInternal = 60;Kapan Memakai Masing-Masing #
Inheritance public adalah pilihan untuk hubungan is-a yang sejati: SepedaMotor adalah Kendaraan, maka semua yang bisa dilakukan kendaraan harus bisa dilakukan motor. Aturan praktisnya kuat — default-kan public dan berhentilah memikirkannya, kecuali ada alasan desain yang tegas untuk pilihan lain.
Inheritance private menyatakan “implemented in terms of”: turunan memakai dasar sebagai mesin internal, tetapi menolak dipandang sebagai jenis dari dasar. Kasus khasnya adalah memakai class utilitas yang member-nya nyaman dipanggil dari method sendiri, sementara antarmuka luar yang kita tawarkan benar-benar lain — dengan using untuk menyeleksi member yang boleh lolos. Untuk kebutuhan setipe ini, composition biasanya lebih ringan dibaca; pemakaian inheritance private pun menyusut dan kini umumnya terbatas pada pola-pola lanjutan di bagian akhir course ini.
Inheritance protected adalah mode paling langka: berguna ketika turunan kita sendiri masih akan diwarisi lagi oleh class ketiga, dan kita ingin member dasar sampai ke cucu tetapi tidak ke dunia luar. Kalau pola itu tidak ada dalam desainmu, kembalilah ke public atau private.
Dengan peta akses ini utuh, kita siap melangkah ke situasi yang lebih menantang: satu class yang mewarisi dua dasar sekaligus — multiple inheritance — beserta jebakan klasiknya, diamond problem.
Rujukan resmi: Derived classes (cppreference) , Using declaration (cppreference) .