Composition menyusun class dari bagian-bagian yang dimilikinya; inheritance mengambil jalan yang berbeda — class baru mewarisi class yang sudah ada. class SepedaMotor : public Kendaraan menyatakan bahwa sepeda motor adalah kendaraan: ia otomatis membawa data dan method milik Kendaraan, lalu menambahkan kemampuan sendiri di atasnya. Hubungan “adalah” (is-a) ini adalah inti dari inheritance, dan bila dipakai pada relasi yang memang tepat, kita menulis jauh lebih sedikit kode tanpa menyalin apa pun. Chapter ini membahas sintaksnya, apa saja yang benar-benar diwariskan, dan urutan konstruksi yang menambahkan satu langkah baru di depan yang sudah kita pelajari dari composition.
Sintaks dan Hubungan is-a #
Sintaks inheritance ditulis pada deklarasi class turunan: class SepedaMotor : public Kendaraan { ... };. Nama dasar dicantumkan setelah titik dua, diawali access specifier public — kata ini mengatur seberapa terbuka member dasar bagi luar, dan untuk sekarang anggap saja public sebagai bentuk standar; variasinya kita kupas tuntas pada Chapter Access Control pada Inheritance. Satu class turunan membawa satu subtipe: di mana pun kode menerima Kendaraan, kita boleh menyerahkan SepedaMotor, karena hubungan is-a membuat keduanya kompatibel.
Istilah yang dipakai: Kendaraan disebut base class (class dasar), sedangkan SepedaMotor disebut derived class (class turunan). Aturan paling penting sebelum menulis titik dua: pastikan kalimat “turunan adalah dasar” benar secara logika. Sepeda motor adalah kendaraan — sah. Mobil adalah mesin — salah, itu komposisi. Kesalahan memilih hubungan di sini adalah salah satu bug desain yang paling sulit diangkat belakangan.
Apa yang Diwariskan #
Class turunan menerima hampir seluruh isi dasar: semua data member dan semua member function menjadi bagian dari dirinya, seolah ditulis ulang di dalam tubuhnya. Pada contoh di bawah, SepedaMotor tidak mendeklarasikan merek, kecepatan, atau info() — tetapi setiap object motor memilikinya dan dapat memakainya persis seperti milik sendiri. Method warisan juga bisa dipanggil dari method turunan: infoLengkap() memanggil info() tanpa perantara apa pun.
Tiga hal yang tidak ikut diwariskan perlu diingat. Pertama, constructor dan destructor tidak diwariskan — setiap class mengelola pembangunan dan pembongkaran dirinya sendiri, meski selalu melibatkan versi dasarnya seperti yang kita lihat sebentar lagi. Kedua, member private dasar tetap ada di dalam object turunan, tetapi tidak bisa diakses langsung oleh kode turunan; itu wilayah terkunci milik dasar, dan protected di sini adalah kunci tengahnya: terlihat bagi turunan, tertutup bagi luar. Ketiga, deklarasi friend tidak mewarisi izinnya — teman dasar bukan otomatis teman turunan.
Konstruksi Dasar Dulu #
Object turunan selalu tersusun dari dua bagian: subtipe dasar di dalamnya, ditambah member khas turunannya. Compiler membangunnya dengan urutan tetap — constructor dasar dieksekusi lebih dulu, baru badan constructor turunan. Logikanya wajar: method dan data warisan harus siap sebelum kode turunan mulai memakainya, sama seperti bagian komposisi dibangun sebelum tuannya, tetapi kali ini bagian itu disebut dengan namanya sendiri di member initializer list.
Bila constructor turunan tidak mencantumkan dasar di initializer list-nya, compiler memanggil default constructor dasar secara otomatis. Bila dasar tidak punya default constructor, kompilasi gagal. Karena itu, constructor dasar berparameter biasanya dipanggil eksplisit: SepedaMotor(const std::string& m, int k) : Kendaraan(m, k). Lihat program lengkapnya — jejak cetak akan membuktikan urutan yang kita klaim:
#include <iostream>
#include <string>
class Kendaraan {
protected:
std::string merek;
int kecepatan = 0;
public:
Kendaraan(const std::string& m, int k) : merek(m), kecepatan(k) {
std::cout << "[Dasar] Kendaraan " << m << " dibangun\n";
}
void info() const {
std::cout << merek << " melaju " << kecepatan << " km/jam\n";
}
};
class SepedaMotor : public Kendaraan {
private:
int gigi = 1;
public:
SepedaMotor(const std::string& m, int k) : Kendaraan(m, k) {
std::cout << "[Turunan] SepedaMotor " << m << " siap\n";
}
void naikGigi() {
++gigi;
kecepatan += 15; // member protected dari dasar: boleh diubah di sini
}
void infoLengkap() const {
info(); // method warisan tetap terpakai tanpa ditulis ulang
std::cout << " gigi saat ini: " << gigi << "\n";
}
};
int main() {
SepedaMotor motor("Satria", 20);
motor.infoLengkap();
motor.naikGigi();
motor.infoLengkap();
return 0;
}Output yang Dihasilkan:
[Dasar] Kendaraan Satria dibangun
[Turunan] SepedaMotor Satria siap
Satria melaju 20 km/jam
gigi saat ini: 1
Satria melaju 35 km/jam
gigi saat ini: 2Mari kita bedah:
- Baris 4-15 (
class Kendaraan): class dasar dengan datamerekdankecepatandi bagianprotected— terbaca dan boleh diubah oleh class turunan, tetapi terkunci dari luar.info()di bagianpublicadalah antarmuka warisan. - Baris 17 (
class SepedaMotor : public Kendaraan): satu baris ini membuatSepedaMotormewarisi seluruh memberKendaraan. Tanpa menulis satu kode pun, motor sudah punyamerek,kecepatan, daninfo(). - Baris 21 (
: Kendaraan(m, k)): constructor turunan meneruskan argumennya ke constructor dasar. Baris pertama output membuktikanKendaraan(m, k)dieksekusi dulu — “[Dasar]” tercetak sebelum “[Turunan]”. - Baris 26 (
kecepatan += 15): method turunan mengubah memberprotectedmilik dasar seolah miliknya sendiri. Bila member ini dibuatprivate, baris ini gagal dengan erroris private within this context. - Baris 29 (
info()): pemanggilan method warisan dari dalam method turunan — tidak ada salinan kode, tidak ada delegasi manual;info()memang sudah ada di dalam setiapSepedaMotor. - Baris 35-38 (di
main()): dari luar,motor.infoLengkap()bekerja mulus menampilkan data warisan dan data baru. Output baris kedua dan kelima menunjukkankecepatanikut naik setelahnaikGigi()— state warisan hidup dan berubah di dalam object turunan.
Bila kita coba menyentuh motor.kecepatan = 0; dari dalam main(), compiler menolaknya dengan jelas:
error: 'int Kendaraan::kecepatan' is protected within this context
note: declared protected here
int kecepatan = 0;Inheritance Bukan Salinan #
Satu pembeda akhir yang membuat inheritance lebih dari sekadar penghematan ketikan: perubahan pada dasar otomatis mengalir ke semua turunannya. Perbaiki info() di Kendaraan, maka SepedaMotor dan semua class lain yang mewarisinya ikut berubah tanpa disentuh. Inilah alasan inheritance dipakai untuk hubungan yang memang stabil dan seumur hidup — dasar dan turunan terikat erat, dan ikatan itu harus dirancang, bukan terjadi karena kebetulan.
Dengan hubungan is-a yang terjalin, pertanyaan berikutnya muncul: seberapa terbuka member dasar ketika diwariskan? Chapter berikutnya membahas access control pada inheritance — apa yang terjadi pada member public dan protected ketika kita mewarisi lewat public, protected, atau private.
Rujukan resmi: Derived classes (cppreference) , Initialization of base classes (cppreference) .