Friday, December 23, 2016

Bab 5. C++ Untuk Programer



Bab. 5 Polimorfisme

Tujuan Instruksional
      ·         Relasi antar objek dalam hirarki pewarisan.
      ·         Fungsi virtual dan kelas abstrak.

·         Studi kasus: Sistem penggajian menggunakan polimorfisme.





5.1 Introduksi

Sekarang Anda akan melanjutkan pembelajaran pemrograman berorientasi objek dengan menjelaskan dan mendemonstrasikan polimorfisme dalam hirarki pewarisan. Polimorfisme memampukan Anda untuk “memprogram secara umum”, bukan “memprogram secara khusus”.  Dengan polimorfisme, Anda dapat merancang dan mengimplementasikan sistem yang dapat diperluas, dimana kelas-kelas baru dapat ditambahkan dengan sedikit atau tanpa modifikasi, sepanjang kelas-kelas baru tersebut bagian dari hirarki pewarisan yang diproses program secara generik. Bagian-bagian program yang harus diubah untuk mengakomodasi kelas-kelas baru adalah yang memerlukan pengetahuan langsung dari kelas-kelas baru yang Anda tambahkan ke dalam hirarki. Sebagai contoh, jika Anda menciptakan kelas KupuKupu yang mewarisi dari kelas Binatang (yang dapat merespon pesan pindah dengan cara terbang sepanjang satu meter), maka Anda hanya perlu menulis kelas KupuKupu dan bagian program yang menginstansiasi objek KupuKupu. Bagian dari program yang memproses setiap Binatang secara generik tetap sama dan tidak perlu diubah.

5.2 Pengenalan Polimorfisme
Dimisalkan bahwa Anda sedang merancang video game yang memanipulasi objek-objek bertipe berbeda, mencakup objek-objek dari kelas SinarLaser, Mars, Venus, LuarAngkasa, dan Pluto. Bayangkan bahwa setiap kelas tersebut mewarisi dari kelas basis bersama ObjekAngkasa, yang memuat fungsi anggota gambar. Setiap kelas terderivasi mengimplementasikan fungsi ini dalam suatu cara yang sesuai untuk kelas tersebut. Sebuah program screen-manager memuat suatu kontainer (vector) yang menampung pointer-pointer ObjekAngkasa yang menunjuk ke objek-objek berbagai kelas. Program tersebut secara periodik mengirim pesan yang sama kepada setiap objek, sebut saja gambar. Setiap tipe objek merespon dengan cara yang unik. Sebagai contoh, objek SinarLaser akan menggambarkan dirinya dengan pancaran warna merah menyalah, objek LuarAngkasa akan menggambarkan dirinya dengan dominasi hitam dengan titik-titik bintang putih menyala, dan objek Mars akan menggambarkan dirinya dengan bulatan jingga. Pesan yang sama (pada kasus ini, gambar) yang dikirimkan kepada berbagai tipe objek memiliki banyak bentuk hasil (many forms of result). Dari sinilah istilah polimorfisme berasal. (Poly = banyak, form = bentuk).

Program screen-manager polimorfik memfasilitasi penambahan kelas-kelas baru pada sistem dengan modifikasi minimal. Dimisalkan bahwa Anda ingin menambahkan beberapa objek Bumi ke dalam program. Untuk melakukannya, Anda harus membangun kelas Bumi yang mewarisi ObjekAngkasa, tetapi menyediakan sendiri definisi fungsi anggota gambar. Kemudian, ketika pointer menunjuk ke objek kelas Bumi di dalam kontainer, Anda tidak perlu memodifikasi kode screen-manager. Screen-manager memanggil fungsi anggota gambar pada tiap objek di dalam kontainer, tanpa memandang tipe objek. Jadi, tanpa memodifikasi sistem (selain membangun dan menyertakan kelas yang akan ditambahkan), Anda dapat menggunakan polimorfisme untuk mengakomodasi kelas-kelas tambahan, yang sebelumnya belum pernah terpikirkan Anda pada saat merancang sistem.

5.3 Relasi Antar Objek dalam Hirarki Pewarisan
Anda telah menciptakan suatu hirarki kelas karyawan, dimana di dalamnya kelas KaryawanKomisiPlusPokok mewarisi dari kelas KaryawanKomisi. Objek-objek KaryawanKomisiPlusPokok dan KaryawanKomisi dimanipulasi menggunakan nama objek untuk memanggil fungsi anggota. Sekarang akan diperiksa relasi antar kelas di dalam hirarki lebih dekat lagi. Beberapa subbab ke depan akan menyajikan rentetan contoh yang mendemonstrasikan bagaimana pointer kelas basis dan pointer kelas terderivasi diarahkan untuk menunjuk objek kelas basis dan objek kelas terderivasi, dan bagaimana pointer-pointer tersebut dapat digunakan untuk memanggil fungsi-fungsi anggota yang memanipulasi objek-objek tersebut.

5.3.1 Memanggil Fungsi Kelas Basis dari Objek Kelas Terderivasi
Contoh pada Gambar 5.1 mendaur-ulang versi akhir dari kelas KaryawanKomisi dan KaryawanKomisiPlusPokok. Contoh ini mendemonstrasikan tiga cara dalam mengarahkan pointer kelas basis dan pointer kelas terderivasi untuk menunjuk ke objek kelas basis dan objek kelas terderivasi. Dua contoh pertama cukup sederhana, dimana sebuah pointer kelas basis akan diarahkan pada objek kelas basis dan memanggil fungsionalitas kelas basis, dan sebuah pointer kelas terderivasi diarahkan pada objek kelas terderivasi dan memanggil fungsionalitas kelas terderivasi. Kemudian, akan didemonstrasikan relasi antara kelas terderivasi dan kelas basis (relasi adalah-suatu dari pewarisan) dengan mengarahkan pointer kelas basis pada objek kelas terderivasi dan menunjukkan bahwa fungsionalitas kelas kelas basis tersedia bagi objek kelas terderivasi.

Gambar 5.1 Menugaskan Alamat Objek Kelas Basis dan Objek Kelas Terderivasi kepada Pointer Kelas Basis dan Pointer Kelas Terderivasi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Gambar 5.1: gambar5_01.cpp
// mengarahkan pointer kelas basis dan pointer kelas terderivasi pada
// objek kelas basis dan pada objek kelas terderivasi.
#include <iostream>
#include <iomanip>
#include "KaryawanKomisi.h"
#include "KaryawanKomisiPlusPokok.h"
using namespace std;

int main()
{
  // menciptakan objek kelas basis
  KaryawanKomisi karyawanKomisi(
    "Robert", "Tohonan", "222-22-2222", 10000, .06 );

  // menciptakan pointer kelas basis
  KaryawanKomisi *karyawanKomisiPtr = 0;

  // menciptakan objek kelas terderivasi
  KaryawanKomisiPlusPokok karyawanKomisiPlusPokok(
    "Rico", "Chandra", "333-33-3333", 5000, .04, 300 );

  // menciptakan pointer kelas terderivasi
  KaryawanKomisiPlusPokok *karyawanKomisiPlusPokokPtr = 0;

  // menetapan format keluaran pecahan
  cout << fixed << setprecision( 2 );

  // menampilkan objek karyawanKomisi dan objek karyawanKomisiPlusPokok
  cout << "Menampilkan objek kelas basis dan objek kelas terderivasi:\n\n";
  karyawanKomisi.tampil(); // memanggil tampil kelas basis
  cout << "\n\n";
  karyawanKomisiPlusPokok.tampil(); // memanggil tampil kelas terderivasi

  // mengarahkan pointer kelas basis pada objek kelas basis dan tampil
  karyawanKomisiPtr = &karyawanKomisi; // sangat natural
  cout << "\n\n\nMemanggil tampil dengan pointer kelas basis yang menunjuk ke "
    << "\nobjek kelas basis memanggil fungsi tampil kelas basis:\n\n";
  karyawanKomisiPtr->tampil(); // memanggil tampil kelas basis

  // mengarahkan pointer kelas terderivasi pada objek kelas terderivasi dan tampil
  karyawanKomisiPlusPokokPtr = &karyawanKomisiPlusPokok; // sangat natural
  cout << "\n\n\nMemanggil tampil dengan pointer kelas terderivasi yang "
    << "\nmenunjuk ke objek kelas terderivasi memanggil fungsi tampil "
    << " kelas terderivasi:\n\n";
  karyawanKomisiPlusPokokPtr->tampil(); // memanggil tampil kelas terderivasi

  // mengarahkan pointer kelas basis pada objek kelas terderivasi dan tampil
  karyawanKomisiPtr = &karyawanKomisiPlusPokok;
  cout << "\n\n\nMemanggil tampil dengan pointer kelas basis yang menunjuk ke "
    << "objek terderivasi\nmemanggil fungsi tampil kelas basis "
    << "pada objek kelas terderivasi:\n\n";
  karyawanKomisiPtr->tampil(); // memanggil tampil kelas basis
  cout << endl;
} // akhir dari main

Menampilkan objek kelas basis dan objek kelas terderivasi:

karyawan komisi: Robert Tohonan
nomor kartu penduduk: 222-22-2222
penjualan kotor: 10000.00
besar komisi: 0.06

karyawan komisi bergaji pokok: karyawan komisi: Rico Chandra
nomor kartu penduduk: 333-33-3333
penjualan kotor: 5000.00
besar komisi: 0.04
gaji pokok: 300.00

Memanggil tampil dengan pointer kelas basis yang menunjuk ke
objek kelas basis memanggil fungsi tampil kelas basis:

karyawan komisi: Robert Tohonan
nomor kartu penduduk: 222-22-2222
penjualan kotor: 10000.00
besar komisi: 0.06

Memanggil tampil dengan pointer kelas terderivasi yang
menunjuk ke objek kelas terderivasi memanggil fungsi tampil  kelas terderivasi:

karyawan komisi bergaji pokok: karyawan komisi: Rico Chandra
nomor kartu penduduk: 333-33-3333
penjualan kotor: 5000.00
besar komisi: 0.04
gaji pokok: 300.00

Memanggil tampil dengan pointer kelas basis yang menunjuk ke objek terderivasi
memanggil fungsi tampil kelas basis pada objek kelas terderivasi:

karyawan komisi: Rico Chandra
nomor kartu penduduk: 333-33-3333
penjualan kotor: 5000.00
besar komisi: 0.04

Ingat bahwa setiap objek KaryawanKomisiPlusPokok merupakan objek KaryawanKomisi yang juga memiliki gaji pokok. Fungsi anggota penghasilan kelas KaryawanKomisiPlusPokok (baris 33-36 pada Gambar 4.15) medefinisikan-ulang fungsi penghasilan kelas KaryawanKomisi (baris 84-87 pada Gambar 4.14) untuk menyertakan gaji pokok objek. Fungsi anggota tampil kelas KaryawanKomisiPlusPokok (baris 39-47 pada Gambar 4.15) mendefinisikan-ulang fungsi anggota tampil kelas KaryawanKomisi (baris 90-97 pada Gambar 4.14) untuk menampilkan informasi yang sama ditambah dengan gaji pokok karyawan.

Menciptakan Objek dan Menampilkan Isinya
Pada Gambar 5.1, baris 13-14 menciptakan sebuah objek KaryawanKomisi dan baris 17 menciptakan sebuah pointer yang menunjuk ke objek KaryawanKomisi; baris 20-21 menciptakan sebuah objek KaryawanKomisi dan baris 24 menciptakan suatu pointer yang menunjuk ke objek KaryawanKomisiPlusPokok. Baris 31 dan 333 menggunakan nama setiap objek untuk memanggil fungsi anggota tampil-nya.

Mengarahkan Pointer Kelas Basis pada Objek Kelas Basis
Baris 36 menugaskan alamat objek kelas basis, karyawanKomisi, kepada pointer kelas basis, karyawanKomisiPtr, yang pada baris 39 dipakai untuk memanggil fungsi anggota tampil pada objek karyawanKomisi. Hal ini memanggil versi tampil yang didefinisikan di dalam kelas basis KaryawanKomisi.

Mengarahkan Pointer Kelas Terderivasi pada Objek Kelas Terderivasi
Baris 42 menugaskan alamat objek kelas terderivasi, karyawanKomisiPlusPokok, kepada pointer kelas terderivasi, karyawanKomisiPlusPokokPtr, yang pada baris 46 dipakai untuk memanggil fungsi anggota tampil pada objek karyawanKomisiPlusPokok. Hal ini memanggil versi tampil yang didefinisikan di dalam kelas basis KaryawanKomisiPlusPokok.

Mengarahkan Pointer Kelas Basis pada Objek Kelas Terderivasi
Baris 49 kemudian menugaskan alamat objek kelas terderivasi, karyawanKomisiPlusPokok kepada pointer kelas basis karyawanKomisiPtr, yang pada baris 53 digunakan untuk memanggil fungsi anggota tampil. “Saling-silang” ini diijinkan karena objek kelas terderivasi merupakan objek kelas basisnya. Meskipun pada kenyataannya bahwa pointer kelas basis karyawanKomisiPtr menunjuk ke objek kelas terderivasi karyawanKomisiPlusPokok, fungsi anggota tampil kelas basis KaryawanKomisi yang dipanggil (bukan fungsi tampil kelas KaryawanKomisiPlusPokok). Keluaran atas pemanggilan setiap fungsi anggota tampil menyingkapkan bahwa fungsionalitas yang dipanggil  tergantung dari tipe pointer (atau referensi) yang digunakan untuk memanggil fungsi, bukan tipe objek yang dipakai untuk memanggil fungsi.

5.3.2 Mengarahkan Pointer Kelas Terderivasi pada Objek Kelas Basis
Pada subbab 5.3.1, Anda menugaskan alamat sebuah objek kelas terderivasi kepada pointer kelas basis dan menjelaskan bahwa kompiler C++ mengijinkan penugasan ini, karena objek kelas terderivasi merupakan objek kelas basis. Sekarang akan dilakukan pendekatan terbalik pada Gambar 5.2, dimana pointer kelas terderivasi akan diarahkan pada objek kelas basis. Program ini akan mendaur-ulang versi akhir kelas KaryawanKomisi dan KaryawanKomisiPlusPokok dari subab 4.4.5. Baris 8-9 pada Gambar 8.2 menciptakan sebuah objek KaryawanKomisi, dan baris 10 menciptakan sebuah pointer KaryawanKomisiPlusPokok. Baris 14 mencoba menugaskan alamat objek kelas basis karyawanKomisi kepada pointer kelas terderivasi karyawanKomisiPlusPokokPtr, tetapi kompiler menghasilkan error. Kompiler melarang penugasan ini, karena KaryawanKomisi bukan merupakan KaryawanKomisiPlusPokok. Pelajari konsekuensi jika kompiler mengijinkan penugasan ini. Melalui suatu pointer KaryawanKomisiPlusPokok, Anda dapat memanggil setiap fungsi anggota KaryawanKomisiPlusPokok, termasuk setGajiPokok, untuk objek yang ditunjuk oleh pointer (objek kelas basis karyawanKomisi). Tetapi, objek KaryawanKomisi tidak menyediakan fungsi anggota setGajiPokok, juga tidak menyediakan anggota data gajiPokok untuk dipakai. Hal ini bisa menimbulkan masalah, karena fungsi anggota setGajiPokok mengasumsikan bahwa terdapat anggota data gajiPokok untuk digunakan pada “lokasi biasa” di dalam objek KaryawanKomisiPlusPokok. Memori tersebut tidak menjadi miliki objek KaryawanKomisi, jadi fungsi anggota setGajiPokok bisa meng-overwrite data penting di dalam memori tersebut, yang bisa saja menjadi milik objek lain.

Gambar 5.2 Mengarahkan Pointer Kelas Terderivasi pada Objek Kelas Basis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Gambar 5.2: gambar5_02.cpp
// Mengarahkan pointer kelas terderivasi pada objek kelas basis.
#include "KaryawanKomisi.h"
#include "KaryawanKomisiPlusPokok.h"

int main()
{
  KaryawanKomisi karyawanKomisi(
    "Jozen", "Tarabunga", "222-22-2222", 10000, .06 );
  KaryawanKomisiPlusPokok *karyawanKomisiPlusPokokPtr = 0;

  // mengarahkan pointer kelas terderivasi pada objek kelas basis
  // Error: objek KaryawanKomisi bukan merupakan objek KaryawanKomisiPlusPokok
  karyawanKomisiPlusPokokPtr = &karyawanKomisi;
} // akhir dari main

Pesan error kompiler C++ Microsoft Visual

e:\c++\gambar5_02.cpp(14) : error C2440: '=' : cannot convert from 'class KaryawanKomisi *' to 'class KaryawanKomisiPlusPokok *'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
e:\c++\gambar5_02.cpp(15) : warning C4508: 'main' : function should return a value; 'void' return type assumed
Error executing cl.exe.

5.3.3 Pemanggilan Fungsi Kelas Terderivasi via Pointer Kelas Basis
Melalui pointer kelas basis, kompiler mengijinkan Anda hanya untuk memanggil fungsi anggota kelas basis. Jadi, jika pointer kelas basis diarahkan pada objek kelas terderivasi, dan percobaan dilakukan untuk mengakses fungsi anggota kelas terderivasi, maka error kompilasi akan terjadi.

Gambar 5.3 menunjukkan konsekuensi dari percobaan pemanggilan fungsi anggota kelas terderivasi melalui pointer kelas basis. Sekarang akan didaur-ulang versi akhir kelas KaryawanKomisi dan KaryawanKomisiPlusPokok dari subab 4.4.5. Baris 9 menciptakan karyawanKomisiPtr, sebuah pointer objek kelas KaryawanKomisi, dan baris 10-11 menciptakan objek kelas KaryawanKomisiPlusPokok. Baris 14 mengarahkan karyawanKomisiPtr pada objek kelas terderivasi karyawanKomisiPlusPokok. Hal ini diijinkan, karena objek kelas KaryawanKomisiPlusPokok merupakan objek KaryawanKomisi (dalam sudut pandang bahwa objek kelas KaryawanKomisiPlusPokok memuat semua fungsionalitas objek kelas KaryawanKomisi). Baris 18-22 memanggil fungsi anggota kelas basis getNamaPertama, getNamaAkhir, getNomorKTP, getPenjualanKotor, dan getBesarKomisi melalui pointer kelas basis. Semua pemanggilan ini dapat dilakukan, karena objek kelas KaryawanKomisiPlusPokok mewarisi semua anggota tersebut dari KaryawanKomisi. Diketahui bahwa karyawanKomisiPtr diarahkan pada objek kelas KaryawanKomisiPlusPokok, jadi pada baris 26-27 Anda mencoba memanggil fungsi-fungsi anggota kelas KaryawanKomisiPlusPokok (getGajiPokok dan setGajiPokok). Kompiler menghasilkan error pada kedua pemanggilan ini, karena kedua fungsi anggota bukanlah fungsi anggota kelas basis KaryawanKomisi. (Pada kasus ini, melalui KaryawanKomisi *, Anda hanya dapat memanggil fungsi anggota kelas KaryawanKomisi seperti setNamaPertama, getNamaPertama, setNamaAkhir, getNamaAkhir, setNomorKTP, getNomorKTP, setPenjualanKotor, getPenjualanKotor, setBesarKomisi, getBesarKomisi, penghasilan, dan tampil).

Gambar 5.3 Memanggil Fungsi Anggota Kelas Terderivasi melalui Pointer Kelas Basis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Gambar 5.3: gambar5_03 .cpp
// Mencoba untuk memanggil fungsi anggota kelas terderivasi
// melalui pointer kelas basis.
#include "KaryawanKomisi.h"
#include "KaryawanKomisiPlusPokok.h"

int main()
{
  KaryawanKomisi *karyawanKomisiPtr = 0; // kelas basis
  KaryawanKomisiPlusPokok karyawanKomisiPlusPokok(
    "Robert", "Tohonan", "333-33-3333", 5000, .04, 300 ); // kelas terderivasi

  // mengarahkan pointer kelas basis pada objek kelas terderivasi
  karyawanKomisiPtr = &karyawanKomisiPlusPokok;

  // memanggil fungsi anggota kelas basis pada objek
  // kelas terderivasi melalui pointer kelas basis (diijinkan)
  string namaPertama = karyawanKomisiPtr->getNamaPertama();
  string namaAkhir = karyawanKomisiPtr->getNamaPertama();
  string ktp = karyawanKomisiPtr->getNomorKTP();
  double penjualanKotor = karyawanKomisiPtr->getPenjualanKotor();
  double besarKomisi = karyawanKomisiPtr->getBesarKomisi();

  // mencoba untuk memanggil fungsi kelas terderivasi (yang tidak diwarisi dari kelas basis)
  // pada objek kelas terderivasi melalui pointer kelas basis (tidak diijinkan)
  double gajiPokok = karyawanKomisiPtr->getGajiPokok();
  karyawanKomisiPtr->setGajiPokok( 500 );
} // akhir dari main

Pesan error kompiler C++ Microsoft Visual

e:\C++\gambar5_03.cpp(26) : error C2039: 'getGajiPokok' : is not a member of 'KaryawanKomisi'
e:\C++\karyawankomisi.h(10) : see declaration of 'KaryawanKomisi'
e:\C++\gambar5_03.cpp(27) : error C2039: 'setGajiPokok' : is not a member of 'KaryawanKomisi'
e:\C++\karyawankomisi.h(10) : see declaration of 'KaryawanKomisi'
e:\C++\gambar5_03.cpp(28) : warning C4508: 'main' : function should return a value; 'void' return type assumed

Kompiler akan mengijinkan akses terhadap fungsi anggota kelas terderivasi (yang tidak diwarisi dari kelas basis) dari pointer kelas basis yang diarahkan pada objek kelas terderivasi jika Anda secara eksplisit melakukan cast pointer kelas basis menjadi pointer kelas terderivasi. Hal ini dikenal dengan downcasting. Seperti Anda ketahui, adalah hal mungkin untuk mengarahkan pointer kelas basis pada objek kelas terderivasi. Tetapi, seperti didemonstrasikan pada Gambar 5.3, pointer kelas basis hanya bisa digunakan untuk memanggil fungsi-fungsi yang dideklarasikan di dalam kelas basis. Downcasting mengijinkan operasi pada objek kelas terderivasi yang ditunjuk oleh pointer kelas basis. Setelah downcast, program dapat memanggil semua fungsi kelas terderivasi yang tidak berada di dalam kelas basis. Downcasting merupakan operasi yang berpotensi membahayakan.

5.3.4 Fungsi Virtual
Pada subbab 5.3.1, pointer kelas basis KaryawanKomisi diarahkan pada objek kelas terderivasi KaryawanKomisiPlusPokok, kemudian fungsi anggota tampil dipanggil melalui pointer tersebut. Pada kasus tersebut, pointer KaryawanKomisi memanggil fungsi anggota tampil (kelas KaryawanKomisi) pada objek KaryawanKomisiKomisiPlusPokok.

Pertama, akan dijelaskan mengapa fungsi virtual berguna. Dimisalkan kelas bangun seperti Lingkaran, SegiTiga, PersegiEmpat, Ellips semuanya diderivasi dari kelas basis Bangun. Setiap kelas ini diberikan kemampuan untuk menggambar dirinya sendiri melalui fungsi anggota gambar. Meskipun setiap kelas memiliki fungsi gambar sendiri, fungsi untuk setiap bangun cukup berbeda. Di dalam sebuah program yang menggambar sehimpunan bangun, akan berguna bila memperlakukan semua bangun secara generik seperti kelas basis Bangun. Kemudian, untuk menggambar sembarang bangun, akan digunakan pointer kelas basis Bangun untuk menunjuk ke fungsi gambar dan membiarkan program menentukan secara dinamis fungsi gambar kelas terderivasi mana yang akan digunakan, berdasarkan pada tipe objek yang ditunjuk oleh pointer kelas basis Bangun.

Untuk memampukan watak ini, Anda perlu mendeklarasikan gambar di dalam kelas basis sebagai fungsi virtual, dan mendefinisikan-ulang gambar di dalam setiap kelas terderivasi untuk menggambar bangun yang sesuai. Fungsi yang didefinisikan-ulang di dalam kelas terderivasi memiliki sidik dan tipe nilai balik yang sama dengan fungsi asli di dalam kelas basis. Jika Anda tidak mendeklarasikan fungsi kelas basis sebagai virtual, maka Anda dapat mendefinisikan-ulang fungsi tersebut. Sebaliknya, jika Anda mendeklarasikan fungsi kelas basis sebagai virtual, maka Anda dapat mendefinisikan-ulang fungsi tersebut untuk memampukan watak polimorfik. Anda mendeklarasikan suatu fungsi sebagai virtual dengan memberikan katakunci virtual pada prototipe fungsi di dalam kelas basis. Sebagai contoh,

virtual void gambar() const;

ditempatkan di dalam kelas basis Bangun. Prototipe tersebut mendeklarasikan bahwa fungsi gambar adalah fungsi virtual yang tidak mengambil argumen apapun dan tidak menghasilkan nilai balik apapun. Fungsi ini dideklarasikan const karena fungsi gambar secara umum tidak mengubah objek Bangun. Fungsi virtual tidak harus berupa fungsi const.

Jika sebuah program memanggil fungsi virtual melalui pointer kelas basis yang menunjuk ke objek kelas terderivasi (misalnya, bangunPtr ->gambar()) atau melalui referensi kelas basis ke objek kelas terderivasi (misalnya, bangunRef.gambar()), maka program akan memilih fungsi kelas terderivasi gambar yang tepat secara dinamis berdasarkan tipe objek, bukan berdasar pada tipe pointer atau tipe referensi. Pemilihan fungsi yang tepat untuk dipanggil secara dinamis dikenal dengan pengikatan dinamis.

Sekarang dilihat bagaimana fungsi virtual dapat memampukan watak polimorfik di dalam hirarki pewarisan karyawan. Gambar 5.4 – 5.5 adalah header untuk kelas KaryawanKomisi dan KaryawanKomisiPlusPokok. Satu-satunya fitur di dalam kedua file ini adalah bahwa Anda menspesifikasi setiap fungsi anggota penghasilan dan tampil sebagai virtal (baris 30-31 pada Gambar 5.4 dan baris 20-21 pada baris 5). Karena kedua fungsi penghasilan dan tampil adalah virtual di dalam kelas KaryawanKomisi, fungsi penghasilan dan tampil pada kelas KaryawanKomisiPlusPokok mendefinisikan-ulang kedua fungsi tersebut di dalam kelas basis KaryawanKomisi. Sekarang, jika Anda mengarahkan pointer kelas basis KaryawanKomisi pada objek kelas terderivasi KaryawanKomisiPlusPokok dan program menggunakan pointer tersebut untuk memanggil fungsi penghasilan atau tampil, maka fungsi penghasilan atau tampil pada kelas KaryawanKomisiPlusPokok lah yang dipanggil. Tidak ada perubahan pada implementasi fungsi anggota pada kelas KaryawanKomisi dan KaryawanKomisiPlusPokok, jadi Anda dapat mendaur-ulang versi pada Gambar 4.14 dan 4.15.

Gambar 5.4 Header Kelas KaryawanKomisi Mendeklarasikan Fungsi penghasilan dan Fungsi tampil sebagai virtual

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Gambar 5.4: KaryawanKomisi.h
// Definisi kelas KaryawanKomisi merepresentasikan seorang karyawan komisi.
#ifndef KOMISI_H
#define KOMISI_H

#include <string> // Kelas string standard C++
using namespace std;

class KaryawanKomisi
{
public:
  KaryawanKomisi( const string &, const string &, const string &,
  double = 0.0, double = 0.0 );

  void setNamaPertama( const string & ); // menetapkan nama pertama
  string getNamaPertama() const; // mengembalikan nama pertama

  void setNamaAkhir( const string & ); // menetapkan nama akhir
  string getNamaAkhir() const; // mengembalikan nama akhir

  void setNomorKTP( const string & ); // menetapkan nomor KTP
  string getNomorKTP() const; // mengembalikan nomor KTP

  void setPenjualanKotor( double ); // menetapkan jumlah penjualan kotor
  double getPenjualanKotor() const; // mengembalikan jumlah penjualan kotor

  void setBesarKomisi( double ); // menetapkan besar komisi (persentase)
  double getBesarKomisi() const; // mengembalikan besar komisi

  virtual double penghasilan() const; // menghitung penghasilan
  virtual void tampil() const; // menampilkan objek KaryawanKomisi
private:
  string namaPertama;
  string namaAkhir;
  string nomorKTP;
  double penjualanKotor; // penjualan kotor mingguan
  double besarKomisi; // persentasi komisi
}; // akhir dari kelas KaryawanKomisi

#endif

Gambar 5.5 Header Kelas KaryawanKomisiPlusPokok Mendeklarasikan Fungsi penghasilan dan Fungsi tampil sebagai virtual

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Gambar 5.5: KaryawanKomisiPlusPokok.h
// Kelas KaryawanKomisiPlusPokok diderivasi dari kelas
// KaryawanKomisi.
#ifndef POKOK_PLUS_H
#define POKOK_PLUS_H

#include <string> // Kelas string standard C++
#include "KaryawanKomisi.h"
using namespace std;

class KaryawanKomisiPlusPokok : public KaryawanKomisi
{
public:
  KaryawanKomisiPlusPokok( const string &, const string &, const string &,
  double = 0.0, double = 0.0, double = 0.0 );

  void setGajiPokok( double ); // menetapkan gaji pokok
  double getGajiPokok() const; // mengembalikan gaji pokok

  virtual double penghasilan() const; // menghitung penghasilan
  virtual void tampil() const; // menampilkan objek KaryawanKomisiPlusPokok
private:
  double gajiPokok; // gaji pokok
}; // akhir dari kelas KaryawanKomisiPlusPokok

#endif

Anda akan memodifikasi Gambar 5.1 untuk menciptakan program pada Gambar 5.6. Baris 40-51 mendemonstrasikan kembali bahwa pointer kelas KaryawanKomisi yang diarahkan kepada objek KaryawanKomisi dapat dipakai untuk memanggil fungsionalitas KaryawanKomisi, dan bahwa pointer kelas KaryawanKomisiPlusPokok yang diarahkan kepada objek KaryawanKomisiPlusPokok dapat dipakai untuk memanggil fungsionalitas KaryawanKomisiPlusPokok. Baris 54 mengarahkan pointer kelas basis karyawanKomisiPtr pada objek kelas terderivasi karyawanKomisiPlusPokok. Perhatikan bahwa ketika baris 61 memanggil fungsi anggota tampil melalui pointer kelas basis, fungsi anggota tampil kelas KaryawanKomisiPlusPokok lah yang dipanggil, jadi baris 61 menampilkan teks berbeda dari yang ditampilkan oleh baris 53 pada Gambar 5.1 (ketika fungsi anggota tampil tidak dideklarasikan sebagai virtual). Dapat dilihat bahwa pendeklarasikan suatu fungsi anggota sebagai virtual menyebabkan program menentukan secara dinamis fungsi mana yang dipanggil berdasarkan tipe objek yang ditunjuk, bukan berdasarkan tipe pointer yang menunjuk.

Perhatikan kembali bahwa ketika karyawanKomisiPtr menunjuk ke objek KaryawanKomisi (baris 40), fungsi tampil kelas KaryawanKomisi lah yang dipanggil, dan ketika karyawanKomisiPtr menunjuk ke objek kelas KaryawanKomisiPlusPokok, fungsi tampil pada kelas KaryawanKomisiPlusPokok lah yang dipanggil. Jadi, pesan yang sama, dalam hal ini adalah tampil melalui suatu pointer kelas basis yang menunjuk ke berbagai objek yang berelasi dalam hirarki pewarisan dengan kelas basis tersebut, mempunyai banyak bentuk. Inilah yang dikatakan dengan watak polimorfik.

Gambar 5.6 Mendemonstrasikan Polimorfisme dengan Memanggil Fungsi virtual terderivasi melalui Pointer Kelas Basis yang Menunjuk ke Objek Kelas Terderivasi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// Gambar 5.6: gambar5_06.cpp
// mengintroduksi polimorfik, fungsi virtual, dan pengikatan dinamis.
#include <iostream>
#include <iomanip>
#include "KaryawanKomisi.h"
#include "KaryawanKomisiPlusPokok.h"
using namespace std;

int main()
{
  // menciptakan objek kelas basis
  KaryawanKomisi karyawanKomisi(
    "Robert", "Tohonan", "222-22-2222", 10000, .06 );

  // menciptakan pointer kelas basis
  KaryawanKomisi *karyawanKomisiPtr = 0;

  // menciptakan objek kelas terderivasi
  KaryawanKomisiPlusPokok karyawanKomisiPlusPokok(
    "Rico", "Chandra", "333-33-3333", 5000, .04, 300 );

  // menciptakan pointer kelas terderivasi
  KaryawanKomisiPlusPokok *karyawanKomisiPlusPokokPtr = 0;

  // menetapan format keluaran pecahan
  cout << fixed << setprecision( 2 );

  // menampilkan objek menggunakan pengikatan statis
  cout << "Menampilkan fungsi tampil pada objek kelas basis "
    << "\ndan objek kelas terderivasi dengan pengikatan statis\n\n";
  karyawanKomisi.tampil(); // pengikatan statis
  cout << "\n\n";
  karyawanKomisiPlusPokok.tampil(); // pengikatan statis

  // menampilkan objek menggunakan pengikatan statis
  cout << "Menampilkan fungsi tampil pada objek kelas basis "
    << "\ndan objek kelas terderivasi dengan pengikatan dinamis\n\n";
 
  // mengarahkan pointer kelas basis pada objek kelas basis dan tampil
  karyawanKomisiPtr = &karyawanKomisi;
  cout << "\n\n\nPemanggilan fungsi virtual tampil dengan pointer kelas basis"
    << "\nyang menunjuk ke objek kelas basis memanggil "
    <<"fungsi tampil kelas basis:\n\n";
  karyawanKomisiPtr->tampil(); // memanggil tampil kelas basis

  // mengarahkan pointer kelas terderivasi pada objek kelas terderivasi dan tampil
  karyawanKomisiPlusPokokPtr = &karyawanKomisiPlusPokok;
  cout << "\n\n\nPemanggilan fungsi virtual tampil dengan pointer kelas terderivasi"
    << "\nyang menunjuk ke objek kelas terderivasi memanggil "
    <<"fungsi tampil kelas terderivasi:\n\n";
  karyawanKomisiPlusPokokPtr->tampil(); // memanggil tampil kelas terderivasi

  // mengarahkan pointer kelas basis pada objek kelas terderivasi dan tampil
  karyawanKomisiPtr = &karyawanKomisiPlusPokok;
  cout << "\n\n\nPemanggilan fungsi virtual tampil dengan pointer kelas basis"
    << "\nyang menunjuk ke objek kelas terderivasi memanggil "
    <<"fungsi tampil kelas terderivasi:\n\n";
 
  // polimorfisme; memanggil fungsi tampil kelas KaryawanKomisiPlusPokok;
  // pointer kelas basis menunjuk ke objek kelas terderivasi
  karyawanKomisiPtr->tampil();
  cout << endl;
} // akhir dari main

Menampilkan fungsi tampil pada objek kelas basis
dan objek kelas terderivasi dengan pengikatan statis

karyawan komisi: Robert Tohonan
nomor kartu penduduk: 222-22-2222
penjualan kotor: 10000.00
besar komisi: 0.06

karyawan komisi bergaji pokok: Rico Chandra
nomor kartu penduduk: 333-33-3333
penjualan kotor: 5000.00
besar komisi: 0.04
gaji pokok: 300.00

Menampilkan fungsi tampil pada objek kelas basis
dan objek kelas terderivasi dengan pengikatan dinamis

Pemanggilan fungsi virtual tampil dengan pointer kelas basis
yang menunjuk ke objek kelas basis memanggil fungsi tampil kelas basis:

karyawan komisi: Robert Tohonan
nomor kartu penduduk: 222-22-2222
penjualan kotor: 10000.00
besar komisi: 0.06

Pemanggilan fungsi virtual tampil dengan pointer kelas terderivasi
yang menunjuk ke objek kelas terderivasi memanggil fungsi tampil kelas terderivasi:

karyawan komisi bergaji pokok: Rico Chandra
nomor kartu penduduk: 333-33-3333
penjualan kotor: 5000.00
besar komisi: 0.04
gaji pokok: 300.00

Pemanggilan fungsi virtual tampil dengan pointer kelas basis
yang menunjuk ke objek kelas terderivasi memanggil fungsi tampil kelas terderivasi:

karyawan komisi bergaji pokok: Rico Chandra
nomor kartu penduduk: 333-33-3333
penjualan kotor: 5000.00
besar komisi: 0.04
gaji pokok: 300.00

5.4 Kelas Abstrak dan Fungsi virtual Murni
Ketika Anda berpikir bahwa sebuah kelas adalah suatu tipe, diasumsikan bahwa program akan menciptakan objek dengan tipe tersebut. Namun, terdapat kasus dimana didefinisikan kelas yang Anda tidak pernah berniat menginstansiasi objek dari kelas itu. Kelas semacam itu dikenal dengan kelas abstrak. Karena kelas abstrak normalnya dipakai sebagai kelas basis di dalam hirarki pewarisan, kelas tersebut dipanggil dengan kelas basis abstrak. Kelas ini tidak bisa dipakai untuk menginstansiasi objek, karena merupakan kelas yang tidak utuh.

Kelas abstrak dapat digunakan sebagai kelas yang dapat diwarisi oleh kelas lainnya. Kelas yang dapat dipakai untuk menginstansiasi objek dinamakan dengan kelas konkrit. Kelas semacam itu mendefinisikan atau mengimplementasikan setiap fungsi anggota yang dideklarasikan. Anda bisa memiliki suatu kelas basis abstrak BangunDuaDimensi dan mewariskan kepada tiga kelas konkrit SegiTiga, Lingkaran, dan SegiEmpat. Anda juga bisa memiliki suatu kelas basis abstrak BangunTigaDimensi sehingga kelas konkrit Kubus, Silinder, dan Bola dapat mewarisi dari kelas basis abstrak tersebut. Kelas basis abstrak terlalu generik untuk mendefinisikan objek riil; Anda perlu lebih spesifik sebelum berpikir untuk menginstansiasi objek. Sebagai contoh, jika seseorang memberitahu Anda untuk “menggambar bangun dua dimensi”, bangun apakah yang akan Anda gambar? Kelas konkrit menyediakan implementasi spesifik yang memungkinkan untuk menginstansiasi objek.

Hirarki pewarisan tidak harus memuat kelas abstrak, tetapi banyak sistem berorientasi objek memiliki hirarki kelas dengan kelas basis abstrak. Dalam beberapa kasus, kelas abstrak berada di level teratas hirarki pewarisan. Salah satu contohnya adalah hirarki bangun pada Gambar 4.3, yang dimulai dengan kelas basis abstrak Bangun. Pada level hirarki berikutnya, Anda memiliki dua kelas basis abstrak, BangunDuaDimensi dan BangunTigaDimensi. Pada level hirarki berikutnya Anda mendefinisikan beberapa kelas konkrit untuk bangun dua dimensi (sebut saja, Lingkaran, SegiEmpat, dan SegiTiga) dan untuk bangun tiga dimensi (sebut saja, Kubus, Silinder, dan Bola).

Fungsi virtual Murni
Suatu kelas dijadikan abstrak dengan mendeklarasikan satu atau lebih fungsi virtual-nya menjadu “murni”. Fungsi virtual murni dispesifikasi  dengan menempatkan “= 0” di dalam deklarasinya, seperti

virtual void gambar() const = 0; // fungsi virtual murni

“= 0” merupakan penspesifikasi murni. Fungsi virtual murni tidak menyediakan implementasi. Setiap kelas terderivasi konkrit harus mendefinisikan-ulang  semua fungsi virtual murni kelas basis dengan implementasi konkrit dari semua fungsi tersebut. Perbedaan antaran fungsi virtual dan fungsi virtual murni adalah bahwa fungsi virtual memiliki sebuah implementasi dan memberikan opsi bagi kelas terderivasi untuk mendefinisikannya-ulang; sebaliknya, fungsi virtual murni tidak menyediakan sebuah implementasi dan mensyaratkan kelas terderivasi untuk mendefinisikan-ulang fungsi tersebut konkrit; jika tidak, kelas terderivasi tetap abstrak. Fungsi virtual murni digunakan ketika tidak masuk akal untuk kelas basis dalam memiliki implementasi suatu fungsi, tetapi Anda menginginkan semua kelas terderivasi konkrit untuk mengimplementasikan fungsi tersebut.

Meskipun Anda tidak bisa menginstansiasi objek dari kelas basis abstrak, Anda dapat menggunakannya untuk mendeklarasikan pointer dan referensi yang dapat menunjuk ke objek dari sembarang kelas konkrit yang diderivasi dari kelas abstrak tersebut. Program umumnya menggunakan pointer dan referensi semacam itu untuk memanipulasi objek-objek kelas terderivasi secara polimorfik.

5.5 Studi Kasus: Sistem Penggajian Menggunakan Polimorfisme
Subbab ini akan menguji-ulang hirarki KaryawanKomisi-KaryawanKomisiPlusPokok yang telah dipelajari. Pada contoh ini, Anda menggunakan sebuah kelas abstrak dan polimorfisme untuk melakukan perhitungan penggajian berdasarkan tipe karyawan. Anda menciptakan hirarki karyawan terperbaiki untuk menyelesaikan masalah berikut:

Sebuah perusahaan menggaji karyawannya secara mingguan. Ada tiga tipe karyawan: Karyawan bergaji dibayar dengan gaji tetap mingguan tanpa memandang jumlah jam kerja, karyawan komisi dibayar berdasarkan persentase hasil penjualannya, karyawan komisi bergaji pokok menerima gaji pokok ditambah dengan persentasi hasil penjualannya. Untuk periode penggajian saat ini, perusahaan memutuskan untuk memberikan bonus kepada karyawan komisi bergaji pokok dengan menambah 10 persen dari gaji pokoknya. Perusahaan ingin mengimplementasikan sebuah program C++ yang bisa melakukan perhitungan penggajian secara polimorfik.

Akan digunakan kelas abstrak Karyawan untuk merepresentasikan konsep umum seorang karyawan. Dua kelas yang mewarisi Karyawan secara langsung adalah KaryawanBergaji dan KaryawanKomisi. Kelas KaryawanKomisiPlusPokok, yang diderivasi dari KaryawanKomisi, merepresentasikan tipe elemen terakhir. Diagram kelas UML pada Gambar 5.7 menampilkan hirarki pewarisan untuk aplikasi penggajian karyawan polimorfik.

Gambar 5.7 Diagram kelas UML untuk hirarki Karyawan

Kelas basis abstrak Karyawan mendeklarasikan “antarmuka” pada hirarki, yaitu sehimpunan fungsi anggota yang dapat dipanggil sebuah program pada semua objek Karyawan. Setiap karyawan, tanpa memandang cara ia digaji dan dihitung, memiliki nama pertama, nama akhir, dan nomor kartu penduduk, sehingga anggota data private seperti namaPertama, namaAkhir, dan nomorKTP diperlukan di dalam kelas basis abstrak Karyawan.

Subbab berikut mengimplementasikan hirarki kelas Karyawan. Masing-masing mengimplementasikan satu  kelas abstrak atau satu kelas konkrit. Bagian terakhir mengimplementasikan program uji yang menciptakan beberapa objek dari semua kelas tersebut dan memprosesnya secara polimorfik.

5.5.1 Menciptakan Kelas Basis Abstrak Karyawan
Kelas Karyawan (Gambar 5.9 – 5.10) menyediakan dua fungsi penghasilan dan tampil, selain beberapa fungsi get dan set yang memanipulasi anggota-anggota data Karyawan. Fungsi penghasilan tentu saja berlaku secara generik pada semua karyawan, tetapi setiap penghitungan penghasilan bergantung pada kelas karyawan. Jadi, penghasilan dideklarasikan sebagai virtual murni di dalam kelas basis Karyawan karena implementasi default tidak masuk akal untuk fungsi tersebut.

Setiap kelas terderivasi mendefinisikan-ulang penghasilan dengan implementasi yang sesuai. Untuk menghitung penghasilan seorang karyawan, program menugaskan alamat dari objek karyawan kepada pointer kelas basis Karyawan, kemudian memanggil fungsi penghasilan pada objek tersebut. Ditetapkan suatu vector yang memuat pointer-pointer Karyawan, masing-masing menunjuk kepada objek Karyawan. Tentu saja, tidak ada objek Karyawan, karena Karyawan adalah kelas abstrak. Karena pewarisan, semua objek kelas terderivasi dari kelas Karyawan adalah objek Karyawan. Program beriterasi menjelajahi vector dan memanggil fungsi penghasilan untuk setiap objek Karyawan. C++ memproses semua pemanggilan fungsi tersebut secara polimorfik. Pencantuman penghasilan sebagai fungsi virtual murni di dalam Karyawan memaksa setiap kelas terderivasi langsung dari kelas Karyawan yang diinginkan menjadi kelas konkrit untuk mendefinisikan-ulang penghasilan.


Gambar 5.8 Antarmuka polimorfik untuk hirarki Karyawan


Fungsi tampil di dalam kelas Karyawan menampilkan nama pertama, nama akhir, dan nomor kependudukan dari seorang karyawan. Seperti yang akan Anda lihat, setiap kelas terderivasi dari kelas Karyawan mendefinisikan-ulang fungsi tampil untuk menampilkan tipe karyawan (misalnya, “karyawan bergaji: “) diikuti dengan informasi lainnya. Fungsi tampil di dalam kelas terderivasi juga dapat memanggil penghasilan, meskipun penghasilan merupakan fungsi virtual murni di dalam kelas basis Karyawan.

Diagram pada Gambar 5.8 menampilkan empat kelas di dalam hirarki, yang masing-masing dengan fungsi penghasilan dan tampil. Untuk setiap kelas, diagram menampilkan hasil yang diinginkan setiap fungsi. Kelas Karyawan menspesifikasi “= 0” untuk fungsi penghasilan untuk mengindikasikan bahwa fungsi tersebut adalah virtual murni dan, oleh karena itu, tidak memiliki implementasi. Setiap kelas terderivasi mendefinisikan-ulang fungsi ini untuk menyediakan implementasi yang sesuai. Anda tidak mencantumkan fungsi get dan set kelas basis karena tidak didefinisikan-ulang di dalam sembarang kelas terderivasi.

Header Kelas Karyawan
Sekarang mari kita tengok header kelas Karyawan (Gambar 5.9). Fungsi-fungsi anggota public menyertakan sebuah konstruktor yang mengambil nama pertama, nama akhir, dan nomor kependudukan sebagai argumen (baris 12); beberapa fungsi set menetapkan nama pertama, nama akhir, dan nomor kependudukan (baris 14, 17, dan 20); beberapa fungsi get menghasilkan nilai balik berupa nama pertama, nama akhir, dan nomor kependudukan (baris 15, 18, dan 21); fungsi virtual (penghasilan) pada baris 24 dan fungsi virtual (tampil) pada baris 25.

Gambar 5.9 Kelas Basis Abstrak Karyawan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Gambar 5.9: Karyawan.h
// Kelas basis abstrak Karyawan
#ifndef KARYAWAN_H
#define KARYAWAN_H

#include <string> // Kelas string standard C++
using namespace std;

class Karyawan
{
public:
  Karyawan( const string &, const string &, const string &);

  void setNamaPertama( const string & ); // menetapkan nama pertama
  string getNamaPertama() const; // mengembalikan nama pertama

  void setNamaAkhir( const string & ); // menetapkan nama akhir
  string getNamaAkhir() const; // mengembalikan nama akhir

  void setNomorKTP( const string & ); // menetapkan nomor KTP
  string getNomorKTP() const; // mengembalikan nomor KTP

  // fungsi virtual murni membuat Karyawan sebagai kelas basis abstrak
  virtual double penghasilan() const = 0; // virtual murni
  virtual void tampil() const; // virtual
private:
  string namaPertama;
  string namaAkhir;
  string nomorKTP;
}; // akhir dari kelas Karyawan

#endif

Ingat bahwa Anda mendeklarasikan penghasilan sebagai fungsi virtual murni karena pertama-tama Anda harus mengetahui tipe Karyawan spesifik untuk menentukan penghitungan penghasilan. Pendeklarasian fungsi ini sebagai virtual murni mengindikasikan bahwa setiap kelas terderivasi konkrit harus menyediakan implementasi penghasilan dan bahwa suatu program dapat menggunakan pointer kelas basis Karyawan untuk memanggil fungsi penghasilan secara polimorfik untuk sembarang tipe Karyawan.

Definisi Fungsi Anggota Kelas Karyawan
Gambar 5.10 memuat implementasi fungsi anggota untuk kelas Karyawan. Tidak ada implementasi yang disediakan untuk fungsi virtual (penghasilan). Konstruktor Karyawan (baris 9-14) tidak memvalidasi nomor kependudukan. Normalnya, validasi semacam itu seharusnya dilakukan.

Gambar 5.10 Definisi Fungsi Anggota Kelas Basis Abstrak Karyawan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Gambar 5.10: Karyawan.cpp
// Definisi fungsi anggota kelas basis abstrak Karyawan.
// Catatan: Tidak ada definisi untuk fungsi virtual.
#include <iostream>
#include "Karyawan.h" // Definisi kelas Karyawan
using namespace std;

// konstruktor
Karyawan::Karyawan( const string &pertama, const string &akhir,
    const string &ktp )
    : namaPertama( pertama ), namaAkhir( akhir ), nomorKTP( ktp )
{
  // tubuh kosong
} // akhir dari konstruktor Karyawan

// menetapkan nama pertama
void Karyawan::setNamaPertama( const string &pertama )
{
  namaPertama = pertama;
} // akhir dari fungsi setNamaPertama

// mengembalikan nama pertama
string Karyawan::getNamaPertama() const
{
  return namaPertama;
} // akhir dari fungsi namaPertama

// menetapkan nama akhir
void Karyawan::setNamaAkhir( const string &akhir )
{
  namaAkhir = akhir;
} // akhir dari fungsi setNamaAkhir

// mengembalikan nama akhir
string Karyawan::getNamaAkhir() const
{
  return namaAkhir;
} // akhir dari fungsi getNamaAkhir

// menetapkan nomor kependudukan
void Karyawan::setNomorKTP( const string &ktp )
{
  nomorKTP = ktp; // seharusnya divalidasi
} // akhir dari fungsi setNomorKTP

// mengembalikan nomor kependudukan
string Karyawan::getNomorKTP() const
{
  return nomorKTP;
} // akhir dari fungsi getNomorKTP

// informasi tampil kelas Karyawan (virtual, tetapi tidak virtual murni)
void Karyawan::tampil() const
{
  cout << getNamaPertama() << ' ' << getNamaAkhir()
    << "\nnomor kependudukan: " << getNomorKTP();
} // akhir dari fungsi tampil

Fungsi virtual tampil (Gambar 5.10, baris 53-57) menyediakan sebuah implementasi yang dapat didefinisikan-ulang di dalam setiap kelas terderivasi. Setiap fungsi tersebut akan, bagaimanapun, menggunakan versi tampil kelas basis untuk menampilkan informasi umum yang berkaitan dengan semua kelas di dalam hirarki Karyawan.

5.5.2 Menciptakan Kelas Terderivasi Konkrit KaryawanBergaji
Kelas KaryawanBergaji (Gambar 5.11 – 5.12) mewarisi dari kelas Karyawan (baris 8 pada Gambar 5.11). Fungsi anggota public menyertakan sebuah konstruktor yang mengambil nama pertama, nama akhir, nomor kependudukan, dan gaji mingguan sebagai argumen (baris 11-12); sebuah fungsi set untuk menugaskan nilai non-negatif baru pada anggota data gajiMingguan (baris 14); sebuah fungsi get untuk menghasilkan nilai balik berupa nilai gajiMingguan (baris 15); sebuah fungsi virtual penghasilan yang menghitung penghasilan KaryawanGajian (baris 18) dan sebuah fungsi virtual tampil (baris 19) yang menampilkan tipe karyawan, misalnya, “karyawan bergaji: “ diikuti dengan informasi spesifik tentang karyawan yang dihasilkan oleh fungsi tampil kelas basis Karyawan dan fungsi getGajiMingguan kelas KaryawanBergaji.

Gambar 5.11 Kelas KaryawanBergaji Diderivasi dari Karyawan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Gambar 5.11: KaryawanBergaji.h
// Kelas KaryawanBergaji diderivasi dari Karyawan.
#ifndef BERGAJI_H
#define BERGAJI_H

#include "Karyawan.h" // Definisi kelas Karyawan

class KaryawanBergaji : public Karyawan
{
public:
  KaryawanBergaji( const string &, const string &,
    const string &, double = 0.0 );

  void setGajiMingguan( double ); // menetapkan gaji mingguan
  double getGajiMingguan() const; // mengembalikan gaji mingguan

  // katakunci virtual menandakan pendefinisian-ulang
  virtual double penghasilan() const; // menghitung penghasilan
  virtual void tampil() const; // menampilkan objek KaryawanBergaji
private:
  double gajiMingguan; // gaji per minggu
}; // akhir dari kelas KaryawanBergaji

#endif // BERGAJI_H

Definisi Fungsi Anggota Kelas KaryawanBergaji
Gambar 5.12 memuat implementasi fungsi anggota untuk kelas KaryawanBergaji. Konstruktor kelas ini melewatkan nama pertama, nama akhir, dan nomor kependudukan kepada konstruktor Karyawan (baris 10) untuk menginisialisasi anggota-anggota data private yang diwarisi dari kelas basis, tetapi tidak secara langsung dapat diakses dari kelas terderivasi. Fungsi penghasilan (baris 32-35) mendefinisikan-ulang fungsi virtual murni penghasilan di dalam Karyawan untuk menyediakan implementasi konkrit yang menghasilkan nilai balik berupa gaji mingguan KaryawanBergaji. Jika Anda tidak mengimplementasikan penghasilan, kelas KaryawanBergaji harus menjadi kelas abstrak, dan sembarang percobaan untuk menginstansiasi objek dari kelas tersebut akan menghasilkan error kompilasi (dan, tentu saja, Anda menginginkan KaryawanBergaji di sini menjadi kelas konkrit). Dalam header kelas KaryawanBergaji, Anda mendeklarasikan fungsi anggota penghasilan dan tampil sebagai virtual (baris 18 -19 pada Gambar 5.11). Sebenarnya penempatan katakunci virtual sebelum kedua fungsi tersebut adalah berlebihan (boleh tidak dilakukan). Anda telah mendefinisikan keduanya sebagai virtual di dalam kelas basis Karyawan, jadi keduanya tetap menjadi fungsi virtual di seluruh hirarki kelas. Pendeklarasian eksplisit sebagai virtual pada setiap level hirarki dapat membuat program lebih jelas.

Gambar 5.12 Definisi Fungsi Anggota Kelas KaryawanBergaji

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Gambar 5.12: KaryawanBergaji.cpp
// Definisi fungsi anggota kelas KaryawanBergaji.
#include <iostream>
#include "KaryawanBergaji.h" // definisi kelas KaryawanBergaji
using namespace std;

// konstruktor
KaryawanBergaji::KaryawanBergaji( const string &pertama,
    const string &akhir, const string &ktp, double gaji )
    : Karyawan( pertama, akhir, ktp )
{
  setGajiMingguan( gaji );
} // akhir dari konstruktor KaryawanBergaji

// menetapkan gaji
void KaryawanBergaji::setGajiMingguan( double gaji )
{
  if ( gaji >= 0.0 )
    gajiMingguan = gaji;
  else
    throw invalid_argument( "Gaji mingguan harus >= 0.0" );
} // akhir dari fungsi setWeeklySalary

// mengembalikan gaji
double KaryawanBergaji::getGajiMingguan() const
{
  return gajiMingguan;
} // akhir dari fungsi getGajiMingguan

// menghitung penghasilan;
// mendefinisikan ulang fungsi virtual murni penghasilan dalam Karyawan
double KaryawanBergaji::penghasilan() const
{
  return getGajiMingguan();
} // akhir dari fungsi penghasilan

// menampilkan informasi KaryawanBergaji
void KaryawanBergaji::tampil() const
{
  cout << "karyawan bergaji: ";
  Karyawan::tampil(); // mendaur-ulang fungsi tampil kelas basis abstrak
  cout << "\ngaji mingguan: " << getGajiMingguan();
} // akhir dari fungsi tampil

Fungsi tampil pada kelas KaryawanBergaji (baris 38-43 pada Gambar 5.12) mendefinisikan-ulang fungsi tampil kelas Karyawan. Jika kelas KaryawanBergaji tidak mendefinisikan-ulang tampil, maka KaryawanBergaji akan mewarisi versi tampil kelas Karyawan. Pada kasus itu, fungsi tampil kelas KaryawanBergaji hanya memberikan nilai balik berupa nama penuh karyawan dan nomor ktp-nya, yang tidak cukup merepresentasikan KaryawanBergaji. Untuk menampilkan informasi utuh KaryawanBergaji, fungsi tampil kelas terderivasi menampilkan “karyawan bergaji: “ diikuti dengan informasi spesifik kelas basis Karyawan (nama pertama, nama akhir, dan nomor kependudukan) yang ditampilkan dengan memanggil fungsi tampil kelas basis menggunakan operator resolusi skop (baris 41). Ini merupakan contoh baik dari pendaur-ulangan kode. Tanpa operator resolusi skop, pemanggilan tampil akan menyebabkan rekursi tak-terhingga.Keluaran yang dihasilkan oleh fungsi tampil kelas KaryawanBergaji memuat gaji mingguan karyawan yang diperoleh dengan memanggil fungsi getGajiMingguan kelas KaryawanBergaji.

5.5.3 Menciptakan Kelas Terderivasi Konkrit KaryawanKomisi
Kelas KaryawanKomisi (Gambar 5.13 – 5.14) mewarisi dari Karyawan (Gambar 5.13, baris 8). Implementasi fungsi anggota (Gambar 5.14) menyertakan sebuah konstruktor (baris 8-14) yang mengambil nama pertama, nama akhir, nomor kependudukan, jumlah penjualan, dan besar komisi sebagai argumen; beberapa fungsi set (baris 17-23 dan 32-38) untuk menugaskan nilai-nilai baru kepada anggota-anggota data besarKomisi dan penjualanKotor; beberapa fungsi get (baris 26-29 dan baris 41-44) untuk mengambil nilai-nilai tersebut; fungsi penghasilan (baris 47-50) untuk menghitung penghasilan KaryawanKomisi; dan fungsi tampil (baris 53-59) untuk menampilkan tipe karyawan, misalnya “karyawan komisi: “ dan informasi spesifik karyawan. Konstruktor melewatkan nama pertama, nama akhir, dan nomor kependudukan kepada konstruktor Karyawan (baris 10) untuk menginisialisasi anggota-anggota data private kelas Karyawan. Fungsi tampil memanggil tampil kelas basis (baris 56) untuk menampilkan informasi spesifik kelas Karyawan.

Gambar 5.13 Kelas KaryawanKomisi Diderivasi dari Karyawan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Gambar 5.13: KaryawanKomisi.h
// Kelas KaryawanKomisi diderivasi dari Karyawan.
#ifndef KOMISI_H
#define KOMISI_H

#include "Karyawan.h" // definisi kelas Karyawan

class KaryawanKomisi : public Karyawan
{
public:
  KaryawanKomisi( const string &, const string &,
    const string &, double = 0.0, double = 0.0 );

  void setBesarKomisi( double ); // menetapkan besar komisi
  double getBesarKomisi() const; // mengembalikan besar komisi

  void setPenjualanKotor( double ); // menetapkan jumlah penjualan kotor
  double getPenjualanKotor() const; // mengembalikan jumlah penjualan kotor

  // katakunci virtual menandakan untuk mendefinisikan-ulang
  virtual double penghasilan() const; // menghitung penghasilan
  virtual void tampil() const; // menampilkan objek KaryawanKomisi
private:
  double penjualanKotor; // penjualan kotor mingguan
  double besarKomisi; // persentase komisi
}; // akhir dari kelas KaryawanKomisi

#endif // KOMISI_H

Gambar 5.13 Definisi Fungsi Anggota Kelas KaryawanKomisi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// Gambar 5.13: KaryawanKomisi.cpp
// Definisi fungsi anggota kelas KaryawanKomisi.
#include <iostream>
#include "KaryawanKomisi.h" // definisi kelas KaryawanKomisi
using namespace std;

// konstruktor
KaryawanKomisi::KaryawanKomisi( const string &pertama, const string &akhir,
    const string &ktp, double penjualan, double komisi )
    : Karyawan( pertama, akhir, ktp )
{
  setPenjualanKotor( penjualan ); // memvalidasi dan menyimpan penjualan kotor
  setBesarKomisi( komisi ); // memvalidasi dan menyimpan besar komisi
} // akhir dari konstruktor KaryawanKomisi

// menetapkan jumlah penjualan kotor
void KaryawanKomisi::setPenjualanKotor( double penjualan )
{
  if ( penjualan >= 0.0 )
    penjualanKotor = penjualan;
  else
    throw invalid_argument( "penjualan kotor harus >= 0.0" );
} // akhir dari fungsi setPenjualanKotor

// menghasilkan jumlah penjualan kotor
double KaryawanKomisi::getPenjualanKotor() const
{
  return penjualanKotor;
} // akhir dari fungsi penjualanKotor

// menetapkan besar komisi
void KaryawanKomisi::setBesarKomisi( double komisi )
{
  if ( komisi > 0.0 && komisi < 1.0 )
    besarKomisi = komisi;
  else
    throw invalid_argument( "besar komisi harus di antara > 0.0 dan < 1.0" );
} // akhir dari fungsi setBesarKomisi

// mengembalikan besar komisi
double KaryawanKomisi::getBesarKomisi() const
{
  return besarKomisi;
} // akhir dari fungsi getBesarKomisi

// menghitung penghasilan
double KaryawanKomisi::penghasilan() const
{
  return getBesarKomisi() * getPenjualanKotor();
} // akhir dari fungsi penghasilan

// menampilkan objek KaryawanKomisi
void KaryawanKomisi::tampil() const
{
  cout << "karyawan komisi: ";
  Karyawan::tampil(); // pendaur-ulangan kode
  cout << "\npenjualan kotor: " << getPenjualanKotor()
    << "; besar komisi: " << getBesarKomisi();
} // akhir dari fungsi tampil

5.5.4 Menciptakan Kelas Terderivasi Konkrit Tak-Langsung KaryawanKomisiPlusPokok
Kelas KaryawanKomisiPlusPokok (Gambar 5.15 – 5.16) secara langsung mewarisi dari kelas KaryawanKomisi (baris 8 pada Gambar 5.15) dan oleh karena itu merupakan kelas terderivasi tak-langsung dari kelas Karyawan. Implementasi fungsi anggota kelas KaryawanKomisiPlusPokok menyertakan sebuah konstruktor (baris 8 -14 pada Gambar 5.16) yang mengambil sebagai argumen nama pertama, nama akhir, nomor kependudukan, jumlah penjualan, besar komisi, dan gaji pokok. Konstruktor tersebut kemudian melewatkan nama pertama, nama akhir, nomor kependudukan, jumlah penjualan, dan besar komisi kepada konstruktor KaryawanKomisi (baris 11) untuk menginisialisasi anggota-anggota data terwarisi. Kelas KaryawanKomisiPlusPokok juga memuat sebuah fungsi set (baris 17-23) untuk menugaskan nilai baru kepada anggota data gajiPokok dan sebuah fungsi get untuk menghasilkan nilai balik berupa gajiPokok. Fungsi penghasilan (baris 33-36) menghitung penghasilan kelas KaryawanKomisiPlusPokok. Baris 35 pada fungsi penghasilan memanggil fungsi penghasilan kelas basis KaryawanKomisi untuk menghitung porsi berbasis komisi dari penghasilan karyawan. Ini merupakan contoh bagus untuk pendaur-ulangan kode. Fungsi tampil kelas KaryawanKomisiPlusPokok (baris 39 – 44) menampilkan “bergaji-pokok”, diikuti dengan keluaran fungsi tampil kelas KaryawanKomisi, kemudian gaji pokok. Keluaran yang dihasilkan dimulai dengan “karyawan komisi bergaji-pokok: “ diikuti dengan sisa informasi dari KaryawanKomisiPlusPokok. Ingat bahwa fungsi tampil kelas KaryawanKomisi menampilkan nama pertama, nama akhir, dan nomor kependudukan dengan cara memanggil fungsi tampil kelas basisnya (Karyawan). Fungsi tampil kelas KaryawanKomisiPlusPokok menginisiasi rantai pemanggilan fungsi yang berada di dalam tiga level hirarki Karyawan.

Gambar 5.15 Header Kelas KaryawanKomisiPlusPokok

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Gambar 5.15: KaryawanKomisiPlusPokok.h
// Kelas KaryawanKomisiPlusPokok diderivasi dari kelas KaryawanKomisi.
#ifndef POKOK_PLUS_H
#define POKOK_PLUS_H

#include "KaryawanKomisi.h"

class KaryawanKomisiPlusPokok : public KaryawanKomisi
{
public:
  KaryawanKomisiPlusPokok( const string &, const string &, const string &,
  double = 0.0, double = 0.0, double = 0.0 );

  void setGajiPokok( double ); // menetapkan gaji pokok
  double getGajiPokok() const; // mengembalikan gaji pokok

  virtual double penghasilan() const; // menghitung penghasilan
  virtual void tampil() const; // menampilkan objek KaryawanKomisiPlusPokok
private:
  double gajiPokok; // gaji pokok
}; // akhir dari kelas KaryawanKomisiPlusPokok

#endif

Gambar 5.16 Definisi Fungsi Anggota Kelas KaryawanKomisiPlusPokok

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Gambar 5.16: KaryawanKomisiPlusPokok.cpp
// Definisi fungsi anggota kelas KaryawanKomisiPlusPokok.
#include <iostream>
#include "KaryawanKomisiPlusPokok.h" // definisi kelas KaryawanKomisiPlusPokok
using namespace std;

// konstruktor
KaryawanKomisiPlusPokok::KaryawanKomisiPlusPokok(
    const string &pertama, const string &akhir, const string &ktp,
    double penjualan, double komisi, double gaji )
    : KaryawanKomisi( pertama, akhir, ktp, penjualan, komisi )
{
  setGajiPokok( gaji ); // memvalidasi dan menyimpan gaji pokok
} // akhir dari konstruktor KaryawanKomisiPlusPokok

// menetapkan gaji pokok
void KaryawanKomisiPlusPokok::setGajiPokok( double gaji )
{
  if ( gaji >= 0.0 )
    gajiPokok = gaji;
  else
    throw invalid_argument( "Gaji pokok harus >= 0.0" );
} // akhir dari fungsi setGajiPokok

// mengembalikan gaji pokok
double KaryawanKomisiPlusPokok::getGajiPokok() const
{
  return gajiPokok;
} // akhir dari fungsi getGajiPokok

// menghasilkan penghasilan
double KaryawanKomisiPlusPokok::penghasilan() const
{
  return getGajiPokok() + KaryawanKomisi::penghasilan();
} // akhir dari fungsi penghasilan

// menampilkan objek KaryawanKomisiPlusPokok
void KaryawanKomisiPlusPokok::tampil() const
{
  cout << "bergaji-pokok ";
  KaryawanKomisi::tampil();
  cout << "; gaji pokok: " << getGajiPokok();
} // akhir dari fungsi tampil

5.5.5 Mendemonstrasikan Pemrosesan Polimorfik
Untuk menguji hirarki Karyawan, program pada Gambar 5.17 menciptakan sebuah objek dari tiap kelas konkrit KaryawanBergaji, KaryawanKomisi, dan KaryawanKomisiPlusPokok. Progam memanipulasi ketiga objek tersebut, pertama dengan pengikatan statis, kemudian secara polimorfik, menggunakan sebuah vector yang memuat pointer-pointer Karyawan. Baris 22-27 menciptakan objek dari tiap kelas terderivasi konkrit Karyawan. Baris 32-38 menampilkan setiap informasi Karyawan dan penghasilannya. Setiap pemanggilan fungsi anggota pada baris 32-37 merupakan contoh pengikatan statis.

Gambar 5.17 Program Uji Hirarki Karyawan secara Polimorfik

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// Gambar 5.17: gambar5_17.cpp
// Memproses objek-objek kelas terderivasi dari Karyawan secara individual
// dan secara polimorfikal menggunakan pengikatan dinamis.
#include <iostream>
#include <iomanip>
#include <vector>
#include "Karyawan.h"
#include "KaryawanBergaji.h"
#include "KaryawanKomisi.h"
#include "KaryawanKomisiPlusPokok.h"
using namespace std;

void virtualViaPointer( const Karyawan * const ); // prototipe
void virtualViaReferensi( const Karyawan & ); // prototipe

int main()
{
  // menetapkan format keluaran pecahan
  cout << fixed << setprecision( 2 );

  // mencciptakan objek-objek kelas terderivasi
  KaryawanBergaji karyawanBergaji(
    "Eva", "Duma", "111-11-1111", 800 );
  KaryawanKomisi karyawanKomisi(
    "Patricia", "Balige", "333-33-3333", 10000, .06 );
  KaryawanKomisiPlusPokok karyawanKomisiPlusPokok(
    "Rico", "Siantar", "444-44-4444", 5000, .04, 300 );

  cout << "Objek Karyawan diproses secara individual dengan pengikatan statis:\n\n";

  // menampilkan setiap informasi Karyawan dan penghasilannya menggunakan pengikatan statis
  karyawanBergaji.tampil();
  cout << "\nbergaji Rp. " << karyawanBergaji.penghasilan() << "\n\n";
  karyawanKomisi.tampil();
  cout << "\nbergaji Rp. " << karyawanKomisi.penghasilan() << "\n\n";
  karyawanKomisiPlusPokok.tampil();
  cout << "\nbergaji Rp. " << karyawanKomisiPlusPokok.penghasilan() << "\n\n"
    << "\n\n";

  // menciptakan vector atas tiga pointer kelas basis
  vector < Karyawan * > karyawan( 3 );

  // menginisialisasi dengan objek-objek Karyawan
  karyawan[ 0 ] = &karyawanBergaji;
  karyawan[ 1 ] = &karyawanKomisi;
  karyawan[ 2 ] = &karyawanKomisiPlusPokok;

  cout << "Objek Karyawan diproses secara polimorfikal dengan pengikatan statis:\n\n";

  // memanggil virtualViaPointer untuk menampilkan setiap informasi Karyawan
  // dan penghasilannya menggunakan pengikatan dinamis
  cout << "Fungsi virtual memanggil via pointer kelas basis:\n\n";

  for ( size_t i = 0; i < karyawan.size(); ++i )
    virtualViaPointer( karyawan[ i ] );

  // memanggil virtualViaReferensi untuk menampilkan setiap informasi Karyawan
  // dan penghasilannya menggunakan pengikatan dinamis
  cout << "Fungsi virtual memanggil via referensi kelas basis:\n\n";

  for ( i = 0; i < karyawan.size(); ++i )
    virtualViaReferensi( *karyawan[ i ] ); // dereferensi
} // akhir dari main

// memanggil virtualViaPointer untuk menampilkan setiap informasi Karyawan
// dan penghasilannya menggunakan pengikatan dinamis
void virtualViaPointer( const Karyawan * const kelasBasisPtr )
{
  kelasBasisPtr->tampil();
  cout << "\nbergaji Rp. " << kelasBasisPtr->penghasilan() << "\n\n";
} // akhir dari fungsi virtualViaPointer

// memanggil virtualViaReferensi untuk menampilkan setiap informasi Karyawan
// dan penghasilannya menggunakan pengikatan dinamis
void virtualViaReferensi( const Karyawan &kelasBasisRef )
{
  kelasBasisRef.tampil();
  cout << "\nbergaji Rp. " << kelasBasisRef.penghasilan() << "\n\n";
} // akhir dari fungsi virtualViaReferensi

Objek Karyawan diproses secara individual dengan pengikatan statis:

karyawan bergaji: Eva Duma
nomor kependudukan: 111-11-1111
gaji mingguan: 800.00
bergaji Rp. 800.00

karyawan komisi: Patricia Balige
nomor kependudukan: 333-33-3333
penjualan kotor: 10000.00; besar komisi: 0.06
bergaji Rp. 600.00

bergaji-pokok karyawan komisi: Rico Siantar
nomor kependudukan: 444-44-4444
penjualan kotor: 5000.00; besar komisi: 0.04; gaji pokok: 300.00
bergaji Rp. 500.00

Objek Karyawan diproses secara polimorfikal dengan pengikatan statis:

Fungsi virtual memanggil via pointer kelas basis:

karyawan bergaji: Eva Duma
nomor kependudukan: 111-11-1111
gaji mingguan: 800.00
bergaji Rp. 800.00

karyawan komisi: Patricia Balige
nomor kependudukan: 333-33-3333
penjualan kotor: 10000.00; besar komisi: 0.06
bergaji Rp. 600.00

bergaji-pokok karyawan komisi: Rico Siantar
nomor kependudukan: 444-44-4444
penjualan kotor: 5000.00; besar komisi: 0.04; gaji pokok: 300.00
bergaji Rp. 500.00

Fungsi virtual memanggil via referensi kelas basis:

karyawan bergaji: Eva Duma
nomor kependudukan: 111-11-1111
gaji mingguan: 800.00
bergaji Rp. 800.00

karyawan komisi: Patricia Balige
nomor kependudukan: 333-33-3333
penjualan kotor: 10000.00; besar komisi: 0.06
bergaji Rp. 600.00

bergaji-pokok karyawan komisi: Rico Siantar
nomor kependudukan: 444-44-4444
penjualan kotor: 5000.00; besar komisi: 0.04; gaji pokok: 300.00
bergaji Rp. 500.00

Baris 41 mengalokasikan sebuah vector, karyawan, yang memuat tiga pointer Karyawan. Baris 44 mengarahkan karyawan[0] pada objek karyawanBergaji. Baris 45 mengarahkan karyawan[1] pada objek karyawanKomisi. Baris 46 mengarahkan karyawan[2] pada objek karyawanKomisiPlusPokok. Kompiler mengijinkan ketiga penugasan ini, karena karyawanBergaji adalah Karyawan, karyawanKomisi adalah Karyawan, dan karyawanKomisiPlusPokok adalah Karyawan. Oleh karena itu, Anda dapat menugaskan alamat-alamat KaryawanBergaji, KaryawanKomisi, dan KaryawanKomisiPlusPokok kepada pointer kelas basis Karyawan, meskipun Karyawan adalah kelas abstrak.

Loop pada baris 54-55 menjelajah vector karyawan dan memanggil fungsi virtualViaPointer (baris 67-71) untuk setiap elemen di dalam karyawan. Fungsi virtualViaPointer menerima di dalam parameter kelasBasisPtr alamat yang disimpan di dalam sebuah elemen karyawan. Setiap pemanggilan terhadap virtualViaPointer menggunakan kelasBasisPtr untuk memanggil fungsi virtual tampil (baris 69) dan penghasilan (baris 70). Fungsi virtualViaPointer tidak memuat sembarang informasi tipe KaryawanBergaji, KaryawanKomisi atau KaryawanKomisiPlusPokok. Fungsi ini hanya mengetahui tentang tipe kelas basis Karyawan. Oleh karena itu, kompiler tidak mengetahui fungsi kelas konkrit mana yang akan dipanggil melalui kelasBasisPtr. Tetapi pada waktu eksekusi, setiap pemanggilan fungsi-virtual memanggil fungsi pada objek yang ditunjuk kelasBasisPtr pada saat itu. Keluaran mengilustrasikan bahwa fungsi-fungsi sesuai untuk setiap kelas dipanggil dan bahwa setiap informasi sesuatu untuk setiap objek ditampilkan. Sebagai contoh, gaji mingguan ditampilkan untuk KaryawanBergaji, dan penjualan kotor ditampilkan untuk KaryawanKomisi dan KaryawanKomisiPlusPokok. Di samping itu, penghasilan atas setiap Karyawan secara polimorfik pada baris 70 menghasilkan hasil yang sama dengan penghasilan atas setiap Karyawan secara pengikatan statis pada baris 33, 35, dan 37.

Terakhir, statemen for lainnya (baris 61-62) menjelajah karyawan dan memanggil fungsi virtualViaReferensi (baris 75-79)  untuk setiap elemen di dalam vector. Fungsi virtualViaReferensi menerima di dalam parameter kelasBasisRef (bertipe const Karyawan &) sebuah referensi kepada objek yang didapatkan dengan mendereferensi pointer yang disimpan di dalam setiap elemen karyawan (baris 62). Setiap pemanggilan terhadap virtualViaReferensi memanggil fungsi virtual tampil (baris 77) dan penghasilan (baris 78) melalui referensi kelasBasisRef untuk mendemonstrasikan bahwa pemrosesan polimorfik dilakukan dengan referensi kelas basis.

Kesimpulan
*      Dengan polimorfisme, Anda dapat merancang dan mengimplementasikan sistem yang dapat diperluas, dimana kelas-kelas baru dapat ditambahkan dengan sedikit atau tanpa modifikasi, sepanjang kelas-kelas baru tersebut bagian dari hirarki pewarisan yang diproses program secara generik. Bagian-bagian program yang harus diubah untuk mengakomodasi kelas-kelas baru adalah yang memerlukan pengetahuan langsung dari kelas-kelas baru yang Anda tambahkan ke dalam hirarki.
*      Anda menugaskan alamat sebuah objek kelas terderivasi kepada pointer kelas basis dan menjelaskan bahwa kompiler C++ mengijinkan penugasan ini, karena objek kelas terderivasi merupakan objek kelas basis.
*      Jika Anda tidak mendeklarasikan fungsi kelas basis sebagai virtual, maka Anda dapat mendefinisikan-ulang fungsi tersebut. Sebaliknya, jika Anda mendeklarasikan fungsi kelas basis sebagai virtual, maka Anda dapat mendefinisikan-ulang fungsi tersebut untuk memampukan watak polimorfik.

Latihan
1)      Banyak program yang ditulis dengan pewarisan dapat ditulis dengan kompsosisi, dan sebaliknya. Tulis-ulanglah kelas KaryawanKomisiPlusPokok pada hirarki KaryawanKomisi-KaryawanKomisiPlusPokok menggunakan komposisi menggantikan pewarisan.

2)      Modifikasi sistem penggajian pada Gambar 5.9-5.17 untuk menyertakan anggota data private tanggalLahir di dalam kelas Karyawan. Asumsikan bahwa penggajian diproses sekali dalam sebulan. Ciptakan sebuah vector yang memuat referensi-referensi Karyawan untuk menyimpan berbagai objek karyawan. Di dalam sebuah loop, hitunglah  penggajian untuk setiap Karyawan secara polimorfik dan tambahkan Rp. 1000 bonus kepada jumlah gaji seorang karyawan jika bulan ini adalah bulan dimana seorang Karyawan berulang tahun.

3)      Implementasikan hirarki Bangun didesain pada Gambar 4.3. Setiap BangunDuaDimensi harus memuat fungsi getLuas untuk menghitung luas suatu bangun dua dimensi. Setiap BangunTigaDimensi harus memiliki fungsi anggota getLuas dan getVolume untuk menghitung luas permukaan dan volume. Ciptakan suatu program yang menggunakan vector yang memuat pointer-pointer Bangun yang menunjuk ke objek-objek setiap kelas konkrit di dalam hirarki. Program harus menampilkan objek yang ditunjuk oleh setiap elemen vector. Di samping itu, di dalam loop yang memproses semua bangun di dalam vector, tentukan apakah setiap bangun merupakan BangunDuaDimensi atau BangunTigaDimensi. Jika sebuah bangun adalah BangunDuaDimensi, maka tampilkan luasnya. Jika bangun tersebut adalah BangunTigaDimensi, maka tampilkan luas dan volumenya.






No comments:

Post a Comment