Langsung ke konten
KamusNgoding
Menengah Cpp 3 menit baca

Inheritance dan Polymorphism di C++

#cpp #inheritance #polymorphism #virtual #abstract #override #oop #vtable

Setelah memahami class dasar, saatnya naik ke level berikutnya — inheritance (pewarisan) memungkinkan class baru mewarisi atribut dan method dari class yang sudah ada, sementara polymorphism memungkinkan satu antarmuka bekerja dengan banyak tipe objek berbeda. Ini adalah dua pilar OOP yang membuat kode bisa di-extend tanpa mengubah kode yang sudah ada.

Inheritance Dasar

#include <iostream>
#include <string>
using namespace std;

// Base class (Parent)
class Hewan {
protected: // Protected: bisa diakses oleh class turunan
    string nama;
    int umur;

public:
    Hewan(string n, int u) : nama(n), umur(u) {}

    void makan() {
        cout << nama << " sedang makan." << endl;
    }

    void info() const {
        cout << "Nama: " << nama << " | Umur: " << umur << " tahun" << endl;
    }
};

// Derived class (Child) — menggunakan : public BaseClass
class Anjing : public Hewan {
private:
    string ras;

public:
    Anjing(string n, int u, string r) : Hewan(n, u), ras(r) {}

    void gonggong() {
        cout << nama << " (Anjing " << ras << "): Guk guk!" << endl;
    }
};

class Kucing : public Hewan {
public:
    Kucing(string n, int u) : Hewan(n, u) {}

    void mengeong() {
        cout << nama << " (Kucing): Meong!" << endl;
    }
};

int main() {
    Anjing d("Buddy", 3, "Labrador");
    d.info();     // Mewarisi dari Hewan
    d.makan();    // Mewarisi dari Hewan
    d.gonggong(); // Method sendiri

    Kucing c("Mochi", 2);
    c.info();
    c.mengeong();

    return 0;
}
// Output:
// Nama: Buddy | Umur: 3 tahun
// Buddy sedang makan.
// Buddy (Anjing Labrador): Guk guk!
// Nama: Mochi | Umur: 2 tahun
// Mochi (Kucing): Meong!

Virtual Functions dan Polymorphism

Tanpa virtual, pemanggilan method melalui pointer base class selalu memanggil versi base class:

class Bentuk {
public:
    // virtual: memungkinkan overriding yang polimorfis
    virtual double luas() const {
        return 0.0;
    }

    virtual void info() const {
        cout << "Bentuk tidak dikenal, luas: " << luas() << endl;
    }

    virtual ~Bentuk() {} // Destructor virtual — PENTING saat menggunakan pointer!
};

class Lingkaran : public Bentuk {
private:
    double jari_jari;

public:
    Lingkaran(double r) : jari_jari(r) {}

    double luas() const override { // override: eksplisit menandai overriding
        return 3.14159 * jari_jari * jari_jari;
    }

    void info() const override {
        cout << "Lingkaran r=" << jari_jari << " | Luas: " << luas() << endl;
    }
};

class PersegiPanjang : public Bentuk {
private:
    double panjang, lebar;

public:
    PersegiPanjang(double p, double l) : panjang(p), lebar(l) {}

    double luas() const override {
        return panjang * lebar;
    }

    void info() const override {
        cout << "PersegiPanjang " << panjang << "x" << lebar
             << " | Luas: " << luas() << endl;
    }
};

int main() {
    // Polymorphism: pointer base class menunjuk ke derived class
    Bentuk* bentuk[] = {
        new Lingkaran(5),
        new PersegiPanjang(4, 6),
        new Lingkaran(3)
    };

    double total = 0;
    for (Bentuk* b : bentuk) {
        b->info();        // Memanggil versi yang benar berdasarkan tipe aktual
        total += b->luas();
    }
    cout << "Total luas: " << total << endl;

    // Jangan lupa delete untuk menghindari memory leak
    for (Bentuk* b : bentuk) delete b;

    return 0;
}
// Output:
// Lingkaran r=5 | Luas: 78.5397
// PersegiPanjang 4x6 | Luas: 24
// Lingkaran r=3 | Luas: 28.2743
// Total luas: 130.814

Abstract Class dan Pure Virtual Functions

Class abstract tidak bisa di-instantiasi langsung — hanya bisa menjadi base class:

class Karyawan {
protected:
    string nama;
    double gaji_pokok;

public:
    Karyawan(string n, double g) : nama(n), gaji_pokok(g) {}

    // Pure virtual (= 0): WAJIB diimplementasikan oleh derived class
    virtual double hitung_gaji() const = 0;
    virtual string jabatan() const = 0;

    void cetak_slip() const {
        cout << "=== Slip Gaji ===" << endl;
        cout << "Nama    : " << nama << endl;
        cout << "Jabatan : " << jabatan() << endl;
        cout << "Gaji    : Rp " << hitung_gaji() << endl;
    }

    virtual ~Karyawan() {}
};

class KaryawanTetap : public Karyawan {
private:
    double tunjangan;

public:
    KaryawanTetap(string n, double g, double t)
        : Karyawan(n, g), tunjangan(t) {}

    double hitung_gaji() const override {
        return gaji_pokok + tunjangan;
    }

    string jabatan() const override { return "Karyawan Tetap"; }
};

class Freelancer : public Karyawan {
private:
    int jam_kerja;

public:
    Freelancer(string n, double tarif_per_jam, int jam)
        : Karyawan(n, tarif_per_jam), jam_kerja(jam) {}

    double hitung_gaji() const override {
        return gaji_pokok * jam_kerja;
    }

    string jabatan() const override { return "Freelancer"; }
};

int main() {
    // Karyawan k; // ❌ Error: abstract class!

    KaryawanTetap kt("Ali", 8_000_000, 2_000_000);
    Freelancer fl("Budi", 150_000, 40);

    kt.cetak_slip();
    fl.cetak_slip();

    return 0;
}
// Output:
// === Slip Gaji ===
// Nama    : Ali
// Jabatan : Karyawan Tetap
// Gaji    : Rp 10000000
// === Slip Gaji ===
// Nama    : Budi
// Jabatan : Freelancer
// Gaji    : Rp 6000000

Perbedaan C++ dengan Java/C# dalam OOP

AspekC++JavaC#
Multiple inheritance✅ Kelas❌ (hanya interface)❌ (hanya interface)
Virtual default❌ (harus eksplisit)✅ Semua methodvirtual (eksplisit)
InterfacePure abstract classinterface keywordinterface keyword
Abstract= 0abstract keywordabstract keyword
Override keywordoverride (C++11)@Override annotationoverride keyword

Multiple inheritance di C++ bisa menyebabkan “diamond problem”. Gunakan dengan hati-hati dan pertimbangkan virtual inheritance jika diperlukan.

Pertanyaan yang Sering Diajukan

Mengapa destructor harus virtual di base class?

Jika kamu menghapus objek derived melalui pointer base class (delete basePtr), tanpa virtual destructor hanya destructor base yang dipanggil — destructor derived tidak dieksekusi, menyebabkan resource leak. Dengan virtual destructor, urutan yang benar dipastikan: destructor derived dipanggil terlebih dahulu, lalu destructor base.

Apa perbedaan override dan tanpa override di C++?

override (C++11) adalah optional tapi sangat disarankan. Tanpanya, jika kamu salah mengetik signature method, compiler tidak memberikan error — method baru hanya dianggap method berbeda, bukan override. Dengan override, compiler memverifikasi bahwa method ini benar-benar meng-override virtual method dari base class.

Kapan menggunakan inheritance vs komposisi?

Gunakan inheritance untuk hubungan “is-a”: Anjing IS-A Hewan, Lingkaran IS-A Bentuk. Gunakan komposisi untuk hubungan “has-a”: Mobil HAS-A Mesin. Prinsip OOP modern lebih menyarankan komposisi — “favor composition over inheritance” — karena lebih fleksibel dan tidak menciptakan coupling yang kuat. Inheritance paling berguna untuk polymorphism dan shared interface.

Apa itu vtable yang sering disebutkan dalam konteks virtual?

vtable (virtual table) adalah tabel pointer ke fungsi yang dibuat compiler untuk class dengan virtual methods. Saat kamu memanggil virtual method melalui pointer/reference, program melihat ke vtable objek tersebut untuk menemukan implementasi yang tepat. Ini adalah mekanisme di balik polymorphism — ada sedikit overhead memori dan waktu, tapi biasanya dapat diabaikan.

Kesimpulan

KonsepSintaks C++Keterangan
Inheritance: public BaseClass mewarisi dari base class
Virtual methodvirtual void f()Bisa di-override oleh derived
Overridevoid f() overrideEksplisit override virtual method
Pure virtualvirtual void f() = 0Wajib diimplementasikan
Abstract classAda pure virtual methodTidak bisa di-instantiasi
Virtual destructorvirtual ~Base()Wajib jika ada pointer ke base

Artikel sebelumnya: Class dan Object di C++ — membuat class dasar di C++.

Langkah selanjutnya: Exception Handling di C++ — cara menangani error dengan try/catch/throw.

Artikel Terkait