BAB
6.
Fungsi
Virtual dan Polimorfisme
6.1 Pengantar
Polimorfisme (polymorphism)
merupakan kombinasi dari dua kata Yunani, yaitu poly dan morphism, yang berarti
bahwa kemampuan untuk berubah bentuk sesuai dengan lingkungan. Anda telah
mengenal dua jenis polimorfisme, yaitu pembebanan (overloading) fungsi dan
pembebanan operator. Dalam pembebanan operator, operator yang sama melakukan
beberapa bentuk operasi yang berbeda sesuai dengan tipe data yang disajikan
kepadanya. Pada pembebanan fungsi, beberapa fungsi dengan nama sama tetapi
memiliki jumlah atau tipe argumen yang berbeda. Jadi, nama yang sama dipanggil
untuk melakukan sejumlah aksi yang berbeda. Template (lihat Bab 16) juga
merupakan bentuk lain dari polimorfisme. Semua kasus ini merupakan contoh dari
pengikatan statis karena pilihan fungsi yang sesuai diputuskan pada waktu kompilasi,
yaitu sebelum eksekusi program dimulai.
Polimorfisme dimana di dalamnya pilihan fungsi dilakukan selama eksekusi
program dinamakan dengan pengikatan dinamis. Ini dilakukan melalui penggunaan
fungsi virtual dan pointer ke kelas basis. Semua ini diilustrasikan pada Gambar
6.1 berikut.
GAMBAR 6.1 Polimorfisme
6.2 Fungsi
Virtual
Polimorfisme dengan pengikatan dinamis dilakukan melalui fungsi virtual.
Tetapi aplikasi dari fungsi virtual dilakukan melalui pointer kelas basis.
Alasan dalam penggunaan pointer kelas basis diilustrasikan pada Gambar 6.2a.
Pointer kelas basis dapat dibuat baik untuk menunjuk objek dari kelas basis
maupun untuk menunjuk objek dari kelas terderivasi. Tetapi, pointer kelas
terderivasi hanya bisa menunjuk ke objek dari kelas terderivasi saja. Pointer
itu tidak dapat menunjuk ke objek dari kelas basis karena objek dari kelas
basis bukanlah objek dari kelas terderivasi. Jika kelas lain menderivasi dari
sebuah kelas terderivasi, yaitu kelas Terderivasi2
dari kelas Terderivasi1 seperti
ditunjukkan pada gambar berikut, maka kelas Terderivasi1 pada dasarnya merupakan kelas basis bagi kelas Terderivasi2. Pada kasus itu, pointer
yang menunjuk kelas Terderivasi1
dapat menunjuk objek-objek dari kelas Terderivasi1
dan objek-objek dari kelas Terderivasi2.
Tetapi pointer yang menunjuk kelas Terderivasi2
tidak dapat menunjuk objek-objek dari kelas Terderivasi1. Namun, pointer kelas Basis (lihat Gambar 6.2b) dapat menunjuk objek-objek dari kelas Basis bergitu pula menunjuk objek-objek
dari kelas Terderivasi1 dan
objek-objek dari kelas Terderivasi2.
Jadi, dengan pointer kelas basis, Anda dapat mengakses objek-objek dari
keseluruhan hierarki kelas di bawahnya.
GAMBAR 6.2 Pointer-pointer kelas dan jangkauannya
masing-masing
Untuk lebih jelas tentang karakteristik-karakteristik dari fungsi virtual,
berikut disajikan empat buah contoh. Pada program 6.1, sebuah kelas basis
dengan nama B dan sebuah kelas terderivasi dengan nama D telah didefinisikan (class D: public B). Kedua kelas memiliki sebuah fungsi publik dengan nama Tampil(). Pada kelas basis, fungsi itu
didefinisikan untuk menampilkan “Apakah Anda akan belajar C++?” dan pada kelas
terderivasi, fungsi itu didefinisikan untuk menampilkan “Saya juga belajar
C++”. Pointer bptr merupakan pointer
yang menunjuk ke kelas B. Pointer ini diinisialisasi dengan &(B)b dimana b adalah sebuah objek
dari kelas B. Ketika pointer ini dipakai untuk memanggil fungsi Tampil(), secara natural ia akan
menampilkan “Apakah Anda akan belajar C++?”, yaitu definisi pada kelas basis.
Sekarang pointer itu ditugasi sebuah alamat baru &d dimana d adalah sebuah objek dari kelas terderivasi D. Objek
d juga merupakan objek dari kelas basis B karena mekanisme pewarisan. Fungsi Tampil() dipanggil kembali dengan
pointer. Fungsi itu adakan menampilkan “Apakah Anda akan belajar C++?” yang
sama dan bukan definisi yang diberikan di dalam kelas D. Lihat program berikut
untuk ilustrasi.
Program 6.1 Mengilustrasikan jika
fungsi tidak dideklarasikan virtual, maka pointer kelas basis akan
menunjuk definisi fungsi pada kelas
basis meskipun pointer itu ditugasi alamat objek dari kelas terderivasi
|
|
#include <iostream>
using namespace std;
class B
{
public:
void
Tampil()
{
cout<<"Apakah Anda akan
belajar C++?"<<endl;
}
}; //akhir dari
kelas B
class D : public B
{
public
:
void
Tampil()
{
cout<<"Saya juga belajar
C++"<<endl;
}
}; //akhir dari
kelas D
int main()
{
B b; //b
adalah sebuah objek dari kelas basis B
D d; //d
adalah sebuah objek dari kelas terderivasi D
B *bptr; //pointer ke kelas B
bptr = &(B)b; //inisialisasi pointer ke objek dari kelas B
bptr -> Tampil(); //fungsi Tampil() dipanggil dengan pointer
bptr = & d; //pointer ditugasi alamat dari d.
bptr -> Tampil(); //pointer dipakai untuk memanggil fungsi
return
0;
}
|
|
KELUARAN
|
|
Apakah Anda
akan belajar C++?
Apakah Anda
akan belajar C++?
|
Dari keluaran program jelas terlihat bahwa meskipun pointer bptr dari kelas basis B ditugasi alamat
dari objek d dari kelas terderivasi D, fungsi yang dipanggil melalui pointer
itu adalah fungsi dari kelas basis.
Sekarang jika pointer didefinisikan untuk kelas D dan diinisialisasi
dengan nilai &(D) d dan jika
Anda mencoba menugasinya alamat dari objek kelas basis, misalkan, &b, maka
kompiler akan menghasilkan error karena b bukanlah objek dari kelas D. Objek dari kelas terderivasi adalah juga
objek dari kelas basis tetapi objek dari kelas basis bukanlah objek dari kelas
terderivasi. Pada dasarnya, sebuah kelas basis tidak mengetahui eksistensi
dari kelas-kelas yang mewarisinya. Ini diilustrasikan pada program berikut.
Program 6.2 Mengilustrasikan pointer
dari kelas terderivasi tidak bisa menunjuk ke objek dari kelas basis
|
|
#include <iostream>
using namespace std;
class B
{
public:
void
Tampil()
{
cout<<"Apakah Anda akan
belajar C++?"<<endl;
}
}; //akhir dari
kelas B
class D : public B
{
public
:
void
Tampil()
{
cout<<"Saya juga belajar
C++"<<endl;
}
}; //akhir dari
kelas D
int main()
{
B b; //b dideklarasikan sebagai objek dari
kelas B
D d; //d dideklarasikan sebagai objek dari
kelas D
D *dptr = &(D)d; //pointer ke kelas D
dptr -> Tampil();
dptr = & b; //dptr ditugasi alamat dari kelas basis
//ini akan menghasilkan
error
dptr -> Tampil();
return
0;
}
|
|
KELUARAN
|
|
cannot convert
from 'class B *' to 'class D *'
|
Hasil di atas akibat dari objek
dari kelas basis bukanlah objek dari kelas terderivasi. Oleh karena itu,
pointer kelas basis tidak dapat memanggil fungsi dari kelas basis.
Program 6.3 menunjukkan bahwa bentuk sesuai dari fungsi Tampil() bisa diperoleh dengan
mendefinisikan pointer-pointer terpisah untuk kelas B dan kelas D. Meskipun
Anda mendapatkan fungsi-fungsi yang relevan, namun ini bukanlah yang
diinginkan. Apa yang diinginkan di sini adalah untuk menunjuk objek dari kelas
terderivasi menggunakan pointer dari kelas basis.
Program 6.3 Mengilustrasikan aksi dari
pointer dari kelas basis dan pointer dari kelas terderivasi
|
|
#include <iostream>
using namespace std;
class B
{
public:
void
Tampil()
{
cout<<"Apakah Anda akan
belajar C++?"<<endl;
}
}; //akhir dari
kelas B
class D : public B
{
public
:
void
Tampil()
{
cout<<"Saya juga belajar
C++"<<endl;
}
}; //akhir dari
kelas D
int main()
{
B b; //b dideklarasikan sebagai objek dari
kelas B
D d; //d dideklarasikan sebagai objek dari
kelas D
B *bptr = & (B) b ; //pointer ke kelas B
D *dptr = &(D) d ; // pointer ke kelas D
bptr -> Tampil();
dptr -> Tampil();
return 0;
}
|
|
KELUARAN
|
|
Apakah Anda
akan belajar C++?
Saya juga
belajar C++
|
Keluaran tersebut didapatkan dengan mendefinisikan dua pointer yang
berbeda, yaitu satu untuk kelas basis dan pointer lain untuk kelas terderivasi.
Adalah natural untuk mendapatkan bentuk sesuai dari fungsi Tampil() dengan cara ini, tetapi ini bukanlah tujuan di sini.
Tujuannya adalah untuk mendapatkan fungsi tertentu dari kelas terderivasi
menggunakan pointer kelas basis. Ini dapat dilakukan hanya dengan
mendeklarasikan fungsi Tampil()
sebagai fungsi virtual di dalam kelas basis. Kata virtual merupakan katakunci di dalam C++. Pada kelas terderivasi,
kata virtual bisa digunakan atau
boleh juga tidak digunakan tetapi prototipe dari fungsi virtual di dalam kelas
basis harus identik dengan prototipe fungsi pada kelas terderivasi. Pada
program berikut, fungsi void Tampil()
dideklarasikan virtual. Definisi dari fungsi Tampil() dimodifikasi menjadi berikut:
virtual void
Tampil() {statemen_statemen; }
Sekarang, fungsi yang sama dipanggil di dalam kelas terderivasi melalui pointer
kelas basis. Definisi yang diberikan di dalam kelas terderivasi
mendefinisikan-ulang (override)
definisi pada kelas basis.
Ini diilustrasikan pada program 6.4 berikut. Pada kelas terderivasi,
fungsi memiliki nama sama dan tipe-tipe parameter yang sama dengan fungsi
virtual pada kelas basis. Jika tipe atau jumlah parameter berubah, maka hal ini
akan menjadi fungsi terbebani (overloaded)
dan akan kehilangan properti dari fungsi virtual.
Program 6.4 Mengilustrasikan aplikasi
dari fungsi virtual
|
|
#include <iostream>
using namespace std;
class B
{
public:
virtual
void Tampil()
{
cout<<"Apakah Anda akan
belajar C++?"<<endl;
}
}; //akhir dari
kelas B
class D : public B
{
public
:
void
Tampil()
{
cout<<"Saya juga belajar
C++"<<endl;
}
}; //akhir dari
kelas D
int main()
{
B b; //b dideklarasikan sebagai objek dari
kelas B
D d; //d dideklarasikan sebagai objek dari
kelas D
B *bptr = &(B) b ; //pointer kelas basis
bptr -> Tampil(); //statemen untuk memanggil fungsi kelas basis
bptr = & d; //pointer kelas basis ditugasi alamat dari objek
terderivasi d
bptr -> Tampil(); //memanggil fungsi Tampil() dari kelas
terderivasi
return
0;
}
|
|
KELUARAN
|
|
Apakah Anda
akan belajar C++?
Saya juga
belajar C++
|
Perhatikan bahwa fungsi Tampil()
telah dideklarasikan virtual. Pointer kelas basis ditugasi alamat objek dari
kelas terderivasi dan kemudian pointer itu dipakai untuk memanggil fungsi Tampil(). Pada kasus ini, definisi
kelas terderivasi dari fungsi Tampil()
mendefinisikan ulang definisi kelas basis dari fungsi itu dan hasilnya seperti
ditunjukkan pada keluaran program.
KARAKTERISTIK DARI FUNGSI VIRTUAL
- Fungsi virtual harus didefinisikan di dalam kelas dimana di dalamnya ia pertama-kali muncul (kelas basis). Fungsi ini didefinisikan sebagai fungsi anggota publik dengan katakunci virtual yang mengawali pendefinisian.
- Fungsi virtual tidak dapat berupa anggota statis.
- Fungsi virtual dapat dideklarasikan di dalam fungsi inline.
- Fungsi virtual dapat dideklarasikan sebagai friend dari kelas lain.
- Selain nama sama, prototipe-prototipe fungsi virtual di dalam kelas basis dan di dalam kelas terderivasi harus sama persis baik tipe, jumlah argumen, dan tipe dari tiap argumen. Penyimpangan dari syarat ini akan membuat kompiler menjadikannya sebagai fungsi yang terbebani (overloaded) dan kehilangan statusnya sebagai fungsi virtual.
- Fungsi konstruktor tidak dapat dijadikan fungsi virtual.
- Fungsi destruktor dapat dijadikan sebagai fungsi virtual.
- Definisi fungsi virtual di dalam kelas basis didefinisikan oleh definisi fungsi tersebut di dalam kelas terderivasi. Jika di dalam kelas terderivasi fungsi tidak didefinisikan ulang, maka definisi kelas basis yang akan dipanggil.
- Fungsi virtual diwariskan kepada semua kelas turunannya pada hierarki pewarisan.
- Sebuah kelas yang memuat satu atau lebih fungsi virtual seringkali dinamakan dengan kelas polimorfis.
Program berikut mengilustrasikan aplikasi dari sebuah fungsi virtual.
Program 6.5 Mengilustrasikan
polimorfisme
|
|
#include <iostream>
using namespace std;
class B
{
public:
virtual
void Tampil()
{
cout<<"Tampil dari kelas
B dipanggil"<<endl;
}
}; //akhir dari
kelas B
class D1 : public B
{
public:
void
Tampil()
{
cout<<"Tampil dari kelas
D1 dipanggil"<<endl;
}
} ; //akhir
dari kelas D1
class D2 : public B
{
public
:
void
Tampil ()
{
cout<<"Tampil dari kelas
D2 dipanggil"<<endl;
}
}; //akhir dari
kelas D2
int main ()
{
B b; //b
adalah sebuah objek dari kelas B
B *bptr = &b; //pointer kelas basis
D1 d1; //d1
dan d2 adalah objek dari kelas D1
D2 d2; //dan
kelas D2
bptr ->Tampil();
bptr = &d1; //pointer kelas basis ditugasi alamat
//dari objek kelas
terderivasi
bptr -> Tampil();
bptr = &d2;
bptr ->Tampil() ;
return
0 ;
}
|
|
KELUARAN
|
|
Tampil dari
kelas B dipanggil
Tampil dari
kelas D1 dipanggil
Tampil dari
kelas D2 dipanggil
|
6.3 Array yang
Memuat Pointer-Pointer Kelas Basis
Ketika terdapat sejumlah kelas terderivasi yang mendefinisikan-ulang dan
menggunakan fungsi virtual dari sebuah kelas basis, adalah lebih mudah untuk
mendeklarasikan sebuah array yang memuat sejumlah pointer ke kelas basis.
Elemen-elemen dari array adalah alamat-alamat dari objek-objek kelas tersebut. Dimisalkan
terdapat kelas D1, D2, D3, dan seterusnya yang diderivasi dari kelas basis B.
Dimisalkan bahwa b, d1, d2, d3, dan seterusnya adalah objek dari kelas B, D1,
D2, D3, dan seterusnya. Untuk situasi seperti ini, Anda bisa mendeklarasikan
dan menginisialisasi sebuah array yang memuat pointer-pointer kelas basis
sebagai berikut:
B *bptr[] = {&b, &d1,
&d2, &d3};
dimana &b adalah alamat
dari objek kelas basis, &d1, &d2, dan &d3 adalah alamat-alamat dari objek-objek kelas terderivasi D1,
D2, dan D3. Aplikasinya diilustrasikan pada program berikut.
Program 6.6 Mengilustrasikan sebuah
array yang memuat pointer-pointer kelas basis yang menunjuk ke objek-objek
kelas terderivasi
|
|
#include <iostream>
using namespace std;
class B
{
public:
virtual
void Tampil()
{
cout<<"Tampil dari kelas
B dipanggil"<<endl;
}
}; //akhir dari
kelas B
class D1 : public B
{
public:
void
Tampil()
{
cout<<"Tampil dari kelas
D1 dipanggil"<<endl;
}
} ; //akhir
dari kelas D1
class D2 : public B
{
public
:
void
Tampil()
{
cout<<"Tampil dari kelas
D2 dipanggil"<<endl;
}
}; //akhir dari
kelas D2
class D3 : public B
{
public
:
void
Tampil()
{
cout<<"Tampil dari kelas
D3 dipanggil"<<endl;
}
}; //akhir dari
kelas D3
int main ()
{
B b;
D1 d1;
D2 d2;
D3 d3;
B *bptr[] = {&b, &d1, &d2,
&d3}; //Array yang memuat pointer-pointer kelas B
for
(int i = 0; i<4; i++)
bptr[i] ->Tampil();
return
0 ;
}
|
|
KELUARAN
|
|
Tampil dari
kelas B dipanggil
Tampil dari
kelas D1 dipanggil
Tampil dari
kelas D2 dipanggil
Tampil dari
kelas D3 dipanggil
|
Pada program tersebut, elemen-elemen dari sebuah array yang berisi
pointer-pointer ke kelas basis ditugasi alamat dari objek kelas basis dan dari
objek-objek kelas terderivasi. Pada keluaran program, Anda dapat melihat bahwa
definisi dari tiap kelas dipanggil.
6.4 Fungsi Virtual
Murni dan Kelas Abstrak
Sebuah fungsi virtual murni tidak didefinisikan di dalam kelas basis,
tetapi ia didefinisikan di dalam kelas terderivasi. Pada kelas basis, fungsi
itu tidak memiliki tubuh fungsi dan deklarasinya diikuti dengan = 0. Deklarasi
dari fungsi virtual murni di dalam kelas basis diilustrasikan berikut:
virtual tipe pengenal(tipe
parameter, —, —) = 0
Di sini, virtual adalah
katakunci dari C++, tipe adalah tipe
data dari nilai balik dan pengenal
adalah nama fungsi. Tidak ada definisi fungsi di dalam kelas basis. Prototipe
dari fungsi diekuasi dengan nol. Tetapi fungsi virtual ini didefinisikan di
dalam kelas-kelas terderivasi dengan caranya sendiri. Sebuah kelas yang memuat
sebuah fungsi virtual murni disebut dengan kelas abstrak. Kelas abstrak tidak
dapat memiliki objek sendiri. Berikut merupakan konsekuensi dari pendeklarasian
fungsi virtual murni:
- Sebuah fungsi virtual murni tidak dapat dipakai untuk aksi apapun di dalam kelas basis. Pada dasarnya, kelas basis yang memuat sebuah fungsi virtual tidak dapat memiliki objek. Jadi tidak perlu diinisialisasi.
- Kelas dimana di dalamnya satu atau lebih fungsi virtual murni dideklarasikan disebut dengan kelas abstrak atau kelas abstrak murni.
- Sebuah kelas abstrak tidak dapat memiliki objek sendiri.
- Kelas terderivasi memiliki definisi sendiri dari fungsi virtual murni.
- Jika sebuah kelas terderivasi tidak mendefinisikan fungsi virtual murni yang dideklarasikan di dalam kelas basisnya, maka kelas itu juga akan menjadi kelas abstrak.
Program berikut memberikan sebuah ilustrasi dari kelas abstrak dan
kompiler memberikan pesan error jika objek-objek dari kelas abstrak
dideklarasikan.
Program 6.7 Mengilustrasikan fungsi
virtual murni
|
|
#include <iostream>
using namespace std;
class B
{
public
:
virtual
void Tampil() = 0;
} ; //akhir
dari kelas B
class D1 : public B // Derived class
{
public
:
void
Tampil(){ cout <<"Tampil() dari kelas D1
dipangil"<<endl;}
};
int main()
{
B b ; //objek b dari kelas B
dideklarasikan yang menghasilkan error
B *bptr = &b;
bptr -> Tampil();
return
0 ;
}
|
|
KELUARAN
|
|
cannot
instantiate abstract class
|
Program berikut mengilustrasikan aplikasi dari fungsi virtual murni.
Sebuah kelas yang menangani sejumlah bangun dapat memiliki sebuah fungsi
virtual Luas(). Fungsi Luas() didefinisikan di dalam kelas Lingkaran dan kelas Persegi yang merupakan kelas-kelas
terderivasi dari kelas BangunBiasa.
Program 6.8 Mengilustrasikan aplikasi
lain dari fungsi virtual murni
|
|
#include <iostream>
using namespace std;
class BangunBiasa {
int
sisi; //private secara default
public:
void
set_sisi(int a)
{
sisi = a;
}
int
get_sisi()
{
return
sisi;
}
virtual
double Luas (void) =0;
}; //akhir dari
kelas BangunBiasa
class Lingkaran : public BangunBiasa
{
public:
double
Luas (void)
{
return
(3.14159*get_sisi()*get_sisi()/4);
}
}; //akhir dari
kelas Lingkaran
class Persegi : public BangunBiasa
{
public:
double
Luas (void)
{
return
(get_sisi()*get_sisi());
}
}; //akhir dari
kelas Persegi
int main ()
{
double
A1, A2;
Persegi S1;
Lingkaran C1;
BangunBiasa* RS1 = & S1;
BangunBiasa* RS2 = & C1;
(*RS1).set_sisi(10);
RS2 -> set_sisi(10);
A1 = (*RS1).Luas(); //(*RS1).Luas() dan
RS1 ->Luas() ekivalen
A2 = RS2 -> Luas();
cout<<"A1 =
"<<A1<<"\t A2 = "<<A2<<endl;
return
0;
}
|
|
KELUARAN
|
|
A1 = 100 A2 = 78.5397
|
6.5 Destruktor
Virtual
Fungsi destruktor dapat dideklarasikan virtual sedangkan fungsi
konstruktor tidak dapat dideklarasikan virtual. Pertanyaan selanjutnya adalah
apakah keuntungan dari pendeklasian virtual atas fungsi destruktor? Pada kasus
objek-objek yang diciptakan secara dinamis dari sebuah kelas terderivasi
melalui pointer kelas basis, jika pointer kelas basis dihapus, maka hanya
destruktor kelas basis yang dipanggil. Ini diilustrasikan pada program berikut.
Namun, jika fungsi destruktor kelas basis dideklarasikan virtual, maka
pada penghapusan pointer kelas basis, fungsi-fungsi destruktor dari kelas-kelas
terderivasi begitu juga dari kelas-kelas yang berelasi (kelas-kelas yang dimuat
di dalam kelas terderivasi) juga akan dipanggil. Dua program berikut
mengilustrasikannya.
Program 6.9 Mengilustrasikan watak
dari fungsi destruktor ketika destruktor kelas basis tidak dideklarasikan
virtual
|
|
#include <iostream>
using namespace std;
class B
{
protected:
int
bx;
public
:
B() {} //konstruktor
kosong dari kelas basis
B(int
m)
{
bx = m ; //konstruktor dari kelas basis
cout<<"Konstruktor dari
kelas B dipanggil"<<endl;
}
virtual
int Perkalian()
{
return
2*bx;
}
//fungsi destruktor tidak
dideklarasikan virtual
~B()
{
cout<<"Destruktor dari
kelas B dipanggil"<<endl;
}
}; //akhir dari
kelas B
class D1
{
protected
:
int
D1x ;
public
:
D1 () {} //konstruktor kosong (default) dari D1
D1(int
n)
{
D1x = n; // constructor of class D1
cout<<"Konstruktor dari
D1 dipanggil"<<endl;
}
int
getD1x()
{
return
D1x;
}
~D1()
{
cout<<"Destruktor dari D1
dipanggil"<<endl;
}
};
class D2
{
protected
:
int
D2x ;
public
:
D2(){}
D2(int
p)
{
D2x = p ;
cout <<"Konstruktor dari
D2 dipanggil"<<endl;
}
int
getD2x()
{
return
D2x;
}
~D2()
{
cout <<"Destruktor dari
D2 dipanggil"<<endl;
}
};
class D3 : public B
{
int
D3x; // private secara default
public
:
D3() {}
D1 d1; //d1
dideklarasikan sebagai objek dari kelas D1
D2 d2; //d2
dideklarasikan sebagai objek dari kelas D2
D3(int
p, int q , int r, int s): B(p),
d1(q), d2(r)
{
D3x = s;
cout<<"Konstruktor dari
D3 dipanggil"<< endl;
}
int
Perkalian()
{
return
D3x * d2.getD2x() * d1.getD1x() * bx;
}
~D3()
{
cout<<"Destruktor dari D3
dipanggil"<<endl;
}
};
int main ()
{
B *Bp ;
Bp = new
B(10); //objek dinamis yang diciptakan dengan operator new
cout<<"Perkalian dari B =
"<<Bp ->Perkalian()<<endl;
Bp = new
D3(2, 3, 4, 5);
cout<<"Perkalian =
"<<Bp -> Perkalian()<<endl;
delete Bp; //penghapusan pointer kelas
basis
return
0 ;
}
|
|
KELUARAN
|
|
Konstruktor
dari kelas B dipanggil
Perkalian dari
B = 20
Konstruktor
dari kelas B dipanggil
Konstruktor
dari D1 dipanggil
Konstruktor
dari D2 dipanggil
Konstruktor
dari D3 dipanggil
Perkalian = 120
Destruktor dari
kelas B dipanggil
|
Pada program di atas, jelaslah bahwa hanya destruktor kelas basis yang
dipanggil. Pada program berikut, fungsi destruktor dari kelas basis
dideklarasikan virtual. Dengan ini, pada penghapusan pointer kelas basis,
fungsi-fungsi destruktor dari kelas terderivasi maupun dari kelas-kelas yang
berelasi (kelas D1 dan D2 pada kasus tersebut) juga dipanggil. Keluaran dari
program ini mengilustrasikannya.
Program 6.10 Mengilustrasikan efek
dari pendeklarasian virtual pada destruktor kelas basis
|
|
#include <iostream>
using namespace std;
class B
{
protected:
int
bx;
public
:
B() {} //konstruktor
kosong dari kelas basis
B(int
m)
{
bx = m ; //konstruktor dari kelas basis
cout<<"Konstruktor dari
kelas B dipanggil"<<endl;
}
virtual
int Perkalian()
{
return
2*bx;
}
//fungsi destruktor tidak dideklarasikan
virtual
virtual
~B()
{
cout<<"Destruktor dari
kelas B dipanggil"<<endl;
}
}; //akhir dari
kelas B
class D1
{
protected
:
int
D1x ;
public
:
D1 () {} //konstruktor kosong (default) dari D1
D1(int
n)
{
D1x = n; // constructor of class D1
cout<<"Konstruktor dari
D1 dipanggil"<<endl;
}
int
getD1x()
{
return
D1x;
}
~D1()
{
cout<<"Destruktor dari D1
dipanggil"<<endl;
}
};
class D2
{
protected
:
int
D2x ;
public
:
D2(){}
D2(int
p)
{
D2x = p ;
cout <<"Konstruktor dari
D2 dipanggil"<<endl;
}
int
getD2x()
{
return
D2x;
}
~D2()
{
cout <<"Destruktor dari
D2 dipanggil"<<endl;
}
};
class D3 : public B
{
int
D3x; // private secara default
public
:
D3() {}
D1 d1; //d1
dideklarasikan sebagai objek dari kelas D1
D2 d2; //d2
dideklarasikan sebagai objek dari kelas D2
D3(int
p, int q , int r, int s): B(p),
d1(q), d2(r)
{
D3x = s;
cout<<"Konstruktor dari
D3 dipanggil"<< endl;
}
int
Perkalian()
{
return
D3x * d2.getD2x() * d1.getD1x() * bx;
}
~D3()
{
cout<<"Destruktor dari D3
dipanggil"<<endl;
}
};
int main ()
{
B *Bp ;
Bp = new
B(10); //objek dinamis yang diciptakan dengan operator new
cout<<"Perkalian dari B =
"<<Bp ->Perkalian()<<endl;
Bp = new
D3(2, 3, 4, 5);
cout<<"Perkalian =
"<<Bp -> Perkalian()<<endl;
delete Bp; //penghapusan pointer kelas
basis
return
0 ;
}
|
|
KELUARAN
|
|
Konstruktor
dari kelas B dipanggil
Perkalian dari B
= 20
Konstruktor
dari kelas B dipanggil
Konstruktor
dari D1 dipanggil
Konstruktor
dari D2 dipanggil
Konstruktor
dari D3 dipanggil
Perkalian = 120
Destruktor dari
D3 dipanggil
Destruktor dari
D2 dipanggil
Destruktor dari
D1 dipanggil
Destruktor dari
kelas B dipanggil
|
6.6 Kelas Basis
Virtual
Gambar 6.3 menunjukkan sebuah kasus pewarisan dimana di dalamnya dua kelas
D1 dan D2 diderivasi dari kelas basis B. Kelas lain D3 diderivasi dari kedua
kelas terderivasi tersebut. Sekarang, sebuah objek dari kelas D3 memanggil
suatu fungsi dari kelas B. Kompiler menunjukkan error yang menandakan
ambibuitas. Karena, kedua kelas D1 dan D2 memiliki salinan sendiri dari kelas
B. Jadi, D3 memiliki dua salinan B. Kompiler tidak bisa memutuskan salinan mana
dari B yang dipanggil. Ambiguitas ini dapat diselesaikan jika kelas basis
dideklarasikan virtual di dalam definisi kelas D1 dan D2. Pada kasus itu, hanya
ada satu salinan dari kelas basis di dalam D3 sehingga tidak ada ambiguitas.
Ini ditunjukkan pada program 6.11 dan program 6.2. Pada program 6.11,
ambiguitas diilustrasikan dan pada program 6.12, ambiguitas diselesaikan.
Program 6.11 Mengilustrasikan
ambiguitas yang diciptakan di dalam kelas terderivasi dari kelas-kelas
terderivasi multi-lintasan dari kelas basis yang sama
|
|
#include <iostream>
using namespace std;
class B
{
public:
void
Tampil() {cout<<"Fungsi Tampil() dari kelas B
dipanggil"<<endl;}
};
class D1 : public B
{};
class D2 : public B
{};
class D3 : public D2, public D1
{};
int main()
{
D3 d3;
d3.Tampil();
return
0;
}
|
|
KELUARAN
|
|
'D3::Tampil' is
ambiguous
|
GAMBAR 6.3 Pewarisan multi-lintasan dari kelas
basis yang sama
Ambiguitas pada program 6.11 dapat diselesaikan dengan mendeklarasikan
virtual atas kelas basis pada deklarasi kelas terderivasi.
Program 6.12 Mengilustrasikan kelas
basis virtual
|
|
#include <iostream>
using namespace std;
class B
{
public:
void
Tampil() {cout<<"Fungsi Tampil() dari kelas B
dipanggil"<<endl;}
};
class D1 : virtual public B
{};
class D2 : virtual public B
{};
class D3 : public D2, public D1
{};
int main()
{
D3 d3;
d3.Tampil();
return
0;
}
|
|
KELUARAN
|
|
Fungsi Tampil()
dari kelas B dipanggil
|
6.7 RTTI
(run-time type information)
Seperti yang telah dijelaskan, pointer kelas basis dapat dibuat untuk
menunjuk ke objek-objek dari sembarang kelas terderivasi, bahkan menunjuk ke
objek-objek dari kelas yang diderivasi dari kelas terderivasi. Namun, pointer
itu tidak membawa informasi apapun tentang objek kelas mana yang ditunjuknya.
Pada program tertentu, informasi semacam ini diperlukan jika Anda ingin
menerapkan suatu fungsi spesial terhadap objek-objek dari kelas tertentu saja.
Ini melibatkan pembedaan tipe-tipe objek dan pencarian objek-objek dari kelas
yang diinginkan sehingga fungsi itu dapat diterapkan.
Salah satu solusinya adalah dengan mendefinisikan sebuah fungsi anggota di
dalam tiap kelas untuk menyediakan informasi tipe dan yang dapat dipakai untuk
memilih tipe objek yang diinginkan. Ketika jumlah kelas semakin banyak, kode
untuk pemilihan ini akan semakin panjang dengan rantai if-else atau switch.
Fungsi virtual juga bisa menjadi alternatif solusi. Program berikut menggunakan
fungsi virtual Tipe() untuk
mendapatkan informasi tipe runt-time.
Program 6.13 Mendapatkan RTTI
|
|
#include <iostream>
using namespace std;
class B
{
public:
virtual
char Tipe(){return 'B';}
};
class C : public B
{
public:
char
Tipe(){return 'C';}
};
class D : public B
{
public:
char
Tipe(){return 'D';}
};
int main()
{
B b;
C c ;
D d ;
B* pb = &b;
cout<<"Tipe dari pb sekarang
adalah : "<< pb -> Tipe()<<endl;
pb = & c;
cout<<"Tipe dari pb sekarang
adalah : "<<pb -> Tipe()<<endl;
pb = & d;
cout<<"Tipe dari pb sekarang
adalah : "<<pb -> Tipe()<<endl;
return
0 ;
}
|
|
KELUARAN
|
|
Tipe dari pb
sekarang adalah : B
Tipe dari pb
sekarang adalah : C
Tipe dari pb
sekarang adalah : D
|
Tetapi C++ menyediakan dukungan langsung untuk RTTI menggunakan dua
operator, yaitu dynamic_cast() dan typeid(). Operator typeid() dapat diterapkan pada kelas polimorfis maupun pada kelas
tak-polimorfis. Operator ini menentukan dan menghasilkan sebuah referensi
terhadap tipe eksak dari objek. Nama kelas dari objek juga dapat diperoleh
menggunakan fungsi name(). Gambar 6.4
memberikan ilustrasi dari operator typeid().
GAMBAR 6.4 Identifikasi kelas dari
objek-objek
Kelas-kelas yang berkaitan dengan typeid()
dimuat di dalam file header <typeinfo>
dan file header ini harus dicantumkan di dalam program jika objek-objeknya akan
dipakai di dalam program. Sintaksis untuk typeid()
adalah sebagai berikut:
typeid(d);
dimana d adalah sebuah objek kelas. Kode ini menghasilkan referensi
terhadap tipe dari objek d. File header <typeinfo>
juga mendefinisikan dua operator relasional, == dan !=, untuk membandingkan
tipe dari dua objek, katakanlah d1 dan d2. Sintaksisnya diilustrasikan sebagai
berikut:
if(typeid(d1)
== typeid(d2))
atau
if
(typeid(d1) != typeid(d2))
File header <typeinfo>
juga mendefinisikan fungsi name()
yang menghasilkan nama dari tipe atau nama dari kelas dari suatu objek.
Sintaksis untuk menggunakan name()
diberikan berikut:
typeid(d1).name();
Fungsi ini menghasilkan nama tipe dari objek d1. Kode berikut dipakai untuk menampilkan nama tipe dari objek d1:
cout<<typeid(d1).name();
Aplikasi dari operator typeid()
diilustrasikan pada program berikut.
Program 6.14 Aplikasi typeid() pada
kelas tak-polimorfis
|
|
#include <iostream>
#include <typeinfo> //file
header untuk typeid()
using namespace std;
class B
{};
class D1 : public B
{};
class D2 : public D1
{};
int main ()
{
D2 d2;
D1 d1;
if
(typeid( d1) == typeid (d2))
cout<<"Kedua objek
sama"<<endl;
else
cout<<"Kedua objek berbeda
tipe"<<endl;
cout<<"Tipe dari d1 adalah
"<<typeid(d1).name()<<endl;
cout<<"Tipe dari d2 adalah
"<<typeid(d2).name()<<endl;
return
0;
}
|
|
KELUARAN
|
|
Kedua objek
berbeda tipe
Tipe dari d1
adalah class D1
Tipe dari d2
adalah class D2
|
6.8 Operator-Operator
Casting pada C++
OPERATOR dynamic_Cast()
Operator ini dipakai untuk mengkonversi tipe objek pada saat run time dan
hanya berlaku pada kelas polimorfis, yaitu kelas basis harus memiliki
sedikitnya satu fungsi virtual. Operator dynamic_cast<>()
juga dapat diterapkan pada pointer dan referensi. Untuk pointer, berikut
sintaksisnya:
dynamic_cast <tipe_derivasi*>
(ptr_basis);
Operator dynamic_cast() ini
mengkonversi pointer kelas basis (ptr_basis)
menjadi pointer kelas terderivasi (tipe_derivasi*)
dikarenakan bahwa objek yang ditunjuk oleh ptr_basis
adalah dari kelas terderivasi yang diinginkan atau kelas yang diderivasi dari
kelas terderivasi yang diinginkan. Jadi,
ini merupakan cara mudah untuk memilih objek-objek dari kelas terderivasi yang
diinginkan dari sejumlah objek dari beberapa kelas terderivasi. Ilustrasi
tentang hal ini ditampilkan pada Gambar 6.5. Pointer-pointer yang nantinya
diinginkan untuk memilih objek-objek dari kelas tertentu semuanya dikonversi
dengan operator dynamic_cast().
Pointer-pointer yang menunjuk objek-objek dari kelas yang diinginkan akan
dikonversi sedangkan pointer-pointer lainnya akan menghasilkan 0.
GAMBAR 6.5 Ilustrasi dari operator dynamic_cast()
Program 6.15a Aplikasi dari operator
dynamic_cast()
|
|
#include <iostream>
using namespace std;
class B
{
public:
B() {};
virtual
void Fungsi(){};
};
class D1 : public B
{};
class D2 : public B
{};
class D3 : public B
{};
int main ()
{
B* Ab[3];
Ab[0] = new D1;
Ab[1] = new D2;
Ab[2] = new D3;
for
(int i =0; i<3; i++)
{
D2 *ptrd2 =
dynamic_cast<D2*>(Ab[i]);
if (ptrd2 != 0 )
cout<<"Objek D2
ditemukan\n";
}
return
0;
}
|
|
KELUARAN
|
|
Kedua objek
berbeda tipe
Tipe dari d1
adalah class D1
Tipe dari d2
adalah class D2
|
File header <typeinfo>
juga mendefinisikan dua fungsi, yaitu bad_typeid
dan bad_cast. Jika operator typeid() tidak berhasil, maka ia akan
melemparkan sebuah eksepsi bad_typeid.
Jika operator dynamic_cast() tidak
berhasil, maka bad_cast akan
dilempar dan program akan berhenti.
OPERATOR static_cast()
Operator static_cast()
melakukan konversi dari satu tipe ke tipe lain dengan cara yang sama dengan
cara casting konvensional dari bahasa
C. Sintaksisnya diberikan berikut:
static_cast<T>(objek)
Ekspresi ini mengkonversi tipe objek menjadi T. Tipe T bisa berupa salah
satu dari berikut:
a.
Tipe data
fundamental seperti int, double, char, dan lainnya.
b.
Tipe yang
didefinisikan user, yaitu nama kelas.
c.
Pointer.
d.
Referensi.
e.
Tipe enumerasi.
Seperti diimplikasi dari namanya, operator ini hanya dapat diterapkan pada
kasus tak-polimorfis. Objek dari satu kelas dapat diubah menjadi objek dari
kelas lain jika diberikan fungsi-konstruksi yang diperlukan. Program berikut
memberikan ilustrasi dari operator static_cast()
untuk pointer (yang mengkonversi dari pointer kelas basis menjadi pointer kelas
terderivasi)
Program 6.15b Aplikasi dari operator
static_cast()
|
|
#include <iostream>
using namespace std;
class Basis
{
public
:
int
x;
int
y;
void
Bicara() { cout<<"Saya adalah kelas Basis.\n";}
}; //akhir dari
kelas Basis
class D : public Basis
{
public:
void
bicara() { cout<<"Saya adalah kelas D. \n";}
Basis B; //B adalah objek dari kelas Basis
int
Luas(Basis B)
{return
B.x*B.y;}
};
int main()
{
Basis B ;
B.x =5;
B.y = 20;
Basis * basisp = &B; // pointer ke
kelas Basis
cout<<"Fungsi Bicara()
dipanggil dengan basisp."<<endl;
basisp -> Bicara();
cout<<"Setelah operator static
cast(), fungsi D dipanggil"<<endl;
D * dp = static_cast<D*>(basisp); //basisp cast dikonversi menjadi tipe D
dp->bicara(); //fungsi Bicara() dari kelas D
dipanggil oleh pointer dp
D* dp1 = (D*)basisp; //konversi
konvensional dari bahasa C
cout<<"Setelah konversi
konvensional dari bahasa C, untuk memanggil Bicara()"<<endl;
dp1 -> bicara();
D d; //d dideklarasikan sebagai objek dari
kelas D
cout<<"Luas = "<<
d.Luas(B)<<endl; //memanggil
fungsi Luas()
cout<<"Setelah mengkonversi sebuah objek menjadi referensi
menggunakan konversi konvensional"<<
endl;
D &dp2 = (D&) B; //mengkonversi
sebuah referensi dengan konversi konvensional bahasa C
dp2.Bicara(); //memanggil sebuah fungsi dari kelas Basis
dp2.bicara(); //memanggil sebuah fungsi dari kelas D
D &dp3 = static_cast<D&>(B);
//mengkonversi sebuah referensi dengan operator
static_cast()
cout<<"Setelah penerapan
operator static_cast(), fungsi-fungsi dipanggil lagi"<<endl;
dp3.Bicara(); //Bicara() dan Bicara()
adalah dua fungsi yang berbeda
dp3.bicara();
return
0 ;
}
|
|
KELUARAN
|
|
Fungsi Bicara()
dipanggil dengan basisp.
Saya adalah
kelas Basis.
Setelah
operator static cast(), fungsi D dipanggil
Saya adalah
kelas D.
Setelah
konversi konvensional dari bahasa C, untuk memanggil Bicara()
Saya adalah
kelas D.
Luas = 100
Setelah
mengkonversi sebuah objek menjadi referensi menggunakan konversi konvensional
Saya adalah
kelas Basis.
Saya adalah
kelas D.
Setelah
penerapan operator static_cast(), fungsi-fungsi dipanggil lagi
Saya adalah
kelas Basis.
Saya adalah
kelas D.
|
OPERATOR reinterpret_cast<>()
Operator ini memiliki sintaksis berikut:
reinterpret_cast<T>(objek)
Operator ini menghasilkan interpretasi-ulang atas operandnya yang cukup
berbeda dari semula. Sebagai contoh, operator ini dapat mengkonversi sebuah int
menjadi sebuah tipe pointer dan sebuah pointer menjadi sebuah int atau mengubah
sebuah pointer menjadi sebuah char, dan seterusnya. Kedua operator, dynamic_cast() dan reinterpret_cast(), sangat rentan dengan error sehingga perlu
digunakan dengan hati-hati.
Operator reinterpret_cast()
dapat dipakai pada pointer maupun pada referensi. Operator ini juga dapat dipakai
objek statis maupun objek dinamis. Sama seperti operator static_cast(), operator ini mempertahankan kekonstanan dari sebuah
objek. Program berikut mengilustrasikan aplikasi dari reinterpret_cast() untuk mengkonversi pointer dan referensi.
Operator ini juga dapat diterapkan pada objek-objek dari kelas yang tak-utuh.
Program 6.16 Aplikasi dari operator
reinterpret_cast()
|
|
#include <iostream>
#include <string>
using namespace std;
class A; //kelas tak-utuh
class B; //kelas tak-utuh
class Basis
{
public
:
void
Bicara() {cout<<"Saya adalah kelas Basis.\n";}
};
class D : public Basis
{
public:
void
bicara() {cout<<"Saya adalah kelas D.\n";}
};
int main()
{
int
x = 65;
int*
px = &x; //px adalah
pointer yang menunjuk ke int x
char*
chp = (char*) px; //px dikonversi
menjadi pointer char* chp
//dengan konversi
konvensional bahasa C
cout<<"*chp = "<<*chp<<endl; //statemen keluaran untuk *chp
cout<<*px <<endl;
//berikut konversi sama yang dilakukan
oleh reinterpret_cast<>()
char*
ptrx = reinterpret_cast<char*> (px);
cout<<"*ptrx =
"<<*ptrx<<endl;
D* ptrd = reinterpret_cast<D*>(px);
//px dikonversi menjadi tipe D
ptrd -> bicara();
B* bp; //Operator
ini juga dapat diterapkan pada kelas tak-utuh
A* ap = reinterpret_cast<A*>(bp);
}
|
|
KELUARAN
|
|
*chp = A
65
*ptrx = A
Saya adalah
kelas D.
|
Pada program berikut, operator reinterpret_cast()
dipakai pada pointer yang menunjuk ke fungsi.
Program 6.17 Aplikasi dari operator
reinterpret_cast()
|
|
#include <iostream>
using namespace std;
void Fungsi1()
{
cout<<"Ini adalah fungsi void
dengan argumen void.\n";
}
void Fungsi2(int m, int n)
{
cout<<"Ini adalah fungsi void
dengan dua argumen.\n";
}
int Fungsi3
(int x, int y)
{
return
x*y;
}
int main()
{
int
x = 65;
int
* px = &x; //px
adalah pointer yang menunjuk ke int x
typedef
void (*PF)(); //typedef atas pointer ke fungsi void
PF fungsi1 = (PF)Fungsi1; //menggunakan konversi konvensional
fungsi1() ; //pemanggilan fungsi
PF fungsi2 =
reinterpret_cast<PF>(Fungsi1); //oleh
reinterpret_cast
fungsi2(); //
fungsi2 dipanggil
int
y = 2;
PF aFungsi2 =
reinterpret_cast<PF>(Fungsi2);
aFungsi2();
typedef
int(*pF)(int, int); //definisi tipe atas pointer yang menunjuk
ke
//fungsi-fungsi
int yag memiliki dua parameter int
pF fungsi3 = (pF)Fungsi3 ;
//konversi konvensional dari bahasa C
cout<<fungsi3(x,y )<<endl;
//pemanggilan fungsi
//berikut adalah konversi yang sama menggunakan
reinterpret_cast<>()
pF fungsi4 =
reinterpret_cast<pF>(Fungsi3);
cout<< fungsi4(x,y)<< endl;
return
0;
}
|
|
KELUARAN
|
|
Ini adalah
fungsi void dengan argumen void.
Ini adalah
fungsi void dengan argumen void.
Ini adalah
fungsi void dengan dua argumen.
130
130
|
OPERATOR const_cast<>()
Operator ini menyediakan akses terhadap sebuah variabel dengan atribut const atau volatile. Objek-objek yang dideklarasikan volatile dapat dimodifikasi dari luar kendali program. Operator ini
tidak mengubah atribut dasar dari variabel. Sintaksis dari operator ini adalah
berikut.
const_cast <T*> (objek_ptr)
Program berikut mencoba untuk mengaplikasikan operator const_cast().
Program 6.18 Aplikasi dari operator
const_cast()
|
|
#include <iostream>
using namespace std;
void F(char* M)
{cout<<M<<endl;}
int main()
{
const
char * S= "Jakarta";
//F(S); Error, tidak dapat mengkonversi
dari const char* menjadi char*
F(const_cast<char*>(S)); //ini OK
const
int n = 10;
const
int *ptrn = &n;
// n= n+5; Error, n adalah const. Tetapi
berikut OK
*(const_cast<int*>(ptrn)) = n + 5;
cout<<*(const_cast<int*>(ptrn))<<endl;
//const_cast<int>(n) = n+8; Ini
menghasilkan error, operator const_cast
//tidak dapat mengkonversi dari const int
menjadi int.
cout << n <<endl;
return
0 ;
}
|
|
KELUARAN
|
|
Jakarta
15
10
|
LATIHAN
1.
Bagaimana Anda
mendeklarasikan pointer yang menunjuk ke sebuah kelas?
2.
Bagaimana
Anda membuat pointer kelas basis agar
menunjuk ke objek dari kelas terderivasi?
3.
Apa keuntungan
dari pendeklarasian sebuah fungsi kelas basis sebagai fungsi virtual?
4.
Apa itu kelas
basis virtual?
5.
Apa perbedaan
antara fungsi virtual dan fungsi terbebani?
6.
Apa itu kelas
abstrak? Apa gunanya kelas abstrak?
7.
Apa yang Anda
pahami tentang pengikatan dinamis?
8.
Apa yang Anda
pahami tentang polimorfisme?
9.
Mengapa
konstruktor tidak bisa dideklarasikan virtual sementara destruktor dapat
dideklarasikan virtual?
10.
Apa keuntungan
dari pendeklasian virtual atas destruktor?
11.
Tulislah sebuah
program yang mengilustrasikan fungsi virtual murni Bicara() di dalam kelas basis Mahluk.
Kelas-kelas terderivasi adalah kelas Anjing,
kelas Kucing, kelas IngMan (orang Inggris), dan IndMan (orang Indonesia). Fungsi Bicara() menampilkan bahasa lidah
masing-masing.
Jawaban:
Program 6.19 Contoh lain dari kelas
abstrak dan fungsi virtual murni
|
|
#include <iostream>
using namespace std;
class Mahluk
{
public:
virtual
void Bicara() = 0;
};
class Anjing : public Mahluk
{
public:
void
Bicara ()
{cout<<"Guk guk guk"<<endl;}
};
class Kucing : public Mahluk
{
public:
void
Bicara(void)
{cout<<"Meonk meonk meonk"<<endl;}
};
class IngMan : public Mahluk
{
public
:
void
Bicara(){cout<<"I can speak English"<<endl;}
};
class IndMan : public Mahluk
{
public
:
void
Bicara(){cout<<"Saya bicara bahasa Indonesia"<<endl;}
};
void main ()
{
Anjing A;
Kucing K;
IngMan IgM;
IndMan InM;
A.Bicara();
K.Bicara();
IgM.Bicara();
InM.Bicara();
cout<<"\n";
}
|
|
KELUARAN
|
|
Guk guk guk
Meonk meonk
meonk
I can speak
English
Saya bicara
bahasa Indonesia
|
No comments:
Post a Comment