Constructor Initialization List

Misalkan ada sebuah kelas Parser yang memiliki data member yang bertipe Stack seperti yang dideklarasikan di atas dan salah satu ctor dari Parser memberikan kemungkinan pengguna objek untuk menciptakan objek Parser dengan sekaligus menyatakan ukuran Stack yang dapat digunakan Parser tersebut. Perhatikan Contoh berikut (Kelas Parser yang memiliki data member bertipe Stack)

CODE
1 #include <Stack.h>
2
3 class Parser {
4 public:
5 Parser(int);
6 // …
7 private:
8 Stack sym_stack, op_stack;
9 // …
10 };

Pada saat constructor Parser::Parser(int) dipanggil, anggota data sym stack akan diciptakan dan diinisialisasi melalui konstruktor default Stack::Stack(). Bagaimana jika inisialisasi ingin dilakukan melalui user-defined constructor Stack::Stack(int)? Ada dua cara untuk melakukan hal ini:

  • Member sym stack diciptakan melalui default ctor, lalu ctor Parser::Parser() melakukan operasi assignment. Dengan cara seperti ini terjadi pemanggilan konstruktor Stack::Stack() (tanpa parameter) serta fungsi yang menangani operasi assignment (dua kali pemanggilan).
  • ctor Parser::Parser() melakukan inisialisasi Stack melalui member initialization list seperti ditunjukkan pada Contoh berikut:
    CODE
    1 Parser::Parser(int x) : sym_stack (x), op_stack (x)
    2 {
    3 // …
    4 }

    Dengan cara ini, hanya ada satu kali pemanggilan konstruktor yaitu Stack::Stack (int)

Manfaat constructor initialization list adalah performansi yang lebih baik. Oleh karena itu, anggota data yang bertipe non-primitif sebaiknya diinisialisasi melalui cara ini.
Constructor initialization list dapat digunakan untuk menginisialisasi beberapa member sekaligus. Untuk melakukan hal ini, nama anggota data yang diinisialisasi dituliskan setelah parameter formal konstruktor dan setiap nama anggota diikuti oleh sejumlah argumen yang sesuai dengan user defined constructor yang ada. Dalam Contoh diatas, anggota data sym stack dan op stack diinisialiasi dengan menggunakan satu parameter aktual karena kelas Stack dalam contoh yang sudah disajikan memiliki user defined constructor dengan satu parameter formal.

Const Member
Pada deklarasi variabel, atribut const menyatakan bahwa variabel tersebut bersifat konstan dan nilainya tidak dapat diubah oleh siapapun. Anggota data (data member) maupun anggota fungsi (function member) dapat juga memiliki atribut const. Makna dari atribut ini adalah:

  • Anggota data yang memiliki atribut const berarti bahwa nilai anggota data tersebut akan tetap sepanjang waktu hidup objeknya. Standard ANSI mengharuskan pengisian nilai awal terhadap anggota data const dilakukan pada saat objek tersebut diciptakan. Bandingkanlah dengan definisi konstan yang juga memanfaatkan pengisian nilai awal pada saat penciptaan berikut:
    CODE
    const int max_size = 5000;

    Untuk sebuah objek, pengisian tersebut harus dilakukan melalui constructor initialization list

  • Anggota fungsi yang memiliki atribut const berarti bahwa fungsi tersebut tidak akan mengubah objek yang memanggilnya.
    Object yang ditandai sebagai const tidak boleh memanggil fungsi anggota yang tidak memiliki atribut const karena hal tersebut dapat mengakibatkan perubahan status objek tersebut.

Dalam contoh Stack di atas, fungsi yang dapat mendapatkan atribut const adalah isEmpty(), sedangkan data yang dapat mendapatkan atribut const adalah size. Pada Contoh dibawah ditunjukkan bagaimana penulisan deklarasi fungsi dan data tersebut setelah mendapatkan atribut const.
Contoh Pemanfaatan const pada anggota fungsi

CODE
1 class Stack {
2 // …
3 public:
4 Stack ();
5 Stack (int s);
6 Stack (const Stack&);
7 int isEmpty() const; /* keyword `const’ dituliskan pada
8 * deklarasi maupun definisi
9 * member function */
10 private:
11 const int size;
12 };
13
14 int Stack::isEmpty () const { // <== PERHATIKAN “const”
15 //…
16 }
17
18 Stack::Stack () : size (defaultStacksize) {
19 }
20
21 Stack::Stack (int p) : size (p) {
22 }
23
24 Stack::Stack (const Stack& s) : size (s.size) {
25 }

Static Member
Setiap objek di dalam kelas memiliki sendiri member datanya. Dalam keadaan tertentu diperlukan anggota data yang digunakan bersama oleh seluruh objek dari satu kelas objek tersebut. Hal ini misalnya dapat digunakan untuk menghitung jumlah objek yang sudah diciptakan.

CODE
1 class Stack {
2 public:
3 // … fungsi lain
4
5 private:
6 static int n_stack; // static data member!!
7 // … data & fungsi lain
8 };

Inisialisasi anggota statik tidak dapat dilakukan di dalam constructor, melainkan di luar deklarasi kelas dan di luar fungsi anggota. Inisialisasi anggota data yang statik dilakukan di file implementasi (X.cc), jangan di dalam file header.

CODE
// inisialisasi anggota data yang statik
// di dalam file Stack.cc
int Stack::n_stack = 0;

Anggota fungsi yang hanya mengakses anggota (data maupun fungsi) statik dapat dideklarasikan sebagai static function

CODE
1 class Stack {
2 // …
3 public:
4 static int NumStackObj ();
5 };
6
7 int Stack::NumStackObj() {
8 // kode yang mengakses hanya data member statik
9 }

Untuk memahami anggota statik (fungsi maupun data) bandingkanlah dengan deklarasi variabel lokal statik berikut:

CODE
1 void SuatuFungsi ()
2 {
3 static int v = -1;
4
5 // … instruksi …
6 }

Jika atribut statik tidak digunakan, maka umur hidup dan keberadaan variabel v sepenuhnya bergantung pada umur hidup dan keberadaan fungsi SuatuFungsi. Dengan dituliskannya atribut static maka umur hidup dan keberadaan variabel v tidak lagi bergantung pada SuatuFungsi. Hanya visibility v yang ditentukan oleh SuatuFungsi. Demikian juga
dengan anggota yang dideklarasikan dengan atribut static. Umur hidup dan keberadaan mereka tidak ditentukan oleh kelas yang melingkupinya.
Sebagai akibatnya, pada anggota fungsi / data yang statik berlaku sifat-sifat berikut:

  • Anggota fungsi statik dapat dipanggil tanpa melalui objek dari kelas tersebut, misalnya:
    CODE
    if (Stack::NumStackObj() > 0) {
    printf (“…..”);
    }
  • Anggota fungsi statik tidak memiliki pointer implisit this
  • Data member yang statik diinisialisasi tanpa perlu adanya objek dari kelas tersebut

Friend
Dalam C++, sebuah kelas (A) atau fungsi (F) dapat menjadi friend dari kelas lain (B). Dalam keadaan biasa, kelas A maupun fungsi F tidak dapat mengakses anggota (data/fungsi) nonpublic milik B. Dengan adanya hubungan friend ini, A dan F dapat mengakses anggota nonpublic dari B. Deklarasi friend dituliskan dari pihak yang memberikan ijin. Pemberian ijin ini tidak bersifat dua arah, yang berarti dalam kode berikut, kelas B tidak memiliki hak untuk mengaskses anggota non-public dari kelas A. Dalam contoh ini, realisasinya adalah:

CODE
1 class B { // kelas “pemberi ijin”
2 friend class A;
3 friend void F (int, char *);
4
5 private:
6 // …
7 public:
8 //…
9 };

Fungsi yang dideklarasikan dengan atribut friend merupakan fungsi di luar kelas sehingga objek parameter aktual mungkin dilewatkan secara call-by-value. Akibatnya operasi yang dilakukan terhadap objek bukanlah objek semula, melainkan salinan dari objek tersebut. Fungsi anggota merupakan fungsi di dalam kelas dan operasi yang dilakukannya selalu berpengaruh pada objek sesungguhnya.
Kriteria penggunaan atribut friend:

  • Sedapat mungkin hindari penggunaan friend. Penggunaan friend di antara kelas menunjukkan perancangan kelas yang kurang baik. Jika kelas A menjadikan kelas B sebagai friend maka kemungkinan besar kelas A dan B seharusnya tidak dipisahkan
  • Jika operasi yang dijalankan oleh sebuah fungsi friend mengubah status dari objek, operasi tersebut harus diimplementasikan sebagai fungsi anggota
  • Gunakan friend untuk overloading pada operator tertentu

Nested Class
Dalam keadaan tertentu, perancang kelas membutuhkan pendeklarasian kelas di dalam deklarasi suatu kelas tertentu. Sebagai contoh pada deklarasi kelas List yang merupakan list dari integer, kita mungkin membutuhkan deklarasi kelas ListElem untuk menyatakan elemen list tersebut. Operasi-operasi terhadap list dide nisikan di dalam kelas List, namun
demikian ada kemungkinan operasi-operasi ini membutuhkan pengaksesan terhadap bagian non-publik dari kelas ListElem sehingga penggunaan friend dituliskan di dalam kelas ListElem seperti yang terlihat pada Contoh berikut:
Contoh Kelas List dan ListElem

CODE
1 class List;
2
3 class ListElem {
4 friend class List;
5 public:
6 //
7 private:
8 };
9
10 class List {
11 public:
12 //
13 private:
14 //
15 };

Sesungguhnya, pemakai kelas List tidak perlu mengetahui keberadaan kelas ListElem. Yang perlu ia ketahui adalah adanya layanan untuk menyimpan nilai (integer) ke dalam list tersebut maupun untuk mengambil nilai dari list tersebut.
Dalam keadaan di atas, kelas ListElem dapat dijadikan sebagai nested class di dalam kelas List dan deklarasinya dapat dituliskan seperti pada Contoh berikut:

CODE
1 class List {
2 //
3 //
4 class ListElem {
5 //
6 //
7 };
8 };

Namun demikian, ada pertanyaan yang mungkin muncul: “Dimanakah kelas ListElem dideklarasikan? Di bagian publik atau non-publik?”. Jika ditempatkan pada bagian public dari kelas List, maka bagian publik dari kelas ListElem akan tampak ke luar kelas List sebagai anggota yang juga publik. Sebaliknya, jika ditempatkan pada bagian non-publik, maka
bagian publik dari kelas ListElem akan tersembunyi terhadap pihak luar kelas List, namun akan tetap terlihat oleh anggota-anggota kelas List. Efek terakhir inilah yang diinginkan, sehingga dengan demikian deklarasi kelas ListElem ditempatkan di bagian non-publik seperti yang ditunjukkan pada Contoh dibawah. Dalam keadaan ini juga kemungkinan besar kelas ListElem tidak perlu memiliki bagian non-publik.
Contoh Deklarasi ListElem di dalam List

CODE
1 class List {
2 public:
3 // bagian public kelas List
4 // …
5 private:
6 class ListElem {
7 public:
8 // semua anggota ListElem berada pada bagian publik
9 };
10
11 // definisi anggota private kelas List
12 };

* Fungsi Anggota Nested Class
Dalam contoh List dan ListElem di atas, pende nisian kelas ListElem berada di dalam lingkup kelas List sehingga nama scope yang harus digunakan dalam mendefinisikan kelas ListElem adalah “List::ListElem” bukan hanya sekedar “ListElem::”. Jika kelas List merupakan kelas generik yang memiliki parameter generik Type, maka nama scope menjadi
“List<Type>::ListElem”.

Perhatikan Contoh berikut.

CODE
1 // File: List.h
2 template <class Type>
3 class List {
4 public:
5 //… bagian ini tidak dituliskan …
6 private:
7 class ListElem {
8 public: // seluruh anggota bersifat publik
9 ListElem (const Type&);
10 ListElem (const ListElem&);
11 ListElem& operator (const ListElem&);
12 Type info; // <– perhatikan parameter generik
13 ListElem* next;
14 };
15 };
16
17
18 template <class Type>
19 List<Type>::ListElem::ListElem (const Type& v) : info (v) {
20 next = 0;
21 }
22
23 template <class Type>
24 List<Type>::ListElem::ListElem (const ListElem& x) : info (x.info) {
25 next = x.next;
26 }
27
28 template <class Type>
29 List<Type>::ListElem& List<Type>::ListElem::operator= (const ListElem& x) {
30 info = x.info;
31 next = x.next;
32 return *this;
33 }

Penggunaan nama parameter generik Type di dalam nested class ListElem pada baris 12 mengakibatkan kelas ListElem menjadi kelas generik. Perhatikanlah pula penulisan nama scope pada baris 19, 24, dan 29, serta jenis kembalian fungsi “operator=” dari kelas ListElem.

End.

Tinggalkan Balasan

Isikan data di bawah atau klik salah satu ikon untuk log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s