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