Fungsi Virtual & VTable
Termasuk bagian Pemrograman Berorientasi Object dalam perjalanan C++ di Coddy. Pelajaran 52 dari 104.
Kata kunci virtual mengatasi masalah yang kita lihat di pelajaran sebelumnya. Saat kamu mendeklarasikan method sebagai virtual, C++ menentukan versi mana yang akan dipanggil berdasarkan tipe object sebenarnya saat program berjalan, bukan tipe pointer.
class Animal {
public:
virtual void speak() {
std::cout << "Some sound" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() override {
std::cout << "Woof!" << std::endl;
}
};
Dog d;
Animal* ptr = &d;
ptr->speak(); // output: Woof!Sekarang method yang benar dipanggil! Kata kunci override bersifat opsional, tetapi disarankan. Kata kunci ini memberi tahu compiler bahwa kamu bermaksud mengganti fungsi virtual, sehingga kesalahan akan terdeteksi jika tanda tangannya tidak cocok.
Bagaimana cara kerjanya? Saat sebuah class memiliki fungsi virtual, kompilator membuat tabel virtual (vtable). Tabel pencarian tersembunyi ini berisi pointer ke fungsi virtual class tersebut. Setiap object yang memiliki fungsi virtual menyimpan pointer tersembunyi (vptr) ke vtable class-nya.
Saat kamu memanggil fungsi virtual melalui pointer, program mencari alamat fungsi yang tepat di vtable saat runtime. Ini disebut dynamic dispatch. Proses ini menambahkan sedikit overhead dibandingkan pemanggilan fungsi biasa, tetapi memungkinkan perilaku polimorfik yang andal.
Satu aturan penting: jika sebuah class memiliki fungsi virtual dan akan digunakan sebagai base class, destructornya juga harus virtual. Ini memastikan pembersihan yang tepat saat menghapus object turunan melalui pointer base:
class Animal {
public:
virtual ~Animal() {} // Destruktor virtual
virtual void speak() {}
};Tantangan
MudahMari kita buat sistem pemutar media yang menunjukkan keunggulan fungsi virtual dan polimorfisme saat runtime. Kamu akan membuat hierarki tipe media yang masing-masing diputar dengan cara berbeda, lalu melihat bagaimana fungsi virtual memungkinkan perilaku yang tepat meskipun diakses melalui pointer class dasar.
Kamu akan mengatur kodemu dalam tiga file:
Media.h: Definisikan class dasarMediayang mewakili media apa pun yang dapat diputar:- Member
std::string titleyang dilindungi - Konstruktor yang menerima judul dan menyimpannya
- Method virtual
play()yang mencetak:Playing media: <title> - Destructor virtual yang mencetak:
Media [<title>] destroyed
- Member
AudioTrack.h: Definisikan classAudioTrackyang mewarisiMediasecara publik:- Member
std::string artistyang bersifat privat - Konstruktor yang menerima judul dan artis, meneruskan judul ke class dasar, lalu menyimpan artis
- Override
play()menggunakan kata kuncioverrideuntuk mencetak:Playing audio: <title> by <artist> - Destructor yang mencetak:
AudioTrack [<title>] destroyed
- Member
main.cpp: Baca tiga input, masing-masing pada baris terpisah:- Judul audio track (string)
- Nama artis (string)
- Judul video (string)
Definisikan class
VideoCliplangsung di main.cpp yang mewarisiMediasecara publik:- Member
int durationyang bersifat privat, dalam satuan detik - Konstruktor yang menerima judul dan durasi, dengan durasi default 120
- Override
play()untuk mencetak:Playing video: <title> (<duration>s) - Destructor yang mencetak:
VideoClip [<title>] destroyed
Buat array berisi tiga pointer
Media*di dalam cakupan blok. Alokasikan secara dinamis:- Sebuah object dasar
Mediadengan judul "Generic Media" - Sebuah object
AudioTrackdengan judul dan artis dari input - Sebuah object
VideoClipdengan judul video dari input
Lakukan perulangan pada array dan panggil
play()pada setiap pointer. Lalu hapus semua object dengan urutan terbalik. Setelah blok, cetak:Playback complete!
Sebagai contoh, dengan input Bohemian Rhapsody, Queen, dan Nature Documentary:
Playing media: Generic Media
Playing audio: Bohemian Rhapsody by Queen
Playing video: Nature Documentary (120s)
VideoClip [Nature Documentary] destroyed
Media [Nature Documentary] destroyed
AudioTrack [Bohemian Rhapsody] destroyed
Media [Bohemian Rhapsody] destroyed
Media [Generic Media] destroyed
Playback complete!Perhatikan bahwa pemanggilan play() melalui pointer Media* menjalankan method class turunan yang tepat berkat fungsi virtual. Perhatikan juga bagaimana destructor virtual memastikan pembersihan yang tepat: destructor class turunan dan dasar sama-sama dijalankan saat menghapus melalui pointer dasar.
Coba sendiri
#include <iostream>
#include <string>
#include "Media.h"
#include "AudioTrack.h"
using namespace std;
// TODO: Definisikan class VideoClip di sini yang mewarisi Media secara publik
// - Anggota duration bertipe int yang bersifat private (dalam detik)
// - Konstruktor menerima title dan duration (nilai default duration adalah 120)
// - Override play() untuk mencetak: Playing video: <title> (<duration>s)
// - Destruktor mencetak: VideoClip [<title>] destroyed
class VideoClip : public Media {
private:
int duration;
public:
// TODO: Implementasikan konstruktor
VideoClip(const std::string& t, int d = 120) : Media(t) {
// TODO: Simpan duration
}
// TODO: Override method play()
void play() override {
// TODO: Implementasikan method ini
}
// TODO: Implementasikan destruktor
~VideoClip() {
// TODO: Implementasikan destruktor ini
}
};
int main() {
// Baca input
string audioTitle;
string artist;
string videoTitle;
getline(cin, audioTitle);
getline(cin, artist);
getline(cin, videoTitle);
// TODO: Buat cakupan blok dengan kurung kurawal
// Di dalam blok:
// 1. Buat array berisi tiga pointer Media*
// 2. Alokasikan secara dinamis:
// - object Media dasar dengan judul "Generic Media"
// - object AudioTrack dengan judul dan artis dari input
// - object VideoClip dengan judul video dari input
// 3. Ulangi setiap elemen array dan panggil play() pada tiap pointer
// 4. Hapus semua object dalam urutan terbalik
// TODO: Setelah blok, cetak: Playback complete!
return 0;
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Pemrograman Berorientasi Object
1Dasar-Dasar OOP
File EksternalBuild dan Kompilasi C++File Header dan File SourceNamespace dan CakupanPengantar OOP dalam C++class vs objectPointer 'this'method (Fungsi Anggota)Atribut (Anggota Data)Dasar Ctor dan DtorRingkasan: Kalkulator Sederhana4Properti class
Anggota instance vs staticGetter dan setterFungsi anggota constKata kunci mutableMethod dan variabel staticFungsi dan class friendRangkuman, Pengelola Rekening Bank7Pewarisan
Pewarisan DasarTingkat Akses pada PewarisanUrutan Pemanggilan Ctor & DtorPenimpaan methodFungsi Virtual & VTablePewarisan BergandaPewarisan VirtualRangkuman: Hierarki Karyawan2Manajemen Memori
Memori Stack vs HeapPointer dan ReferenceMemori Dinamis (new/delete)Smart Pointer di C++RAII di C++Rangkuman: Pengelola Array Dinamis5Enkapsulasi
Penentu Akses dalam C++Penentu Akses secara MendalamPenyembunyian Informasistruct vs classNested dan Inner classRangkuman, Sistem Catatan Siswa8Polimorfisme
Polimorfisme saat Compile vs RuntimeOverloading FungsiMeninjau Kembali Fungsi VirtualFungsi Virtual MurniKelas AbstrakDesain Interface di C++Casting Dinamis & RTTIRangkuman: Kalkulator Bentuk3Konstruktor & Destruktor
Konstruktor DefaultKonstruktor BerparameterKonstruktor SalinKonstruktor PindahDaftar Inisialisasi KonstruktorKonstruktor DelegasiKupas Tuntas DestruktorAturan Tiga, Lima, dan NolRangkuman: class String6Overloading Operator
Pengantar Overloading OperatorOverloading Operator AritmetikaOverloading Operator PerbandinganOperator StreamOverloading Operator PenugasanOverloading Operator [] dan ()Operator Konversi TipeRingkasan, class Matrix9Template
Template FungsiTemplate classSpesialisasi TemplateTemplate VariadikDasar SFINAE & Type TraitsRangkuman, Container GenerikLatihan sendiri: Compiler C++ online