Sintaks inheritance dan aksesnya

Inheritance public, protected, private, dan relasi is-a.

Daftar isi

Topik ini membahas sintaks pewarisan (inheritance) pada C++, yaitu mekanisme membentuk kelas baru dari kelas yang sudah ada beserta aturan akses membernya di dalam hierarki. Materinya mencakup relasi is-a sebagai dasar keputusan pewarisan, sintaks class Turunan : public Dasar, jangkauan member public, protected, dan private terhadap kelas turunan, serta penanda akses yang ditulis pada baris pewarisannya. Setelah membacanya, pembaca dapat menulis hierarki kelas sederhana dan menentukan member mana yang boleh disentuh oleh setiap bagian program.

Relasi is-a dan sintaks dasar pewarisan#

Pewarisan menyatakan bahwa sebuah kelas turunan adalah bentuk khusus dari kelas dasarnya, sebuah hubungan yang disebut relasi is-a. Pensil adalah alat tulis, sehingga kelas Pensil dapat mewarisi kelas AlatTulis dan menambahkan perilakunya sendiri. Uji relasi ini dilakukan sebelum sintaks ditulis, karena pewarisan yang tidak sesuai hubungan is-a menghasilkan desain yang sulit dijaga. Apabila hubungan kedua kelas lebih tepat dibaca “memiliki”, pemecahan masalahnya bukan pewarisan melainkan komposisi.

Sintaks pewarisan dinyatakan pada baris deklarasi kelas, berupa tanda titik dua, penanda akses, dan nama kelas dasarnya. Program berikut menerapkannya pada kelas Pensil yang mewarisi AlatTulis.

cpp alat-tulis.cpp
#include <iostream>
#include <string>

class AlatTulis {
protected:
    std::string nama;

public:
    explicit AlatTulis(const std::string& namaAlat) : nama{namaAlat} {}

    void perkenalan() const {
        std::cout << "Ini " << nama << ", siap menulis.\n";
    }
};

class Pensil : public AlatTulis {
private:
    int ketajaman;

public:
    Pensil(const std::string& namaAlat, int ketajamanAwal)
        : AlatTulis{namaAlat}, ketajaman{ketajamanAwal} {}

    void status() const {
        std::cout << nama << " memiliki ketajaman " << ketajaman << " persen.\n";
    }
};

int main() {
    Pensil p{"Pensil 2B", 90};
    p.perkenalan();
    p.status();
    return 0;
}
text Keluaran
Ini Pensil 2B, siap menulis.
Pensil 2B memiliki ketajaman 90 persen.

Deklarasi class Pensil : public AlatTulis menjadikan Pensil kelas turunan yang mewarisi AlatTulis secara public. Member function perkenalan tidak ditulis ulang di Pensil, tetapi tetap dapat dipanggil dari main karena status warisannya mengikuti wilayah asalnya. Constructor Pensil memanggil constructor kelas dasar melalui daftar inisialisasi, yaitu bagian AlatTulis{namaAlat} sebelum badan constructor dijalankan, karena pembentukan objek turunan selalu dimulai dari kelas dasarnya. Member nama dinyatakan protected, sehingga status yang berada di kelas turunan dapat membacanya secara langsung.

Perintah berikut mengompilasi berkas itu dan menjalankan hasilnya. Berkas biner ditempatkan di /tmp agar direktori proyek tetap bersih.

bash
# kompilasi alat-tulis.cpp menjadi program di folder sementara /tmp
g++ alat-tulis.cpp -o /tmp/alat-tulis
# jalankan program hasil kompilasi
/tmp/alat-tulis
text Keluaran
Ini Pensil 2B, siap menulis.
Pensil 2B memiliki ketajaman 90 persen.

Jangkauan akses member dalam hierarki#

Tiga wilayah akses yang sudah dikenal pada modul kelas dan objek memperoleh peran baru ketika pewarisan hadir. Tabel berikut merangkum siapa yang dapat mengakses member kelas dasar, yaitu kelas turunan yang mewarisinya dan kode luar yang hanya menggunakan objeknya.

Member AlatTulisAkses dari kelas turunanAkses dari kode luar
publicdapat diaksesdapat diakses
protecteddapat diaksestidak dapat diakses
privatetidak dapat diaksestidak dapat diakses

Member protected adalah perpanjangan dari private yang memberi akses tambahan bagi kelas turunan. Pada program sebelumnya, baris std::cout << nama di dalam status sah karena nama bersifat protected. Pernyataan yang sama ditulis di main akan ditolak compiler, sebagaimana fragmen berikut.

cpp Contoh: member protected ditolak bila diakses dari kode luar
// tidak-dijalankan (member nama bersifat protected, sehingga aksesnya dari main ditolak compiler)
Pensil p{"Pensil 2B", 90};
p.nama = "Pensil HB";

Perilaku member private dalam hierarki sering ditebak salah. Member tersebut tetap menjadi bagian dari setiap objek turunan dan tetap menempati memori di dalamnya, tetapi kelas turunan tidak dapat mengaksesnya secara langsung. Apabila nama diubah menjadi private, member function status di Pensil langsung ditolak compiler pada baris aksesnya. Jalur yang sah untuk mencapai data tersebut adalah melalui member public atau protected milik kelas dasar, misalnya perkenalan yang membaca nama dari dalam kelas AlatTulis.

Penanda akses pada pewarisan#

Penanda akses ditulis sebelum nama kelas dasar pada baris pewarisan, misalnya kata public pada class Pensil : public AlatTulis. Penanda itu menentukan kedudukan baru seluruh member warisan di dalam kelas turunan. Compiler membaca penanda ini ketika memutuskan apakah member warisan masih terbuka bagi kode luar. Tabel berikut merangkum perubahan kedudukannya.

Member di kelas dasar: public: protected: private
publictetap publicmenjadi protectedmenjadi private
protectedtetap protectedtetap protectedmenjadi private
privatetidak dapat diaksestidak dapat diaksestidak dapat diakses

Pewarisan public adalah bentuk yang dipakai hampir seluruh hierarki kelas karena member public kelas dasar tetap public di kelas turunan, sehingga relasi is-a dipertahankan utuh. Sebaliknya, pewarisan private menutup seluruh member warisan dari kode luar, sebagaimana fragmen berikut.

cpp Contoh: pewarisan private menutup member warisan dari kode luar
// tidak-dijalankan (pewarisan private menjadikan perkenalan sebagai member private milik PensilPribadi)
class PensilPribadi : private AlatTulis {
public:
    PensilPribadi() : AlatTulis{"Pensil 2B"} {}
};

PensilPribadi p;
p.perkenalan();

Pewarisan protected dan private lazim dipakai saat kelas turunan dibangun di atas kelas dasar. Bentuk pewarisan ini tidak menjanjikan bahwa kelas turunan adalah bentuk khusus dari kelas dasarnya. Kebutuhan semacam itu di C++ modern lebih sering diselesaikan dengan komposisi, yaitu menyimpan objek kelas dasar sebagai member biasa. Untuk hierarki polimorfik yang menjadi tema modul ini, pewarisan public adalah pilihan bakunya.

Kesalahan umum#

Empat kesalahan berikut paling sering muncul saat hierarki kelas pertama kali ditulis.

  • Menggunakan pewarisan untuk hubungan “memiliki”. Kelas MesinPensil memiliki ujung, bukan merupakan ujung, sehingga menggabungkannya melalui member lebih tepat daripada mewariskan. Perbaikannya adalah menyimpan objek kelas lain sebagai member biasa.
  • Menjadikan seluruh data protected agar mudah dijangkau kelas turunan. Kelonggaran itu memindahkan tanggung jawab menjaga invarian, yaitu aturan konsistensi data, kepada setiap kelas turunan, dan perbaikannya adalah mengembalikan data ke wilayah private lalu membuka member function protected yang diperlukan.
  • Menganggap member private tidak ikut diwariskan. Member itu ikut menyusun objek turunan dan tetap diinisialisasi oleh constructor kelas dasar, sedangkan yang hilang hanyalah akses langsung dari kelas turunan.
  • Mengubah wilayah akses member kelas dasar menjadi public begitu kebutuhan akses dari luar muncul. Perubahan itu membuka data kepada semua kode, bukan hanya kepada hierarkinya. Perbaikannya adalah memilih protected bila aksesnya memang disediakan untuk kelas turunan, atau menjalankan akses itu melalui member function kelas dasar.

Ringkasan#

  • Pewarisan dinyatakan pada deklarasi kelas dengan bentuk class Turunan : public Dasar dan menjadi cara mengekspresikan relasi is-a.
  • Member protected dapat diakses oleh kelas turunan tetapi tetap tertutup bagi kode luar, sedangkan member private tidak dapat diakses langsung oleh kelas turunan.
  • Penanda akses pada baris pewarisan menentukan kedudukan baru member warisan, dan pewarisan public adalah bentuk baku untuk hierarki polimorfik.
  • Constructor kelas dasar dipanggil melalui daftar inisialisasi constructor kelas turunan karena objek dibangun mulai dari bagian kelas dasarnya.

Referensi#