Bab. 13 Beberapa Topik Penting
Lainnya
Tujuan
Instruksional
|
|
·
Operator const_cast.
·
Anggota kelas Mutable.
·
namespace.
·
Katakunci operator.
|
·
Pointer ke anggota
kelas (.* dan ->*)
·
Pewarisan jamak.
·
Kelas basis virtual.
|
13.1 Introduksi
Pada kesempatan ini akan dibahas
beberapa fitur tambahan C++. Akan didiskusikan tentang operator const_cast, yang mengijinkan Anda untuk
menambah atau menghapus kualifikasi const
atas suatu variabel. Selanjutnya, akan didiskusikan mengenai namespace, yang berguna untuk memastikan
setiap pengenal di dalam program memiliki nama yang unik dan bermanfaat untuk
menyelesaikan konflik penamaan yang diakibatkan oleh penggunaan pustaka yang
memiliki nama variabel, nama fungsi, dan nama kelas sama.
Kemudian akan disajikan beberapa
katakunci operator, berguna bagi programmer yang memiliki papanketik yang tidak
mendukung beberapa karakter tertentu seperti !, &, ^, ~, dan |. Diskusi
dilanjutkan dengan topik penspesifikasi kelas mutable, yang memampukan Anda untuk mengindikasikan bahwa suatu
anggota data harus selalu bisa dimodifikasi, meskipun ia berada di dalam suatu
objek yang saat ini diperlakukan sebagai objek const oleh program.
Berikutnya akan diintroduksi dua
operator spesial yang dapat digunakan dengan pointer yang menunjuk kepada
anggota kelas untuk mengakses anggota data atau fungsi anggota tanpa perlu
mengetahui namanya terlebih dahulu. Di bagian akhir, akan dikenalkan pewarisan
jamak, yang memampukan kelas terderivasi untuk mewarisi anggota-anggota dari
beberapa kelas basis. Akan didiskusikan pula tentang beberapa masalah potensial
dari pewarisan jamak dan tentang pewarisan virtual
yang dapat memecahkan masalah tersebut.
13.2 Operator const_cast
C++
menyediakan operator const_cast untuk
membuang kualifikasi const atau volatile. Anda mendeklarasikan variabel dengan kualifikasi volatile ketika Anda mengharapkan
variabel tersebut dimodifikasi oleh perangkat keras atau oleh program lain yang
dikenal kompiler. Pendeklarasikan variabel sebagai volatile mengindikasikan bahwa kompiler tidak mengoptimasikan
penggunaan variabel tersebut karena, jika dilakukan, akan mempengaruhi program
lain (yang tak dikenal kompiler) dalam mengakses dan memodifikasi variabel volatile.
Secara
umum, adalah berbahaya bila menggunakan operator const_cast, karena hal ini mengijinkan program untuk memodifikasi
variabel yang dideklarasikan const. Terdapat
beberapa kasus dimana hal ini perlu dilakukan. Misalnya, pada pustaka lama C
dan C+, terdapat beberapa fungsi yang memiliki parameter non-const dan tidak memodifikasi parameter
tersebut. Jadi, ketika Anda berniat untuk melewatkan data const kepada fungsi semacam itu, maka Anda perlu membuang
kualifikasi const pada data tersebut,
karena, jika tidak, kompiler akan mengeluarkan pesan kesalahan.
Sama
halnya, Anda bisa melewatkan data non-const
kepada fungsi yang memperlakukan data sebagai konstanta, kemudian menghasilkan
nilai balik berupa data tersebut (sebagai konstanta). Pada kasus seperti itu,
Anda bisa membuang kualifikasi const
pada nilai balik menggunakan operator const_cast,
seperti yang didemonstrasikan pada Gambar 13.1.
Gambar 13.1 Mendemonstrasikan const_cast
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
|
// Gambar 13.1: gambar13_01.cpp
// Mendemonstrasikan const_cast.
#include <iostream>
#include <cstring> // memuat
protototipe untuk fungsi strcmp dan strlen
#include <cctype> // memuat
prototipe untuk fungsi toupper
using namespace std;
// menghasilkan string terbesar C-style
const char *maksimum( const char *pertama, const
char *kedua )
{
return ( strcmp( pertama, kedua )
>= 0 ? pertama : kedua );
} // akhir dari fungsi maksimum
int main()
{
char s1[] = "selamat";
// array karakter yang bisa dimodifikasi
char s2[] = "jalan";
// array karakter yang bisa dimodifikasi
// const_cast
dibutuhkan untuk mengijinkan const char * yang dijadikan nilai balik
// oleh maksimum
ditugaskan kepada variabel char * variable maksPtr
char *maksPtr = const_cast<
char * >( maksimum( s1, s2 ) );
cout << "String yang lebih
besar adalah: " << maksPtr << endl;
for ( size_t i = 0; i <
strlen( maksPtr ); ++i )
maksPtr[ i ] = toupper( maksPtr[ i ] );
cout << "String yang
dikapitalisasi: " << maksPtr << endl;
} // akhir dari main
|
String
yang lebih besar adalah: selamat
String
yang dikapitalisasi: SELAMAT
Pada
program ini, fungsi maksimum (baris
9-12) menerima dua string C-style sebagai parameter char* dan menghasilkan nilai balik const char* yang menunjuk kepada string terbesar dari dua string
yang ada. Fungsi main mendeklarasikan
dua string C-style sebagai array non-const
char (baris 16-17); jadi, kedua array itu dapat dimodifikasi. Dalam main, Anda diharapkan menampilkan string
terbesar dari kedua string C-style, kemudian memodifikasi string C-style
tersebut dengan mengkapitalisasi semua hurufnya.
Dua
parameter fungsi maksimum bertipe const
char*, jadi nilai baliknya harus bertipe const char* pula. Jika nilai balik fungsi itu dispesifikasi sebagai
char*, maka kompiler akan menampilkan
pesan kesalahan yang mengindikasikan bahwa nilai yang dijadikan nilai balik
tidak bisa dikonversi dari const char*
menjadi char*.
Meskipun
fungsi maksimum percaya bahwa datanya
bersifat konstanta, namun diberikan dua array di dalam main yang tidak memuat data konstanta. Oleh karena itu, main harus dapat memodifikasi isi kedua
array tersebut jika diperlukan. Karena diketahui bahwa kedua array tersebut
bisa dimodifikasi, maka Anda perlu menggunakan const_cast (baris 21) untuk membuang kekonstantaan pointer yang
dijadikan nilai balik oleh maksimum,
sehingga Anda dapat memodifikasi data di dalam array yang memuat string
terbesar dari dua string C-style yang diberikan. Anda kemudian dapat
menggunakan pointer (nama array karakter) di dalam statemen for (baris 25-26) untuk mengkonversi isi
string terbesar menjadi huruf besar semua. Tanpa operator const_cast pada baris 21, program ini tidak akan bisa dikompilasi,
karena Anda tidak diijinkan untuk menugaskan pointer bertipe const char* kepada pointer bertipe char*.
13.3 Anggota Kelas mutable
Pada
Anda telah dikenalkan operator const_cast,
yang membolehkan pembuangan kekonstantaan suatu tipe. Operasi const_cast dapat pula diterapkan
terhadap anggota data suatu objek const
dari tubuh suatu anggota fungsi const.
Hal ini memampukan fungsi anggota const
untuk memodifikasi anggota data, meskipun objek dipandang sebagai const di dalam tubuh fungsi tersebut.
Operasi semacam itu dilakukan ketika semua anggota data suatu objek
dideklarasikan const, tetapi pada
anggota data tertentu perlu dilakukan pemodifikasian.
Sebagai
contoh, pikirkan tentang list berantai yang menetapkan isinya dengan tatanan
berurutan. Pencarian dalam list berantai tidak memerlukan pemodifikasian
terhadap data, sehingga fungsi pencarian dapat berupa suatu fungsi anggota const pada kelas list berantai. Namun, adalah
masuk akal bila di dalam suatu objek list berantai, agar pencarian selanjutnya
lebih efisien, dilakukan penjejakan lokasi kecocokan terakhir. Untuk
melakukannya, fungsi anggota const yang melaksanakan pencarian harus dapat
memodifikasi anggota data yang menjejak lokasi kecocokan terakhir.
Jika
anggota data seperti yang telah dijelaskan selalu bisa dimodifikasi, C++
menyediakan penspesifikasi mutable
sebagai alternatif dari const_cast.
Anggota data mutable selalu bisa
dimodifikasi, meskipun di dalam suatu fungsi anggota const atau objek const. Pemodifikasi
mutable dan const_cast digunakan dalam konteks yang berbeda. Pada objek const tanpa memiliki anggota data mutable, operator const_cast dapat digunakan setiap kali anggota perlu dimodifikasi.
Hal ini akan mereduksi peluang suatu anggota dimodifikasi secara tak sengaja
karena anggota secara permanen tidak dapat dimodifikasi (tanpa penggunaan const_cast). Operasi yang melibatkan const_cast tersembunyi di dalam
implementasi fungsi anggota.
Mendemonstrasikan
Anggota Data mutable
Gambar
13.2 mendemonstrasikan penggunaan anggota data mutable. Program mendefinisikan kelas Uji_Mutable (baris 7-21), yang memuat sebuah konstruktor, fungsi getNilai dan sebuah anggota data private, nilai, yang dideklarasikan mutable.
Baris 15-18 mendefinisikan fungsi getNilai
sebagai fungsi anggota const yang
menghasilkan nilai balik berupa hasil salinan atas nilai. Perhatikan bahwa fungsi menginkremen anggota data mutable, nilai, di dalam statemen return.
Normalnya, fungsi anggota const tidak
dapat memodifikasi anggota data kecuali jika menggunakan const_cast terhadap tipe non-const.
Karena nilai adalah mutable, maka fungsi ini dapat
memodifikasi data.
Gambar 13.2 Mendemonstrasikan penspesifikasi
mutable
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
|
// Gambar 13.2: gambar13_02.cpp
// Mendemonstrasikan penspesifikasi mutable.
#include <iostream>
using namespace std;
// definisi kelas UjiMutable
class UjiMutable
{
public:
UjiMutable( int v = 0 )
{
nilai = v;
} // akhir dari konstruktor UjiMutable
int getNilai() const
{
return ++nilai; // menginkremen
nilai
} // akhir dari fungsi getNilai
private:
mutable int nilai; // anggota mutable
}; // akhir dari kelas UjiMutable
int main()
{
const UjiMutable uji( 99 );
cout << "Nilai awal:
" << uji.getNilai();
cout << "\nNilai
termodifikasi: " << uji.getNilai() << endl;
} // akhir dari main
|
Nilai
awal: 99
Nilai
termodifikasi: 100
Baris
25 mendeklarasikan objek const UjiMutable,
uji, dan menginisialisasinya dengan
99. Baris 27 memanggil fungsi anggota const,
getNilai, yang menambahkan satu
kepada nilai dan menjadikan isi
sebelumnya sebagai nilai balik. Perhatikan bahwa kompiler mengijinkan
pemanggilan terhadap fungsi anggota getNilai
pada objek uji karena objek tersebut
merupakan suatu objek const dan getNilai adalah suatu fungsi anggota const. Namun, getNilai memodifikasi variabel nilai.
Jadi, ketika baris 28 memanggil getNilai
kembali, nilai baru dari nilai (100)
ditampilkan untuk membuktikan bahwa anggota data mutable telah dimodifikasi.
13.4 namespace
Suatu
program dapat menyertakan banyak pengenal yang didefinisikan dalam berbagai
skop. Kadangkala variabel dalam suatu skop tumpang-tindih (bertubrukan) dengan
suatu variabel bernama sama dalam skop yang berbeda, yang kemungkinan akan
menyebabkan konflik penamaan. Kondisi tumpang-tindih semacam itu terjadi pada
banyak level. Ketumpang-tindihan pengenal seringkali terjadi dalam pustaka
pihak-ketiga, yang secara tak-sengaja menggunakan pengenal global yang sama
(seperti nama fungsi). Hal ini akan menyebabkan error kompilasi.
C++
standard memecahkan masalah ini dengan penggunaan namespace. Setiap namespace
mendefinisikan skop dimana pengenal dan variabel ditempatkan. Untuk menggunakan
anggota suatu namespace, Anda harus
mencantumkan nama anggota, nama namespace, dan operator resolusi skop (::),
seperti ini:
NamaSpaceKu::anggota
atau
menggunakan direktif using. Secara
umum, statemen using semacam itu ditempatkan di awal file dimana
anggota-anggota namespace digunakan. Sebagai contoh, perhatikan penempatan
direktif using pada awal suatu file
kode-sumber
using
namespace NamaSpaceKu;
menyatakan
bahwa anggota-anggota namespace, NamaSpaceKu, dapat digunakan di dalam
file tanpa perlu mengawali setiap anggota dengan NamaSpaceKu dan operator resolusi skop (::).
Penggunaan
direktif using dalam format
using std::cout;
membawa
satu nama ke dalam skop dimana direktif berada. Penggunaan direktif using dalam format
using namespace std;
membawa
semua nama dari namespace tertentu (std)
ke dalam skop dimana direktif berada.
Tidak
semua namespace dijamin unik. Dua pertiga vendor bisa jadi menggunakan nama
pengenal sama secara tak-sengaja untuk nama-nama namespace. Gambar 13.3 mendemonstrasikan kegunaan namespace.
Gambar 13.3 Mendemonstrasikan beberapa namespace
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 13.3: gambar13_03.cpp
// Mendemonstrasikan beberapa namespace.
#include <iostream>
using namespace std;
int integer1 = 98; // variabel global
// Menciptakan namespace Contoh
namespace Contoh
{
//
mendeklarasikan dua konstanta dan satu variabel
const
double PI = 3.14159;
const
double E = 2.71828;
int
integer1 = 8;
void
tampilNilai(); // prototipe
//
namespace bersarang
namespace
Dalam
{
//
mendefinisikan enumerasi
enum
Tahun { FISKAL1 = 1990, FISKAL2, FISKAL3 };
} //
akhir dari namespace Dalam
} // akhir dari namespace Contoh
// menciptakan namespace tak-bernama
namespace
{
double
doubleTakBernama = 88.22; // mendeklarasikan variabel
} // akhir dari namespace tak-bernama
int main()
{
// menampilkan nilai
doubleTakBernama dari namespace tak-bernama
cout << "doubleTakBernama =
" << doubleTakBernama;
// menampilkan variabel global
cout << "\n(global) integer1 =
" << integer1;
// menampilkan nilai-nilai dari namespace
Contoh
cout << "\nPI = "
<< Contoh::PI
<< "\nE = " << Contoh::E
<< "\ninteger1 = "
<< Contoh::integer1
<< "\nFISKAL3 = "
<< Contoh::Dalam::FISKAL3 << endl;
Contoh::tampilNilai(); // memanggil fungsi tampilNilai
} // end main
// menampilkan variabel dan nilai konstanta
void Contoh::tampilNilai()
{
cout << "\nDalam tampilNilai:\ninteger1
= " << integer1 << "\nPI = "
<< PI << "\nE =
" << E << "\ndoubleTakBernama = "
<< doubleTakBernama <<
"\n(global) integer1 = " << ::integer1
<< "\nFISKAL3 = "
<< Dalam::FISKAL3 << endl;
} // akhir dari tampilNilai
|
doubleTakBernama = 88.22
(global) integer1 = 98
PI = 3.14159
E = 2.71828
Integer1 = 8
FISKAL3 = 1992
Mendefinisikan
namespace
Baris
9-24 menggunakan katakunci namespace
untuk mendefinisikan namespace Contoh.
Tubuh suatu namespace diapit oleh kurung-kurawal ({}). Anggota-anggota
namespace Contoh memuat dua konstanta
(PI dan E pada baris 12-13), sebuat int
(integer1 pada baris 14), sebuah
fungsi (tampilNilai pada baris 16)
dan sebuah namespace bersarang (Dalam
pada baris 19-23). Perhatikan bahwa anggota integer1
memiliki nama sama dengan variabel global integer1
(baris 6). Variabel-variabel bernama sama harus memiliki skop yang berbeda,
jika tidak, maka error kompilasi akan terjadi. Sebuah namespace dapat memuat
konstanta, data, kelas, namespace
bersarang, fungsi, dan lainnya. Definisi namespace harus menempati skop global
atau bersarang di dalam namespace lain.
Baris
27-30 menciptakan sebuah namespace tak-bernama yang memuat anggota doubleTakBernama. Variabel, kelas, dan
fungsi di dalam sebuah namespace tak-bernama hanya dapat diakes di dalam unit
translasi sekarang (file .cpp atau file yang memuatnya). Tetapi, tidak seperti
variabel, kelas, atau fungsi dengan hubungan static, anggota suatu namespace tak-bernama dapat digunakan sebagai
argumen template. Namespace tak-bernama memiliki direktif using secara implisit, sehingga anggota-anggotanya menempati
namespace global, yang dapat diakses secara langsung dan tidak perlu mencantumkan
nama namespace. Variabel global juga merupakan bagian dari namespace global dan
dapat diakses dari semua skop.
Direktif using
Tidak Boleh Ditempatkan dalam Header
Namespace secara khusus berguna dalam
aplikasi berskala besar yang menggunakan banyak pustaka kelas. Pada kasus
tersebut, terdapat kemungkinan yang lebih tinggi terjadinya konflik penamaan.
Saat bekerja pada proyek semacam itu, tidak boleh terdapat direktif using dalam header. Jika Anda
menempatkan direktif using dalam
header, maka hal itu akan membawa semua nama ke dalam sembarang file yang
mencantumkan header tersebut. Hal ini akan mengakibatkan tubrukan nama yang
sangat susah untuk ditemukan bila terjadi. Direkomendasikan hanya menggunakan,
seperti std::cout atau std::string.
13.5 Katakunci Operator
Standard
C++ menyediakan beberapa katakunci operator (Gambar 13.4) yang dapat digunakan
untuk menggantikan beberapa operator C++. Anda bisa menggunakan katakunci
operator jika Anda memiliki papanketik yang tidak mendukung beberapa karakter
tertentu seperti !, &, ^, ~, |, dan lain-lain.
Gambar
13.4 Katakunci Operator
Operator
|
Katakunci operator
|
Penjelasan
|
&&
||
!
!=
&
|
^
~
&=
|=
^=
|
and
or
not
not_eq
bitand
bitor
xor
compl
end_eq
or_eq
xor_eq
|
AND logikal
OR logikal
NOT logikal
ketak-sama-dengan-an
AND bitwise
OR bitwise
XOR bitwise
komplemen bitwise
penugasan AND bitwise
penugasan OR bitwise
penugasan XOR bitwise
|
Gambar
13.5 mendemonstrasikan beberapa katakunci operator. Microsoft visual 2010
memerlukan header <ciso646>
(baris 4) untuk menggunakan katakunci operator. Pada GNU C++, header ini kosong
karena katakunci operator telah terdefinisi.
Gambar 13.5 Mendemonstrasikan Katakunci Operator
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
|
// Gambar 13.5: gambar13_05.cpp
// Mendemonstrasikan katakunci operator.
#include <iostream>
#include <ciso646> // untuk Microsoft Visual C++
using namespace std;
int main()
{
bool a = true;
bool b = false;
int c = 2;
int d = 3;
// menyebabkan nilai-nilai bool ditampilkan
sebagai true atau false
cout << boolalpha;
cout << "a = "
<< a << "; b = " << b
<< "; c = "
<< c << "; d = " << d;
cout << "\n\nKatakunci
operator logikal:";
cout << "\n a and a:
" << ( a and
a );
cout << "\n a and b:
" << ( a and
b );
cout << "\n a or a: "
<< ( a or a );
cout << "\n a or b: "
<< ( a or b );
cout << "\n not a: "
<< ( not a );
cout << "\n not b: "
<< ( not b );
cout << "\na not_eq b:
" << ( a
not_eq b );
cout << "\n\nKatakunci
operator bitwise:";
cout << "\nc bitand d: "
<< ( c bitand d );
cout << "\nc bit_or d: "
<< ( c bitor d );
cout << "\n c xor d:
" << ( c xor
d );
cout << "\n compl c:
" << ( compl
c );
cout << "\nc and_eq d: "
<< ( c and_eq d );
cout << "\n c or_eq d:
" << ( c
or_eq d );
cout << "\nc xor_eq d: "
<< ( c xor_eq d )
<< endl;
} // akhir dari main
|
a
= true; b = false; c = 2; d = 3
Katakunci
operator logikal:
a and a: true
a and b: false
a or a: true
a or b: true
not a: false
not b: true
a
not_eq b: true
Katakunci
operator bitwise:
c
bitand d: 2
c
bit_or d: 3
c xor d: 1
compl c: -3
c
and_eq d: 2
c or_eq d: 3
c xor_eq d: 0
Program
mendeklarasikan dan menginisialisasi dua variabel bool dan dua variabel integer
(baris 9-12). Operasi-operasi logikal (baris 21-27) dilakukan terhadap variabel
bool a dan b menggunakan
berbagai katakunci operator logikal. Operasi-operasi bitwise (baris 30-36)
dilakukan terhadap variabel int c dan d menggunakan berbagai katakunci operator bitwise. Setiap hasil
operasi kemudian ditampilkan.
13.6 Pointer ke Anggota Kelas (.* dan
->*)
C++
menyediakan operator .* dan ->* untuk mengakses anggota kelas melalui
pointer. Ini merupakan kapabilitas yang jarang dipakai dan hanya digunakan oleh
programer C++ handal. Di sini hanya akan disajikan contoh penggunaan pointer
yang menunjuk ke beberapa anggota kelas. Gambar 13.6 mendemonstrasikan beberapa
pointer-ke-anggota-kelas.
Gambar 13.6 Mendemonstrasikan Operator .* dan
->*
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
|
// Gambar 13.6: gambar13_06.cpp
// Mendemonstrasikan operator .* dan ->*.
#include <iostream>
using namespace std;
// definisi kelas Uji
class Uji
{
public:
void fungsi()
{
cout << "Dalam fungsi\n";
} // akhir dari fungsi fungsi
int nilai; // angota data publik
}; // akhir dari kelas Uji
void arrowStar( Uji * ); // prototipe
void dotStar( Uji * ); // prototipe
int main()
{
Uji uji;
uji.nilai = 8; // menugaskan nilai 8
arrowStar( &uji ); // melewatkan alamat
kepada arrowStar
dotStar( &uji ); // melewatkan alamat
kepada dotStar
} // akhir dari main
// mengakses fungsi anggota objek Uji
menggunakan ->*
void arrowStar( Uji *ujiPtr )
{
void ( Uji::*anggotaPtr )() =
&Uji::fungsi; // mendeklarasikan pointer fungsi
( ujiPtr->*anggotaPtr )();
// memanggil fungsi secara tak-langsung
} // akhir dari arrowStar
// mengakses anggota objek Uji menggunakan
.*
void
dotStar( Uji *ujiPtr2 )
{
int Uji::*vPtr = &Uji::nilai;
// mendeklarasikan pointer
cout << ( *ujiPtr2
).*vPtr << endl; // mengakses nilai
} // akhir dari dotStar
|
Dalam
fungsi
8
Program
mendeklarasikan kelas Uji (baris
7-16), yang menyediakan fungsi anggota public,
fungsi, dan anggota data public, nilai. Baris 18-19 menyediakan prototipe untuk fungsi arrowStar (didefinisikan pada baris
30-34) dan dotStar (didefinisikan
pada baris 37-41), yang mendemonstrasikan operator ->* dan .*. Baris 23
menciptakan objek uji, dan baris 24
menugaskan 8 kepada anggota data nilai.
Baris 25-26 memanggil fungsi arrowStar
dan dotstar dengan alamat objek uji.
Baris
32 di dalam fungsi arrowStar
mendeklarasikan dan menginisialisasi variabel anggotaPtr sebagai sebuah pointer ke fungsi anggota. Dalam
deklarasi ini, Uji::* mengindikasikan
bahwa nama variabel anggotaPtr
merupakan sebuah pointer ke anggota kelas Uji.
Untuk mendeklarasikan pointer ke fungsi, nama pointer yang diawali dengan *
diapit oleh sepasang kurung, seperti (Uji::*anggotaPtr).
Pointer ke suatu fungsi harus mencantumkan, sebagai bagian dari tipenya, kedua
tipe nilai balik fungsi yang ditunjuk dan daftar parameter fungsi tersebut.
Tipe nilai balik fungsi ditempatkan di sebelah kiri kurung dan daftar parameter
ditempatkan dan diapit oleh sepasang kurung yang lain di sebelah kanan
deklarasi pointer. Pada kasus ini, fungsi memiliki nilai balik bertipe void dan tidak mempunyai parameter.
Pointer
anggotaPtr diinisialisasi dengan alamat
fungsi anggota fungsi pada kelas Uji. Header fungsi harus sesuai dengan
deklarasi pointer fungsi, yaitu fungsi fungsi
harus memiliki tipe nilai balik void
dan tanpa parameter. Perhatikan bahwa sisi kanan penugasan menggunakan operator
alamat (&) untuk mendapatkan alamat fungsi anggota fungsi. Perhatikan pula bahwa baik sisi kiri atau sisi kanan
penugasan pada baris 32 tidak menunjuk ke objek spesifik dari kelas Uji. Hanya nama kelas yang digunakan dan
operator resolusi skop (::). Baris 33 memanggil fungsi anggota yang disimpan di
dalam anggotaPtr (yaitu, fungsi) menggunakan operator ->*.
Karena anggotaPtr merupakan sebuah
pointer ke suatu anggota kelas, maka operator ->* yang digunakan (bukan operator ->).
Baris
39 mendeklarasikan dan menginisialisasi vPtr
sebagai sebuah pointer ke anggota data int
dari kelas Uji. Sisi kanan penugasan
mengindikasikan alamat anggota data nilai.
Baris 40 melakukan dereferensi pointer ujiPtr2,
kemudian menggunakan operator .* untuk mengakses anggota data yang ditunjuk
oleh vPtr. Kode klien dapat
menciptakan banyak pointer ke anggota kelas yang dapat diakses oleh klien. Pada
contoh ini, baik fungsi anggota fungsi
maupun anggota data nilai
dideklarasikan public.
13.7 Pewarisan Jamak
Dalam
C++, sebuah kelas dapat diderivasi dari lebih dari satu kelas basis, suatu
teknik yang dikenal sebagai pewarisan jamak, dimana di dalamnya kelas
terderivasi mewarisi anggota-anggotanya dari dua atau lebih kelas basis.
Kapabilitas ini dapat mendorong pendaur-ulangan kode yang efisien, tetapi dapat
pula menyebabkan berbagai masalah serius. Pewarisan jamak merupakan konsep
rumit yang seharusnya hanya digunakan oleh para programmer berpengalaman. Pada
kenyataannya, berbagai masalah yang disebabkan oleh pewarisan jamak sangat
susah dideteksi, sehingga bahasa pemrograman yang lebih muda, seperti Java dan
C#, tidak mengijinkan sebuah kelas diderivasi dari lebih dari satu kelas basis.
Masalah
yang umum terjadi pada pewarisan jamak adalah bahwa setiap kelas basis bisa
jadi memuat anggota data atau fungsi anggota yang memiliki nama sama. Hal ini
bisa menyebabkan masalah ambiguitas ketika dikompilasi. Perhatikan contoh
pewarisan jamak (Gambar 13.7, 13.8, 13.9, 13.10, dan 13.11). Kelas Basis1
(Gambar 13.7) memuat satu anggota data protected
int, nilai (baris 20), sebuah
konstruktor (baris 10-13) yang menetapkan nilai
dan fungsi anggota public, getData, pada baris 15-18 yang
menghasilkan nilai balik nilai.
Gambar 13.7 Definisi kelas Basis1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// Gambar 13.7: Basis1.h
// Definisi kelas Basis1
#ifndef BASIS1_H
#define BASIS1_H
// definisi kelas Basis1
class Basis1
{
public:
Basis1( int nilaiParameter )
{
nilai = nilaiParameter;
} // konstruktor Basis1
int getData() const
{
return nilai;
} // akhir dari fungsi getData
protected: // hanya bisa diakses dari kelas terderivasi
int nilai; // diwarisi oleh kelas
terderivasi
}; // akhir dari kelas Basis1
#endif // BASIS1_H
|
Kelas
Basis2 (Gambar 13.8) mirip dengan
kelas Basis1, dimana data
terproteksinya (huruf) bertipe char (bari 20). Seperti kelas Basis1, Basis2 memiliki sebuah fungsi anggota public, getData, tetapi
fungsi ini menghasilkan nilai balik berupa anggota data char, huruf.
Gambar 13.8 Definisi kelas Basis2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// Gambar 13.8: Basis2.h
// Definition of class Basis2
#ifndef BASIS2_H
#define BASIS2_H
// definisi kelas Basis2
class Basis2
{
public:
Basis2( char dataKarakter )
{
huruf = dataKarakter;
} // akhir dari konstruktor Basis2
char getData() const
{
return huruf;
} // akhir dari fungsi getData
protected: // hanya bisa diakses dari kelas terderivasi
char huruf; // diwarisi oleh kelas
terderivasi
}; // akhir dari kelas Basis2
#endif // BASIS2_H
|
Kelas
Terderivasi (Gambar 13.9 - 13.10)
mewarisi kedua kelas Basis1 dan Basis2 melalui pewarisan jamak. Kelas Terderivasi mempunyai sebuah anggota
data private bertipe double bernama riil (baris 20), sebuah konstruktor untuk menginisialisasi semua
data pada kelas Terderivasi dan
sebuah fungsi anggota getRiil yang
menghasilkan nilai balik berupa nilai dari variabel riil.
Gambar 13.9 Definisi kelas Basis2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// Gambar 13.9: Terderivasi.h
// Definisi kelas Terderivasi yang mewarisi
// dua kelas basis (Basis1 dan Basis2).
#ifndef TERDERIVASI_H
#define TERDERIVASI_H
#include <iostream>
#include "Basis1.h"
#include "Basis2.h"
using namespace std;
// definisi kelas terderivasi
class Terderivasi : public Basis1, public
Basis2
{
friend ostream &operator<<(
ostream &, const Terderivasi & );
public:
Terderivasi( int, char, double
);
double getRiil() const;
private:
double riil; // data private kelas
terderivasi
}; //
akhir kelas Terderivasi
#endif // TERDERIVASI_H
|
Gambar 13.10 Definisi kelas Basis2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// Gambar 13.10: Terderivasi.cpp
// Definisi fungsi anggota untuk kelas
Terderivasi
#include "Terderivasi.h"
// konstruktor Terderivasi memanggil
konstruktor
// kelas Basis1 dan Basis2.
// menggunakan
penginisialisasi anggota untuk memanggil konstruktor kelas-basis
Terderivasi::Terderivasi( int integer, char
karakter, double double1 )
:
Basis1( integer ), Basis2( karakter ), riil( double1 ) { }
// mengembalikan riil
double Terderivasi::getRiil() const
{
return riil;
} // akhir dari fungsi getRiil
// menampilkan semua anggta dari Terderivasi
ostream &operator<<( ostream
&keluaran, const Terderivasi &terderivasi )
{
keluaran << " Integer:
" << terderivasi.nilai << "\n Karakter: "
<< terderivasi.huruf <<
"\nBilangan riil: " << terderivasi.riil;
return keluaran; // membuat
pemanggilan bertingkat
} // akhir dari operator<<
|
Untuk
mengindikasikan pewarisan jamak dicantumkan titik dua (:) setelah kelas Terderivasi dengan kelas-kelas basis
yang dipisahkan dengan koma (baris 13, Gambar 13.9). Pada Gambar 13.10,
perhatikan bahwa konstruktor Terderivasi
secara eksplisit memanggil konstruktor dari setiap kelas basis, Basis1 dan Basis2, menggunakan sintaks penginisialisasi-anggota (baris 9).
Konstruktor kelas basis dipanggil secara berurutan sesuai dengan hirarki
pewarisan. Jika konstruktor kelas basis tidak dipanggil secara eksplisit dalam
daftar penginisialisasi-anggota, maka konstruktor default yang akan dipanggil
secara implisit.
Operator
penyisipan stream teroverload (Gambar 13.10, baris 18-23) menggunakan argumen
keduanya, sebuah referensi ke suatu objek Terderivasi,
untuk menampilkan data pada objek Terderivasi.
Fungsi operator ini adalah fungsi friend
dari Terderivasi, sehingga operator<< dapat secara langsung
mengakses semua anggota protected dan
private pada kelas Terderivasi, termasuk anggota data
terproteksi nilai (diwarisi dari
kelas Basis1), anggota data
terproteksi huruf (diwarisi dari
kelas Basis2) dan anggota data privat
riil (dideklarasikan di dalam kelas Terderivasi).
Sekarang
akan diuji fungsi main (Gambar 13.11)
yang menguji kelas-kelas pada Gambar 13.7 - 13.10. Baris 11 menciptakan objek Basis1, basis1, dan menginisialisasinya dengan nilai int 10, kemudian menciptakan pointer basis1Ptr dan menginisialisasinya dengan pointer null. Baris 12 menciptakan objek Basis2, basis2, dan menginisialisasinya dengan nilai char ‘Z’, kemudian menciptakan pointer basis2Ptr. Baris 13 menciptakan objek Terderivasi, terderivasi,
dan menginisialisasinya untuk memuat nilai int
7, nilai char ‘A’, dan nilai double 3.5.
Gambar 13.11 Menguji pewarisan jamak
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
|
// Gambar 13.11: gambar13_11.cpp
// Menguji pewarisan jamak.
#include <iostream>
#include "Basis1.h"
#include "Basis2.h"
#include "Terderivasi.h"
using namespace std;
int main()
{
Basis1 basis1( 10 ), *basis1Ptr = 0; //
menciptakan objek Basis1
Basis2 basis2( 'Z' ), *basis2Ptr = 0; //
menciptakan objek Basis2
Terderivasi terderivasi( 7, 'A', 3.5 ); // menciptakan objek
Terderivasi
// menampilkan anggota-anggora data dari
objek kelas basis
cout << "Objek basis1 memuat
integer " << basis1.getData()
<< "\nObjek basis2 memuat
karakter " << basis2.getData()
<< "\nObjek terderivasi
memuat:\n" << terderivasi << "\n\n";
// menampilkan anggota-anggota data dari
objek kelas terderivasi
// operator resolusi skop memecahkan masalah
ambiguitas getData
cout << "Anggota-anggota
data dari kelas Terderivasi dapat diakses secara individual:"
<< "\n Integer: "
<< terderivasi.Basis1::getData()
<< "\n Karakter: "
<< terderivasi.Basis2::getData()
<< "\nBilangan riil: "
<< terderivasi.getRiil()
<< "\n\n";
cout << "Terderivasi
dapat dipandang sebagai objek dari kelas basis:\n";
// memperlakukan Terderivasi sebagai objek
Basis1
basis1Ptr = &terderivasi;
cout << "basis1Ptr->getData()
menghasilkan " << basis1Ptr->getData() << '\n';
// memperlakukan Terderivasi sebagai objek
Basis2
basis2Ptr = &terderivasi;
cout << "basis2Ptr->getData()
menghasilkan " << basis2Ptr->getData() << endl;
} // akhir dari main
|
Objek
basis1 memuat integer 10
Objek
basis2 memuat karakter Z
Objek terderivasi
memuat:
Integer: 7
Karakter: A
Bilangan riil: 3.5
Anggota-anggota
data dari kelas Terderivasi dapat diakses secara individual:
Integer: 7
Karakter: A
Bilangan riil: 3.5
Terderivasi dapat dipandang sebagai objek dari kelas
basis:
basis1Ptr->getData() menghasilkan 7
basis2Ptr->getData() menghasilkan A
Baris
16-18 menampilkan nilai-nilai data setiap objek. Untuk objek basis1 dan basis2, dipanggil fungsi anggota getData dari setiap objek. Meskipun terdapat dua fungsi getData pada contoh ini, pemanggilan
terhadap fungsi itu tidak menjadi ambigu. Pada baris 16, kompiler mengetahui
bahwa basis1 merupakan objek dari
kelas Basis1, sehingga getData dari kelas Basis1 yang dipanggil. Pada baris 17, kompiler mengetahui bahwa basis2 merupakan objek dari kelas Basis2, sehingga getData dari kelas Basis2
yang dipanggil. Baris 18 menampilkan isi objek terderivasi menggunakan operator penyisipan stream teroverload.
13.8 Pewarisan Jamak dan Kelas Basis
virtual
Pewarisan
jamak digunakan, misalnya, di dalam pustaka C++ standard untuk membentuk kelas basic_iostream (Gambar 13.12).
Gambar
13.12 Pewarisan jamak untuk membentuk kelas basic_iostream
Kelas
basic_ios merupakan kelas basis bagi
kedua kelas basic_istream dan basic_ostream, masing-masing dibentuk
dari pewarisan tunggal. Hal ini memampukan objek-objek kelas basic_iostram untuk menyediakan
fungsionalitas dari basic_istream dan
basic_ostream. Dalam hirarki
pewarisan jamak, situasi yang dideskripsikan pada Gambar 13.12 dikenal pula
dengan pewarisan diamond.
Karena
kelas basic_istream dan basic_ostream masing-masing mewarisi basic_ios, masalah berpotensi terjadi
pada basic_iostream. Kelas basic_iostream dapat memuat dua salinan
dari anggota kelas basic_ios, satu
diwarisi melalui kelas basic_istream
dan satu lagi diwarisi melalui kelas basic_ostream.
Situasi semacam itu akan menjadi ambigu dan dapat menyebabkan error kompilasi,
karena kompiler tidak akan mengetahui versi mana dari angota kelas basic_ios yang digunakan. Pada bagian
ini, Anda akan melihat bagaimana menggunakan kelas basis virtual untuk menyelasikan masalah pewarisan dari kelas basis
tak-langsung.
Error Kompilasi
Dihasilkan Ketika Ambiguitas Terjadi dalam Hirarki Diamond
Gambar
13.13 mendemonstrasikan ambiguitas yang dapat terjadi di dalam hirarki diamond.
Kelas Basis (baris 8-12) memuat fungsi
virtual, tampil, pada baris 11. Kelas TerderivasiSatu
(baris 15-23) dan TerderivasiDua
(baris 26-34) masing-masing secara publik mewarisi dari Basis dan mengoverride
fungsi tampil.
Gambar 13.13 Pewarisan Jamak
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
|
// Gambar 13.13: gambar13_13.cpp
// Mencoba secara polimorfik memanggil
sebuah fungsi
// yang secara jamak diwarisi dari dua kelas
basis.
#include <iostream>
using namespace std;
// definisi kelas Basis
class Basis
{
public:
virtual void tampil() const = 0; // fungsi virtual
}; // akhir dari kelas Basis
// definisi kelas TerderivasiSatu
class TerderivasiSatu : public Basis
{
public:
//
mengoverride fungsi tampil
void
tampil() const
{
cout
<< "TerderivasiSatu\n";
} //
akhir dari fungsi tampil
}; // akhir dari kelas TerderivasiSatu
// definisi kelas TerderivasiSatu
class TerderivasiDua : public Basis
{
public:
//
mengoverride fungsi tampil
void
tampil() const
{
cout
<< "TerderivasiDua\n";
} //
akhir dari fungsi tampil
}; // akhir dari kelas TerderivasiDua
// definisi kelas Jamak
class Jamak : public TerderivasiSatu, public
TerderivasiDua
{
public:
//
menentukan versi mana dari fungsi tampil
void
tampil() const
{
TerderivasiDua::tampil();
} //
akhir dari fungsi tampil
}; // akhir dari kelas Jamak
int main()
{
Jamak keduanya; // menginstansiasi objek
Jamak
TerderivasiSatu satu; // menginstansiasi
objek TerderivasiSatu
TerderivasiDua dua; // menginstansiasi
TerderivasiDua
Basis *array[ 3 ]; // menciptakan array yang
memuat pointer kelas basis
array[ 0 ] = &keduanya; // ERROR--ambigu
array[ 1 ] = &satu;
array[ 2 ] = &dua;
// secara polimorfik memanggil fungsi tampil
for ( int i = 0; i < 3; ++i
)
array[ i ] -> tampil();
} // akhir main
|
gambar22_13.cpp:
In function ‘int main()’:
gambar22_13.cpp:54:
error: ‘Basis’ is an ambiguous base of ‘Jamak’
Kelas
Jamak (baris 37-45) mewarisi kedua
kelas TerderivasiSatu dan TerderivasiDua. Dalam kelas Jamak, fungsi tampil dioverride (didefinisikan-ulang) untuk
memanggil fungsi tampil pada kelas TerderivasiDua (baris 43). Perhatikan
bahwa Anda harus mencantumkan pemanggilan tampil
dengan nama kelas TerderivasiDua
untuk menentukan versi tampil yang
mana yang akan dipanggil.
Fungsi
main (baris 47-61) mendeklarasikan
objek-objek kelas Jamak (baris 49), TerderivasiSatu (baris 50) dan TerderivasiDua (baris 51). Baris 52
mendeklarasikan sebuah array yang memuat pointer-pointer Basis *. Setiap elemen array diinisialisasi dengan alamat suatu objek
(baris 54-56). Error akan terjadi bila alamat dari keduanya, sebuah objek kelas Jamak,
ditugaskan kepada array[0]. Objek keduanya sebenarnya memuat dua subobjek
bertipe Basis, sehingga kompiler
tidak mengetahui subobjek yang mana yang ditunjuk oleh array[0] dan membangkitkan error kompilasi yang mengindikasikan
suatu konversi ambigu.
Masalah
ini diatasi dengan pewarisan virtual.
Ketika sebuah kelas basis diwarisi sebagai virtual,
maka hanya akan ada satu subobjek yang akan muncul di dalam kelas terderivasi.
Proses ini dikenal dengan pewarisan kelas-basis virtual. Gambar 13.14 merevisi program pada Gambar 13.13 dengan
menggunakan sebuah kelas basis virtual.
Gambar 13.14 Pewarisan Jamak dengan Pewarisan
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
// Gambar 13.14: gambar13_14.cpp
// Menggunakan kelas basis virtual
#include <iostream>
using namespace std;
// definisi kelas Basis
class Basis
{
public:
virtual void tampil() const = 0; // fungsi virtual
}; // akhir dari kelas Basis
// definisi kelas TerderivasiSatu
class TerderivasiSatu : public Basis
{
public:
//
mengoverride fungsi tampil
void
tampil() const
{
cout
<< "TerderivasiSatu\n";
} //
akhir dari fungsi tampil
}; // akhir dari kelas TerderivasiSatu
// definisi kelas TerderivasiSatu
class TerderivasiDua : public Basis
{
public:
//
mengoverride fungsi tampil
void
tampil() const
{
cout
<< "TerderivasiDua\n";
} //
akhir dari fungsi tampil
}; // akhir dari kelas TerderivasiDua
// definisi kelas Jamak
class Jamak : public TerderivasiSatu, public
TerderivasiDua
{
public:
//
menentukan versi mana dari fungsi tampil
void
tampil() const
{
TerderivasiDua::tampil();
} //
akhir dari fungsi tampil
}; // akhir dari kelas Jamak
int main()
{
Jamak keduanya; // menginstansiasi objek
Jamak
TerderivasiSatu satu; // menginstansiasi
objek TerderivasiSatu
TerderivasiDua dua; // menginstansiasi
TerderivasiDua
// mendeklarasikan array, memuat pointer-pointer
kelas basis dan
// menginisialisasi setiap elemen dengan objek
bertipe kelas-terderivasi
Basis
*array[ 3 ];
array[ 0 ] = &keduanya;
array[ 1 ] = &satu;
array[ 2 ] = &dua;
// secara polimorfik memanggil fungsi tampil
for ( int i = 0; i < 3; ++i
)
array[ i ] -> tampil();
} // akhir main
|
TerderivasiDua
TerderivasiSatu
TerderivasiDua
Kunci
perubahan di sini adalah bahwa kelas TerderivasiSatu
(baris 14) dan TerderivasiDua (baris
25) masing-masing mewarisi dari Basis
dengan menspesifikasi virtual public
Basis. Karena kedua kelas tersebut mewarisi dari Basis, maka masing-masing memuat subobjek Basis. Keuntungan pewarisan virtual
tidak menjadi jelas sampai kelas Jamak
mewarisi TerderivasiSatu dan TerderivasiDua (baris 36). Karena tiap
kelas basis itu menggunakan pewarisan virtual untuk mewarisi anggota-anggota
kelas Basis, kompiler memastikan
bahwa hanya terdapat satu subobjek Basis
yang diwariskan kepada kelas Jamak.
Hal ini mengeliminasi error ambiguitas yang dibangkitkan kompiler pada Gambar 13.13.
Kompiler sekarang mengijinkan konversi implisit atas pointer kelas-terderivasi (&keduanya) menjadi pointer kelas-basis array[0] pada baris 55 di dalam main.
Statemen for pada baris 60-61 secara
polimorfik memanggil tampil untuk
tiap objek.
Kesimpulan
C++ menyediakan operator const_cast untuk membuang kualifikasi const atau volatile.
Program mendeklarasikan sebuah
variabel dengan kualifikasi volatile
ketika program tersebut mengharapkan variabel itu dapat dimodifikasi oleh
program lain. Pendeklarasian sebuah variabel sebagai volatile mengindikasikan bahwa kompiler tidak mengoptimasikan
penggunaan variabel tersebut, karena, jika begitu, akan mempengaruhi kemampuan
program lain untuk mengakses dan memodifikasi variabel volatile tersebut.
Secara umum, adalah berbahaya
menggunakan operator const_cast,
karena hal itu mengijinkan program untuk memodifikasi variabel yang telah
dideklarasikan const, yang seharusnya
tidak boleh dimodifikasi.
Pemodifikasi mutable dan const_cast
digunakan dalam konteks yang berbeda. Pada objek const tanpa memiliki anggota data mutable, operator const_cast
dapat digunakan setiap kali anggota perlu dimodifikasi. Hal ini akan mereduksi
peluang suatu anggota dimodifikasi secara tak sengaja karena anggota secara
permanen tidak dapat dimodifikasi (tanpa penggunaan const_cast).
Suatu program dapat menyertakan banyak
pengenal yang didefinisikan dalam berbagai skop. Kadangkala variabel dalam
suatu skop tumpang-tindih (bertubrukan) dengan suatu variabel bernama sama
dalam skop yang berbeda, yang kemungkinan akan menyebabkan konflik penamaan.
Kondisi tumpang-tindih semacam itu terjadi pada banyak level.
Ketumpang-tindihan pengenal seringkali terjadi dalam pustaka pihak-ketiga, yang
secara tak-sengaja menggunakan pengenal global yang sama (seperti nama fungsi).
Hal ini akan menyebabkan error kompilasi. C++
standard memecahkan masalah ini dengan penggunaan namespace.
Standard C++ menyediakan beberapa
katakunci operator (Gambar 22.4) yang dapat digunakan untuk menggantikan
beberapa operator C++. Anda bisa menggunakan katakunci operator jika Anda
memiliki papanketik yang tidak mendukung beberapa karakter tertentu seperti !,
&, ^, ~, |, dan lain-lain.
C++ menyediakan operator .* dan
->* untuk mengakses anggota kelas melalui pointer. Ini merupakan kapabilitas
yang jarang dipakai dan hanya digunakan oleh programer C++ handal.
Dalam C++, sebuah kelas dapat
diderivasi dari lebih dari satu kelas basis, suatu teknik yang dikenal sebagai
pewarisan jamak, dimana di dalamnya kelas terderivasi mewarisi
anggota-anggotanya dari dua atau lebih kelas basis. Kapabilitas ini dapat mendorong
pendaur-ulangan kode yang efisien, tetapi dapat pula menyebabkan berbagai
masalah serius. Pewarisan jamak merupakan konsep rumit yang seharusnya hanya
digunakan oleh para programmer berpengalaman. Pada kenyataannya, berbagai
masalah yang disebabkan oleh pewarisan jamak sangat susah dideteksi, sehingga
bahasa pemrograman yang lebih muda, seperti Java dan C#, tidak mengijinkan
sebuah kelas diderivasi dari lebih dari satu kelas basis.
Latihan
1.
Perhatikan
potongan kode di bawah ini dan tentukan apakah setiap statemen true atau false. Jelaskan mengapa demikian.
a)
Variabel kilometer terlihat di dalam namespace Data.
b)
Objek string1 terlihat di dalam namespace Data.
c)
Konstanta AMERIKA tidak terlihat di dalam namespace Data.
d)
Konstanta INDONESIA terlihat di dalam namespace Data.
e)
Fungsi fungsi terlihat di dalam namespace Data.
f)
Namespace Data terlihat di dalam namespace InformasiNegara.
g)
Objek peta terlihat di dalam di dalam namespace InformasiNegara.
h)
Objek string1 terlihat di dalam namespace InformasiRegional.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
namespace InformasiNegara
{
using namespace std;
enum Negara { POLANDIA, SWISS, JERMAN,
INDONESIA, AMERIKA };
int kilometer;
string string1;
namespace InformasiRegional
{
short getPopulasi();
// diasumsikan definisinya ada
PetaData peta; // diasumsikan definisinya
ada
} // akhir dari InformasiRegional
} // akhir dari InformasiNegara
namespace Data
{
using
namespace InformasiNegara:: InformasiRegional;
void *fungsi( void *, int );
} // akhir dari Data
|
2.
Bandingkan
mutable dan const_cast. Berikan sedikitnya satu contoh penggunaan mutable dan cons_cast.
3.
Tulislah
sebuah program yang menggunakan const_cast
untuk memodifikasi sebuah variabel const.
Gunakan pointer untuk menunjuk pengenal const
tersebut.
4.
Apa
kegunaan kelas basis virtual?
5.
Tulislah
sebuah program yang menggunakan kelas basis virtual. Kelas pada hirarki
pewarisan teratas harus menyediakan sebuah konstruktor yang memerlukan
sedikitnya satu argumen (jangan sediakan konstruktor default).
6.
Temukan
dan perbaiki kesalahan pada tiap potongan kode berikut ini:
a) namespace Nama {
int
x;
int
y;
mutable
inte z;
};
b) int integer = const_cast< int >( double );
No comments:
Post a Comment