Friday, December 23, 2016

Bab 13. C++ Untuk Programer



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