Friday, December 23, 2016

Bab 2. C++ Untuk Programer



Bab. 2 Kelas Bagian 2

Tujuan Instruksional
·         Objek const dan fungsi anggota const.
·         Komposisi.
·         Fungsi friend dan kelas friend.
·         Menggunakan pointer this.
·         Anggota kelas static.
·         Kelas proxy





2.1 Introduksi

Pada bab ini, akan dilanjutkan diskusi tentang kelas dengan beberapa topik tambahan. Akan digunakan objek const dan fungsi anggota const untuk mencegah pemodifikasian objek dan menegakkan prinsip hak minimum. Akan didiskusikan pula komposisi, sebuah bentuk pendaur-ulangan kode dimana di dalamnya kelas dapat memiliki objek dari kelas lain sebagai anggota. Berikutnya, akan dikenalkan pertemanan, dimana pertemanan memampukan perancang kelas untuk menspesifikasi fungsi non-anggota yang dapat mengakses anggota kelas non-public, sebuah teknik yang seringkali digunakan dalam overloading operator. Akan didiskusikan juga pointer spesial (dinamakan this), yang merupakan sebuah argumen implisit kepada setiap fungsi anggota non-static suatu kelas. Hal ini mengijinkan fungsi anggota tersebut untuk mengakses anggota data dan fungsi anggota non-static suatu objek. Pada beberapa kasus, akan direkomendasikan penggunaan anggota kelas static dan menunjukkan bagaimana anggota data static dan fungsi anggota static di dalam kelas Anda. Terakhir, akan ditunjukkan bagaimana menciptakan sebuah kelas proxy untuk menyembunyikan detil implementasi kelas (termasuk data private-nya) dari klien.

2.2 Objek const dan Fungsi Anggota const
Sekarang akan dilihat bagaimana prinsip hak minimum diterapkan pada objek. Beberapa objek perlu dimodifikasi dan beberapa lainnya tidak perlu. Anda dapat menggunakan katakunci const untuk menspesifikasi bahwa sebuah objek tidak bisa dimodifikasi dan bahwa sembarang usaha untuk memodifikasi objek akan menghasilkan error kompilasi. Statemen

const Waktu siang( 12, 0, 0 );

mendeklarasikan sebuah objek const, siang, dari kelas Waktu dan menginisialisasinya dengan 12 siang.

C++ tidak mengijinkan pemanggilan fungsi anggota untuk objek const kecuali jika fungsi anggota tersebut dideklarasikan const. Hal ini berlaku pula untuk semua fungsi get yang tidak memodifikasi objek.

Suatu fungsi anggota dispesifikasi sebagai const di dalam prototipenya dengan menyisipkan katakunci const setelah daftar parameter fungsi dan di dalam definisi fungsi dengan menyisipkan katakunci const sebelum kurung kurawal kiri yang mengawali tubuh fungsi.

Mendefinisikan dan Menggunakan Fungsi Anggota const
Program pada Gambar 2.1 – 2.3 memodifikasi kelas Waktu pada Gambar 1.8 – 1.9 dengan menjadikan beberapa fungsi get dan fungsi tampilUniversal dideklarasikan const.  Di dalam header Waktu.h (Gambar 2.1), baris 19-21 dan 24 sekarang menyertakan katakunci const setelah daftar parameter prototipe fungsi. Definisi atas setiap fungsi terkait pada Gambar 2.2 (baris 53, 59, 65, dan 71) juga menspesifikasi katakunci const setelah daftar parameter setiap fungsi.

Gambar 2.1 Definisi Kelas Waktu dengan Fungsi Anggota const

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Gambar 2.1:  Waktu.h
// Definisi kelas Waktu dengan fungsi anggota const.
// Fungsi anggota didefinisikan di dalam Waktu.cpp
#ifndef WAKTU_H
#define WAKTU_H

class Waktu
{
public:
  Waktu( int = 0, int = 0, int = 0 ); // konstruktor default

  // fungsi set
  void setWaktu( int, int, int ); // menetapkan jam, menit, dan detik
  void setJam( int ); // menetapkan jam
  void setMenit( int ); // menetapkan menit
  void setDetik( int ); // menetapkan detik

  // fungsi get
  int getJam() const; // menghasilkan jam
  int getMenit() const; // menghasilkan menit
  int getDetik() const; // menghasilkan detik

  // fungsi untuk menampilkan (normalnya dideklarasikan const)
  void tampilUniversal() const; // menampilkan waktu dalam format universal
  void tampilStandard(); // menampilkan waktu dalam format standard
private:
  int jam; // 0 - 23 (format 24-jam)
  int menit; // 0 - 59
  int detik; // 0 - 59
}; // akhir dari kelas Waktu

#endif

Gambar 2.2 Definisi Fungsi Anggota Kelas Waktu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// Gambar. 2.2: Waktu.cpp
// Definisi fungsi anggota untuk kelas Waktu.
#include <iostream>
#include <iomanip>
#include <stdexcept> // untuk kelas eksepsi invalid_argument
#include "Waktu.h" // menyertakan definisi kelas Waktu dari Waktu.h
using namespace std;

// konstruktor Waktu menginisialisasi data private;
// memanggil fungsi anggota setWaktu untuk menetapkan variabel-variabel;
// nilai-nilai default adalah 0 (lihat definisi kelas).
Waktu::Waktu( int jam, int menit, int detik )
{
  setWaktu( jam, menit, detik );
} // akhir dari konstruktor Waktu

// menetapkan nilai Waktu baru menggunakan waktu universal
void Waktu::setWaktu( int jam, int menit, int detik )
{
  setJam( jam );
  setMenit( menit );
  setDetik( detik );
} // akhir dari fungsi setWaktu

// menetapkan nilai jam
void Waktu::setJam( int h )
{
  if ( h >= 0 && h < 24 )
    jam = h;
  else
    throw invalid_argument( "jam harus dalam rentang 0-23" );
} // akhir dari fungsi setJam

// menetapkan nilai menit
void Waktu::setMenit( int m )
{
  if ( m >= 0 && m < 60 )
    menit = m;
  else
    throw invalid_argument( "menit harus dalam rentang 0-59" );
} // akhir dari fungsi setMenit

// menetapkan nilai detik
void Waktu::setDetik( int s )
{
  if ( s >= 0 && s < 60 )
    detik = s;
  else
    throw invalid_argument( "detik harus dalam rentang 0-59" );
} // akhir dari fungsi setDetik

// menghasilkan nilai jam
int Waktu::getJam() const // fungsi get harus const
{
  return jam;
} // akhir dari fungsi getJam

// menghasilkan nilai menit
int Waktu::getMenit() const
{
  return menit;
} // akhir dari fungsi getMenit

// menghasilkan nilai detik
int Waktu::getDetik() const
{
  return detik;
} // akhir dari fungsi getDetik

// menampilkan Waktu dalam format universal (HH:MM:SS)
void Waktu::tampilUniversal() const
{
  cout << setfill( '0' ) << setw( 2 ) << jam << ":"
    << setw( 2 ) << menit << ":" << setw( 2 ) << detik;
} // akhir dari fungsi tampilUniversal

 // menampilkan Waktu dalam format standard (HH:MM:SS AM atau PM)
void Waktu::tampilStandard()
{
  cout << ( ( jam == 0 || jam == 12 ) ? 12 : jam % 12 ) << ":"
    << setfill( '0' ) << setw( 2 ) << menit << ":" << setw( 2 )
    << detik << ( jam < 12 ? " AM" : " PM" );
} // akhir dari fungsi tampilStandard

Gambar 2.3 menginstansiasi dua objek Waktu, yaitu objek non-const bangunTidur (baris 7) dan objek const sore (baris 8). Program mencoba memanggil dua fungsi anggota non-const setJam (baris 13) dan tampilStandard (baris 20) pada objek const sore. Pada setiap kasus itu, kompiler membangkitkan pesan error. Program juga mengilustrasikan tiga kombinasi lainnya tentang pemanggilan fungsi anggota pada objek, yaitu sebuah fungsi non-const pada sebuah objek non-const (baris 11), sebuah fungsi anggota const pada sebuah objek non-const (baris 15), dan sebuah fungsi anggota const pada sebuah objek const. Pesan error dibangkitkan untuk fungsi anggota non-const yang dipanggil pada sebuah objek const, seperti ditampilkan pada keluaran.

Gambar 2.3 Mencoba Mengakses Objek const dengan Fungsi Anggota non-const

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Gambar 2.3: gambar2_03.cpp
// Mencoba mengakses objek const dengan fungsi anggota non-const.
#include "Waktu.h" // include Time class definition

int main()
{
  Waktu bangunTidur( 6, 45, 0 ); // objek non-const
  const Waktu sore( 12, 0, 0 ); // objek const

  // FUNGSI ANGGOTA OBJEK
  bangunTidur.setJam( 18 ); // non-const non-const

  sore.setJam( 12 ); // const non-const

  bangunTidur.getJam(); // non-const const

  sore.getMenit(); // const const
  sore.tampilUniversal(); // const const

  sore.tampilStandard(); // const non-const
} // akhir dari main

Pesan error kompiler Microsoft Visual C++:

C:\C++\gambar2_03.cpp(13) : error C2662:
'Waktu::setWaktu' : cannot convert 'this' pointer from 'const Waktu' to 'Waktu &'
Conversion loses qualifiers

C:\C++\gambar2_03.cpp(20) : error C2662:
' Waktu::tampilStandard' : cannot convert 'this' pointer from 'const Waktu' to ' Waktu &'
Conversion loses qualifiers

Sebuah konstruktor harus berupa fungsi anggota non-cost (Gambar 2.2, baris 11-14), tetapi ia masih dapat digunakan untuk menginisialisasi objek const (Gambar 2.3, baris 8). Definisi konstruktor Waktu (Gambar 2.2, baris 11-14) menunjukkan bahwa ia memanggil fungsi anggota non-const, setWaktu (baris 17-22), untuk melakukan penginisialisasian sebuah objek Waktu. Pemanggilan suatu fungsi non-const dari pemanggilan konstruktor sebagai bagian dari inisialisasi sebuah objek const adalah tindakan yang diijinkan.

Baris 20 pada Gambar 2.3 membangkitkan error kompilasi meskipun fungsi anggota tampilStandard dari kelas Waktu tidak memodifikasi objek. Fakta bahwa suatu fungsi anggota tidak memodifikasi objek tidak cukup untuk mengindikasikan bahwa fungsi tersebut merupakan fungsi konstan. Oleh karena itu, fungsi tersebut harus dideklarasikan const.

Menginisialisasi Anggota Data const dengan Penginisialisasi Anggota
Program pada Gambar 2.4 – 2.6 mengintroduksi penggunaan sintaks penginisialisasi anggota. Semua anggota data dapat diinisialisasi menggunakan sintaks penginisialisasi anggota, tetapi anggota data const dan anggota data yang direferensi harus diinisialisasi menggunakan penginisialisasi anggota.

Gambar 2.4 Definisi Kelas Inkremen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Gambar 2.4: Inkremen.h
// Definisi kelas Inkremen.
#ifndef INKREMEN_H
#define INKREMEN_H

class Inkremen
{
public:
  Inkremen( int c = 0, int i = 1 ); // konstruktor default

  // definisi tambahInkremen
  void tambahInkremen()
  {
    hitung += inkremen;
  } // akhir dari fungsi tambahInkremen

  void tampil() const; // menampilkan hitung dan inkremen
private:
  int hitung;
  const int inkremen; // anggota data const
}; // akhir dari kelas Inkremen

#endif

Gambar 2.5 Definisi Fungsi Anggota Kelas Inkremen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Gambar 2.5: Inkremen.cpp
// Definisi fungsi anggota untuk kelas Inkremen menggunakan
// penginisialisasi anggota untuk menginisialisasi tipe data const.
#include <iostream>
#include "Inkremen.h" // menyertakan definisi kelas Inkremen
using namespace std;

// konstruktor
Inkremen::Inkremen( int c, int i )
: hitung( c ), // penginisialisasi untuk anggota non-const
inkremen( i ) // penginisialisasi untuk anggota const
{
  // tubuh kosong
} // akhir dari konstruktor Inkremen

// menampilkan hitung dan inkremen
void Inkremen::tampil() const
{
  cout << "hitung = " << hitung << ", inkremen = " << inkremen << endl;
} // akhir dari fungsi tampil

Gambar 2.6 Program untuk Menguji Kelas Inkremen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Gambar 2.6: gambar2_06.cpp
// Program untuk menguji kelas Inkremen.
#include <iostream>
#include "Inkremen.h" // menyertakan definisi kelas Inkremen
using namespace std;

int main()
{
  Inkremen nilai( 10, 5 );

  cout << "Sebelum penginkremenan: ";
  nilai.tampil();

  for ( int j = 1; j <= 3; ++j )
  {
    nilai.tambahInkremen();
    cout << "Setelah penginkremenan " << j << ": ";
    nilai.tampil();
  } // akhir dari for
} // akhir dari main

Sebelum penginkremenan: hitung = 10, inkremen = 5
Setelah penginkremenan 1: hitung = 15, inkremen = 5
Setelah penginkremenan 2: hitung = 20, inkremen = 5
Setelah penginkremenan 3: hitung = 25, inkremen = 5

Definisi konstruktor (Gambar 2.5, baris 9-14) menggunakan sebuah daftar penginisialisasi anggota untuk menginisialisasi anggota-anggota data kelas Inkremen, yaitu integer non-const (hitung) dan integer const (inkremen), yang didefinisikan pada baris 19-20 pada Gambar 2.4. Penginisialisasi anggota ditempatkan di antara daftar parameter konstruktor dan kurung kurawal kiri yang memulai tubuh konstruktor. Daftar penginisialisasi anggota (Gambar 2.5, baris 10-11) dipisahkan dari daftar parameter menggunakan titik-dua (:). Setiap penginisialisasi anggota memuat nama anggota data yang diikuti dengan sepasang kurung yang mengapit nilai awal anggota. Pada contoh ini, hitung diinisialisasi dengan nilai dari parameter konstruktor c dan inkremen diinisialisasi dengan nilai dari parameter konstruktor i. Jika terdapat lebih dari dua penginisialisasi anggota, maka harus dipisahkan dengan koma. Penginisialisasi anggota dieksekusi sebelum tubuh konstruktor dieksekusi.

Mengapa Fungsi tampil Dideklarasikan const?
Fungsi tampil (Gambar 2.5, baris 17-20) dideklarasikan const. Mungkin cukup aneh bagi Anda mengapa melabel fungsi ini const, karena program kemungkinan tidak akan memiliki sebuah objek const Inkremen. Tetapi, adalah hal yang mungkin bahwa program akan memiliki sebuah referensi const ke objek Inkremen atau sebuah pointer const yang menunjuk ke objek Inkremen. Umumnya, ini terjadi ketika objek kelas Inkremen dilewatkan kepada fungsi atau dijadikan nilai balik oleh fungsi. Pada kedua kasus tersebut, hanya fungsi anggota const dari kelas Inkremen yang dapat dipanggil melalui referensi atau pointer. Jadi, adalah masuk akal untuk mendeklarasikan tampil sebagai const. Dengan melakukannya, Anda dapat mencegah terjadinya error pada situasi dimana objek Inkremen diperlakukan sebagai objek const.

Kesalahan: Mencoba Menginisialisasi Anggota Data const dengan Penugasan
Gambar 2.7 menunjukkan error kompilasi yang diakibatkan oleh penginisialisasian anggota data const (inkremen) dengan statemen penugasan di dalam tubuh konstruktor Inkremen, bukan dengan penginisialisasi anggota.

Gambar 2.7 Error kompilasi yang diakibatkan oleh penginisialisasian anggota data const (inkremen) dengan statemen penugasan

C:\C++\Inkremen.cpp(10) : error C2758:
Inkremen:: Inkremen: must be initialized in constructor base/member
initializer list
C:\C++\ Inkremen.h(20) : see
declaration of Inkremen:: Inkremen'
C:\C++\ Inkremen.cpp(12) : error C2166:
l-value specifies const object

Inkremen.cpp:9: error: uninitialized member ‘Inkremen:: Inkremen' with
'const' type 'const int'
Inkremen.cpp:12: error: assignment of read-only data-member
' Inkremen:: Inkremen'

2.3 Komposisi: Objek sebagai Anggota Kelas
Sebuah objek JamWaker perlu mengetahui kapan ia harus membunyikan alarm, jadi mengapa tidak menyertakan sebuah objek Waktu sebagai anggota dari kelas JamWaker? Kapabilitas semacam itu disebut dengan komposisi dan kadangkala dikenal dengan relasi memiliki-suatu, yaitu sebuah kelas dapat memiliki objek dari kelas lain sebagai anggota.

Sebelumnya, Anda telah melihat bagaimana melewatkan argumen kepada konstruktor suatu objek yang diciptakan di dalam main. Sekarang akan ditunjukkan bagaimana konstruktor suatu objek dapat melewatkan argumen kepada konstruktor objek anggota melalui penginisialisasi anggota.

Program berikutnya menggunakan kelas Tanggal (Gambar 2.8 – 2.9) dan Karyawan (Gambar 2.10 – 2.11) untuk mendemonstrasikan komposisi. Definisi kelas Karyawan (Gambar 2.10) memuat empat anggota data private, yaitu namaPertama, namaAkhir, tanggalLahir, dan tanggalKerja. Anggota tanggalLahir dan tanggalKerja merupakan objek dari kelas Tanggal, yang memuat tiga anggota data private, yaitu bulan, hari, dan tahun. Header konstruktor Karyawan (Gambar 2.11, baris 10-11) menspesifikasi bahwa konstruktor memiliki empat parameter (pertama, akhir, tanggalLahir, dan tanggalKerja). Dua parameter pertama dilewatkan melalui penginisialisasi anggota kepada konstruktor kelas string. Dua parameter terakhir dilewatkan melalui penginisialisasi anggota kepada konstruktor kelas Tanggal.



Gambar 2.8 Definisi Kelas Tanggal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Gambar 2.8: Tanggal.h
// Definisi kelas Tanggal; fungsi anggota didefinisikan dalam Tanggal.cpp

#ifndef TANGGAL_H
#define TANGGAL_H

class Tanggal
{
public:
  Tanggal( int = 1, int = 1, int = 1900 ); // konstruktor default
  void tampil() const; // menampilkan tanggal dalam format bulan/hari/tahun
  ~Tanggal(); // disediakan untuk memastikan urutan penghancuran objek
private:
  int bulan; // 1-12 (Januari-Desembar)
  int hari; // 1-31 tergantung bulan
  int tahun; // sembarang tahun

  // fungsi utilitas untuk memeriksa apakah hari sesuaai dengan bulan dan tahun
  int periksaHari( int ) const;
}; // akhir dari kelas Tanggal

#endif

Gambar 2.9 Definisi Fungsi-Anggota Kelas Tanggal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Gambar 2.9: Tanggal.cpp
 // Definisi fungsi-anggota kelas Tanggal.
#include <iostream>
#include <stdexcept>
#include "Tanggal.h" // menyertakan definisi kelas Tanggal
using namespace std;

// konstruktor memastikan nilai yang cocok untuk bulan; memanggil
// fungsi utilitas periksaHari untuk memastikan nilai yang cocok untuk hari
Tanggal::Tanggal( int mn, int dy, int yr )
{
  if ( mn > 0 && mn <= 12 ) // memvalidasi bulan
    bulan = mn;
  else
    throw invalid_argument( "bulan harus 1-12" );

  tahun = yr; // dapat memvalidasi yr
  hari = periksaHari( dy ); // memvalidasi hari

  // menampilkan objek Tanggal untuk menampilkan kapan konstruktornya dipanggil
  cout << "Konstruktor objek Tanggal untuk tanggal ";
  tampil();
  cout << endl;
} // akhir dari konstruktor Tanggal

// menampilkan objek Tanggal dalam format bulan/hari/tahun
void Tanggal::tampil() const
{
  cout << bulan << '/' << hari << '/' << tahun;
} // akhir dari fungsi tampil

// menampilkan objek Tanggal kapan destruktornya dipanggil
Tanggal::~Tanggal()
{
  cout << "Destruktor objek Tanggal untuk tanggal ";
  tampil();
  cout << endl;
} // akhir dari konstruktor ~Tanggal

// fungsi utilitas untuk memastikan nilai hari valid berdasarkan
// bulan dan tahun; menangani tahun leap juga
int Tanggal::periksaHari( int ujiHari ) const
{
  static const int hariPerBulan[ 12 + 1 ] =
    { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

  // menentukan apakah ujiHari valid untuk bulan tertentu
  if ( ujiHari > 0 && ujiHari <= hariPerBulan[ bulan ] )
    return ujiHari;

  // 29 Pebruari untuk tahun leap
  if ( bulan == 2 && ujiHari == 29 && ( tahun % 400 == 0 ||
     ( tahun % 4 == 0 && tahun % 100 != 0 ) ) )
    return ujiHari;

  throw invalid_argument( "Hari tak-valid untuk bulan dan tahun" );
} // akhir dari fungsi periksaHari

Gambar 2.10 Definisi Kelas Karyawan Menunjukkan Komposisi

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
// Gambar 2.10: Karyawan.h
// Definisi kelas Karyawan menunjukkan komposisi.
// Fungsi anggota didefinisikan di dalam Karyawan.cpp.
#ifndef KARYAWAN_H
#define KARYAWAN_H

#include <string>
#include "Tanggal.h" // menyertakan definisi kelas Tanggal
using namespace std;

class Karyawan
{
public:
  Karyawan( const string &, const string &,
  const Tanggal &, const Tanggal & );
  void tampil() const;
  ~Karyawan(); // untuk memastikan urutan penghancuran
private:
  string namaPertama; // komposisi: objek anggota
  string namaAkhir; // komposisi: objek anggota
  const Tanggal tanggalLahir; // komposisi: objek anggota
  const Tanggal tanggalKerja; // komposisi: objek anggota
}; // akhir dari kelas Karyawan

#endif

Gambar 2.11 Definisi Fungsi-Anggota Kelas Tanggal

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 2.11: Karyawan.cpp
// Definisi fungsi anggota kelas.
#include <iostream>
#include "Karyawan.h" // Definisi kelas Karyawan
#include "Tanggal.h" // Definisi kelas Tanggal
using namespace std;

// konstruktor menggunakan penginisialisasi anggota untuk melewatkan nilai-nilai
// penginisialisasi kepada konstruktor objek anggota
Karyawan::Karyawan( const string &pertama, const string &akhir,
   const Tanggal &tanggalHariLahir, const Tanggal &tanggalHariKerja )
   : namaPertama( pertama ), // menginisialisasi namaPertama
     namaAkhir( akhir ), // menginisialisasi namaAkhir
     tanggalLahir( tanggalHariLahir ), // menginisialisasi tanggalLahir
     tanggalKerja( tanggalHariKerja ) // menginisialisasi tanggalKerja
{
  // menampilkan objek Karyawan untuk menunjukkan kapan konstruktor dipanggil
  cout << "Konstruktor objek Tanggal: "
    << namaPertama << ' ' << namaAkhir << endl;
} // akhir destruktor Karyawan

// objek Karyawan tampil
void Karyawan::tampil() const
{
  cout << namaAkhir << ", " << namaPertama << " Dipekerjakan: ";
  tanggalLahir.tampil();
  cout << " Tanggal Lahir: ";
  tanggalLahir.tampil();
  cout << endl;
} // akhir dari fungsi tampil

 // menampilkan objek Karyawan untuk menampilkan kapan destruktornya dipanggil
Karyawan::~Karyawan()
{
  cout << "Destruktor objek Karyawan: "
    << namaAkhir << ", " << namaPertama << endl;
} // akhir dari destruktor ~Karyawan

Daftar Penginisialisasi Anggota Konstruktor Karyawan
Titik-dua (:) yang diikuti header konstruktor (Gambar 2.11, baris 12) memulai daftar penginisialisasi anggota. Penginisialisasi anggota menspesifikasi parameter konstruktor Karyawan dilewatkan kepada kepada konstruktor dari kelas string dan anggota-anggota data kelas Tanggal. Parameter pertama, akhir, tanggalHariLahir, dan tanggalHariKerja dilewatkan kepada konstruktor dari objek namaPertama (Gambar 2.11, baris 12), konstruktor dari objek namaAkhir (Gambar 2.11, baris 13), konstruktor dari tanggalHariLahir (Gambar 2.11, baris 14), dan konstruktor dari objek tanggalHariKerja (baris 15). 

Konstruktor Penyalin Default Kelas Tanggal
Seperti Anda perhatikan pada kelas Tanggal (Gambar 2.8), perhatikan bahwa kelas tersebut tidak menyediakan konstruktor yang menerima parameter bertipe Tanggal. Jadi, mengapa daftar penginisialisasi anggota dari kelas Karyawan dapat menginisialisasi objek tanggalLahir dan tanggalKerja dengan melewatkan objek-objek Tanggal kepada konstruktor-konstruktor Tanggal? Seperti yang Anda ketahui, kompiler menyediakan konstruktor penyalin di dalam setiap kelas untuk menyalin setiap anggota data dari objek argumen konstruktor ke dalam objek anggota yang diinisialisasi.

Menguji Kelas Tanggal dan Karyawan
Gambar 2.12 menciptakan dua objek Tanggal (baris 9-10) dan melewatkan keduanya sebagai argumen kepada konstruktor objek Karyawan yang diciptakan pada baris 11. Baris 14 menampilkan data objek Karyawan. Ketika setiap objek Tanggal diciptakan pada baris 9-10, konstruktor yang didefinisikan pada baris 9-26 pada Gambar 2.9 menampilkan sebaris keluaran untuk menunjukkan bahwa konstruktor telah dipanggil. Ketika objek anggota Tanggal di dalam setiap objek Karyawan diinisialisasi di dalam daftar penginisialisasi anggota dari konstruktor Karyawan (Gambar 2.11, baris 14-15), konstruktor penyalin default dipanggil. Karena konstruktor ini didefinisikan secara implisit oleh kompiler, maka ia tidak memuat sembarang statemen keluaran untuk mendemonstrasikan kapan ia dipanggil.

Gambar 2.12 Mendemonstrasikan Komposisi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Gambar 2.12: gambar2_12.cpp
// Mendemonstrasikan komposisi---objek dengan objek anggota.
#include <iostream>
#include "Karyawan.h" // Employee class definition
using namespace std;

int main()
{
  Tanggal lahir( 7, 24, 1949 );
  Tanggal kerja( 3, 12, 1988 );
  Karyawan manajer( "Rismon", "Sianipar", lahir, kerja );

  cout << endl;
  manajer.tampil();
} // end main

Konstruktor objek Tanggal untuk tanggal 7/24/1949
Konstruktor objek Tanggal untuk tanggal 3/12/1988
Konstruktor objek Tanggal: Rismon Sianipar

Sianipar, Rismon Dipekerjakan: 7/24/1949 Tanggal
Destruktor objek Karyawan: Sianipar, Rismon
Destruktor objek Tanggal untuk tanggal 3/12/1988
Destruktor objek Tanggal untuk tanggal 7/24/1949
Destruktor objek Tanggal untuk tanggal 3/12/1988
Destruktor objek Tanggal untuk tanggal 7/24/1949

Kelas Tanggal dan kelas Karyawan masing-masing menyertakan suatu konstruktor (baris 33-38 pada Gambar 2.9 dan baris 33-37 pada Gambar 2.11) yang menampilkan pesan error ketika sebuah objek kelasnya didestruksi. Hal ini memampukan Anda untuk memastikan di dalam keluaran program bahwa objek telah dikonstruksi dari dalam ke luar dan didestruksi dengan urutan terbalik (dari luar ke dalam), yaitu objek-objek anggota Tanggal dihancurkan setelah objek Karyawan yang memuatnya dihancurkan. Perhatikan empat baris terakhir pada keluaran Gambar 2.12. Dua baris terakhir merupakan keluaran dari destruktor Tanggal yang bekerja pada objek kerja (baris 10) dan lahir (baris 9). Keluaran ini memastikan bahwa tiga objek yang diciptakan di dalam main dihancurkan dengan urutan terbalik dari ketika diciptakan. Keluaran destruktor Karyawan ditampilkan pada lima baris dari bawah. Baris keempat dan ketiga dari bawah menunjukkan destruktor yang bekerja untuk objek tanggalKerja (baris 5.10, baris 22) dan tanggalLahir (Gambar 2.10, baris 21). Keluaran ini membuktikan bahwa objek Tanggal dihancurkan dari luar ke dalam. Konstruktor kelas string tidak memuat sembarang statemen keluaran, jadi Anda tidak bisa melihat kapan objek namaPertama dan namaAkhir dihancurkan.

Apa yang Terjadi Ketika Anda tidak Menggunakan Daftar Penginisialisasi Anggota?
Jika suatu objek anggota tidak diinisialisasi melalui penginisialisasi anggota, maka konstruktor default dari objek anggota akan dipanggil secara implisit. Nilai-nilai, jika ada, yang ditetapkan oleh konstruktor default akan dioverride oleh fungsi set. Akan tetapi, untuk inisialisasi kompleks, pendekatan ini memerlukan pekerjaan tambahan yang signifikan.

2.4 Fungsi friend dan Kelas friend
Fungsi friend suatu kelas didefinisikan di luar skop kelas tersebut, tetapi memiliki hak untuk mengakses anggota non-public dan public kelas. Fungsi, kelas atau fungsi anggota dari kelas lain dapat dideklarasikan menjadi friend bagi kelas lain. Penggunaan fungsi friend dapat meningkatkan kinerja program. Bagian ini akan menyajikan contoh tentang bagaimana suatu fungsi friend bekerja. Untuk mendeklarasikan suatu fungsi sebagai friend bagi sebuah kelas, Anda harus membubuhkan katakunci friend di depan prototipe fungsi di dalam definisi kelas.

Untuk mendeklarasikan semua fungsi anggota kelas dari kelas KelasDua sebagai friend bagi KelasSatu, anda bisa memakai format deklarasi

friend class KelasDua;

di dalam definisi kelas KelasSatu. Agar kelas B menjadi friend bagi kelas A, maka kelas A harus secara eksplisit mendeklarasikan bahwa kelas B adalah temannya. Juga, relasi pertemanan tidak bersifat simetrik atau transitif; yaitu, jika kelas A adalah friend bagi kelas B, dan kelas B adalah friend bagi kelas C, maka Anda tidak bisa menyimpulkan bahwa kelas B adalah friend bagi kelas A (karena pertemanan tidak simetrik), bahwa Anda tidak bisa menyimpulkan kelas C adalah friend bagi kelas B (karena pertemenan tidak simetrik), dan bahwa Anda tidak bisa menyimpulkan bahwa kelas A adalah friend bagi kelas C (pertemanan tidak transitif).

Memodifikasi Data Private Suatu Kelas Menggunakan Fungsi friend
Gambar 2.13 merupakan suatu contoh dimana di dalamnya Anda mendefinisikan fungsi friend (setX) untuk menetapkan anggota data private (x) pada kelas Hitung. Deklarasi friend (baris 9) ditempatkan di awal definisi kelas (secara konvensi), ditempatkan sebelum fungsi anggota public dideklarasikan. Akan tetapi, sebenarnya deklarasi friend ini dapat ditempatkan dimana saja di dalam kelas.

Gambar 2.13 friend Dapat Mengakses Anggota private

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
// Gambar 2.13: gambar2_13.cpp
// friend dapat mengakses anggota private suatu kelas.
#include <iostream>
using namespace std;

// Definisi kelas Hitung
class Hitung
{
  friend void setX( Hitung &, int ); // deklarasi friend
public:
  // konstruktor
  Hitung()
    : x( 0 ) // menginisialisasi x dengan 0
  {
    // tubuh kosong
  } // akhir dari konstruktor Hitung

  // menampilkan x
  void tampil() const
  {
    cout << x << endl;
  } // akhir dari fungsi tampil
private:
  int x; // anggota data
}; // akhir dari kelas Hitung

// fungsi setX dapat memodifikasi data private kelas Hitung
// karena setX dideklarasikan sebagai friend bagi Hitung (baris 9)
void setX( Hitung &c, int val )
{
  c.x = val; // diijinkan karena setX adalah friend bagi Hitung
} // akhir dari fungsi setX

int main()
{
  Hitung penghitung; // menciptakan objek Hitung

  cout << "penghitung.x setelah instansiasi: ";
  penghitung.tampil();

  setX( penghitung, 8 ); // menetapkan x menggunakan fungsi friend
  cout << "penghitung.x setelah pemanggilan fungsi friend setX: ";
  penghitung.tampil();
} // akhir dari main

penghitung.x setelah instansiasi: 0
penghitung.x setelah pemanggilan fungsi friend setX: 8

Fungsi setX (baris 29-32) merupakan C-style, fungsi yang berdiri-sendiri, karena bukan merupakan fungsi anggota dari kelas Hitung. Karena alasan ini, ketika setX dipanggil untuk objek penghitung, baris 41 melewatkan penghitung sebagai argumen kepada setX, bukan menggunakan nama objek untuk memanggil fungsi, seperti

penghitung.setX( 8 );

Jika Anda menghapus deklarasi friend pada baris 9, Anda akan menerima pesan error yang mengindikasikan bahwa fungsi setX tidak dapat memodifikasi anggota data private kelas Hitung (x).

2.5 Menggunakan Pointer this
Anda telah melihat bagaimana fungsi anggota objek dapat memanipulasi data objek. Bagaimana fungsi anggota mengetahui anggota data objek mana yang akan dimanipulasi? Setiap objek memiliki akses terhadap alamatnya sendiri melalui sebuah pointer yang dinamakan dengan this (salah satu katakunci C++). Pointer this bukan bagian dari objek. Memori yang diokupasi oleh pointer this tidak direfleksikan dalam hasi penerapan operasi sizeof pada sebuah objek. Pointer this dilewatkan (oleh kompiler) sebagai argumen implisit kepada fungsi anggota non-static dari setiap objek.
Objek menggunakan pointer this secara implisit atau eksplisit untuk mereferensi anggota data dan fungsi anggotanya. Tipe pointer this bergantung pada tipe objek dan apakah fungsi anggota, yang di dalamnya pointer this digunakan, dideklarasikan const atau tidak. Sebagai contoh, di dalam suatu fungsi anggota non-const dari kelas Karyawan, pointer this memiliki tipe Karyawan * const (sebuah pointer const yang menunjuk ke objek Karyawan non-const). Di dalam suatu fungsi anggota const dari kelas Karyawan, pointer this memiliki tipe const Karyawan * const (sebuah pointer const yang menunjuk ke objek Karyawan konstan). Pada contoh berikutnya akan ditampilkan penggunaan implisit dan eksplisit dari pointer this.

Penggunaan Implisit dan Eksplisit dari Pointer this untuk Mengakses Anggota Data Suatu Objek
Gambar 2.14 mendemonstrasikan penggunaan implisit dan eksplisit dari pointer this untuk memampukan suatu fungsi anggota dari kelas Test untuk menampilkan data private (x) dari suatu objek Test. Untuk ilustrasi, fungsi anggota tampil (baris 24-36) pertama-tama menampilkan x dengan menggunakan pointer this secara implisit (baris 27), dimana hanya nama anggota data yang dispesifikasi. Kemudian tampil menggunakan dua notasi yang berbeda untuk mengakses x melalui pointer this. Salah notasi menggunakan operator panah (->) (baris 31) dan notasi lainnya menggunakan operator dot (.) pada baris 35. Sepasang kurung diperlukan karena operator dot memiliki derajat keutamaan yang lebih tinggi dari operator *. Tanpa kurung, ekspresi *this.x akan dievaluasi sebagai *(this.x), yang akan menghasilkan error kopilasi, karena operator dot tidak dapat digunakan dengan pointer.

Menggunakan Pointer this untuk Membuang Pemanggilan Fungsi secara Bertingkat
Kegunaan lain dari pointer this adalah memampukan pemanggila fungsi anggota secara bertingkat, yaitu pemanggil beberapa fungsi di dalam statemen yang sama (seperti  dalam baris 12 pada Gambar 2.17). Program pada Gambar 2.15 – 2.17 memodifikasi beberapa fungsi set: setWaktu, setJam, setMenit, dan setDetik sehingga masing-masing mengembalikan sebuah referensi ke objek Waktu untuk memampukan pemanggilan fungsi anggota secara bertingkat. Perhatikan pada Gambar 2.16 bahwa statemen terakhir dalam tubuh setiap fungsi anggota menghasilkan nilai balik berupa *this (baris 22, 33, 44, dan 55), yang merupakan nilai balik bertipe Waktu &.

Gambar 2.15 Definisi Kelas Waktu

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
// Gambar 2.15:  Waktu.h
// Pemanggilan fungsi anggota secara bertingkat.

// Definisi kelas Waktu
// Fungsi anggota didefinisikan di dalam Waktu.cpp
#ifndef WAKTU_H
#define WAKTU_H

class Waktu
{
public:
  Waktu( int = 0, int = 0, int = 0 ); // konstruktor default

  // fungsi set
  Waktu &setWaktu( int, int, int ); // menetapkan jam, menit, dan detik
  Waktu &setJam( int ); // menetapkan jam (setelah validasi)
  Waktu &setMenit( int ); // menetapkan menit (setelah validasi)
  Waktu &setDetik( int ); // menetapkan detik (setelah validasi)

  // fungsi get
  int getJam() const; // menghasilkan jam
  int getMenit() const; // menghasilkan menit
  int getDetik() const; // menghasilkan detik

  void tampilUniversal() const; // menampilkan waktu dalam format universal
  void tampilStandard() const; // menampilkan waktu dalam format standard
private:
  int jam; // 0 - 23 (format 24-jam)
  int menit; // 0 - 59
  int detik; // 0 - 59
}; // akhir dari kelas Waktu

#endif

Gambar 2.16 Definisi Fungsi Anggota untuk Kelas Waktu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// Gambar. 2.16: Waktu.cpp
// Definisi fungsi anggota untuk kelas Waktu.
#include <iostream>
#include <iomanip>
#include "Waktu.h" // menyertakan definisi kelas Waktu dari Waktu.h
using namespace std;

// fungsi konstruktor untuk menginisialisasi setiap anggota data private;
// memanggil fungsi setWaktu untuk menetapkan variabel;
// nilai-nilai default adalah 0
Waktu::Waktu( int jam, int menit, int detik )
{
  setWaktu( jam, menit, detik ); // memvalidasi dan menetapkan waktu
} // akhir dari konstruktor Waktu

// menetapkan nilai Waktu baru menggunakan waktu universal
Waktu &Waktu::setWaktu( int h, int m, int s )
{
  setJam( h ); // menetapkan bidang private jam
  setMenit( m ); // menetapkan bidang private menit
  setDetik( s ); // menetapkan bidang private detik
  return *this;
} // akhir dari fungsi setWaktu

// menetapkan nilai jam
Waktu &Waktu::setJam( int h )
{
  if ( h >= 0 && h < 24 )
    jam = h;
  else
    throw invalid_argument( "jam harus dalam rentang 0-23" );

  return *this;
} // akhir dari fungsi setJam

// menetapkan nilai menit
Waktu &Waktu::setMenit( int m )
{
  if ( m >= 0 && m < 60 )
    menit = m;
  else
    throw invalid_argument( "menit harus dalam rentang 0-59" );

  return *this;
} // akhir dari fungsi setMenit

// menetapkan nilai detik
Waktu &Waktu::setDetik( int s )
{
  if ( s >= 0 && s < 60 )
    detik = s;
  else
    throw invalid_argument( "detik harus dalam rentang 0-59" );

  return *this;
} // akhir dari fungsi setDetik

// menghasilkan nilai jam
int Waktu::getJam() const
{
  return jam;
} // akhir dari fungsi getJam

// menghasilkan nilai menit
int Waktu::getMenit() const
{
  return menit;
} // akhir dari fungsi getMenit

// menghasilkan nilai detik
int Waktu::getDetik() const
{
  return detik;
} // akhir dari fungsi getDetik

// menampilkan Waktu dalam format universal (HH:MM:SS)
void Waktu::tampilUniversal() const
{
  cout << setfill( '0' ) << setw( 2 ) << jam << ":"
    << setw( 2 ) << menit << ":" << setw( 2 ) << detik;
} // akhir dari fungsi tampilUniversal

 // menampilkan Waktu dalam format standard (HH:MM:SS AM atau PM)
void Waktu::tampilStandard() const
{
  cout << ( ( jam == 0 || jam == 12 ) ? 12 : jam % 12 ) << ":"
    << setfill( '0' ) << setw( 2 ) << menit << ":" << setw( 2 )
    << detik << ( jam < 12 ? " AM" : " PM" );
} // akhir dari fungsi tampilStandard

Program pada Gambar 2.17 menciptakan objek Waktu, t, pada baris 9, kemudian menggunakannya di dalam pemanggilan fungsi anggota secara bertingkat. Mengapa teknik pengembalian *this (sebagai referensi) dapat dilakukan? Operator dot (.) memiliki asosiatifitas dari kiri ke kanan, jadi baris 12 mengevaluasi t.setJam(18), kemudian memberikan nilai balik berupa sebuah referensi ke objek t sebagai nilai dari pemanggilan fungsi ini. Ekspresi yang tersisa kemudian diinterpretasikan sebagai

t.setMenit( 30 ).setDetik( 22 );

Gambar 2.17 Pemanggilan Fungsi Anggota Secara Bertingkat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Gambar 2.17: gambar2_17.cpp
// Pemanggilan fungsi anggota secara bertingkat dengan pointer this.
#include <iostream>
#include "Waktu.h" // Definisi kelas Waktu
using namespace std;

int main()
{
  Waktu t; // menciptakan objek Waktu

  // pemanggilan fungsi secara bertingkat
  t.setJam( 18 ).setMenit( 30 ).setDetik( 22 );

  // menampilkan waktu dalam format universal dan format standaard
  cout << "Waktu universal: ";
  t.tampilUniversal();

  cout << "\nWaktu Standard: ";
  t.tampilStandard();

  cout << "\n\nWaktu standard yang baru: ";

  // pemanggilan fungsi secara bertingkat
  t.setWaktu( 20, 20, 20 ).tampilStandard();
  cout << endl;
} // akhir dari main

Waktu universal: 18:30:22
Waktu Standard: 6:30:22 PM

Waktu standard yang baru: 8:20:20 PM

Pemanggilan t.setMenit(30) dieksekusi dan memberikan nilai balik berupa sebuah referensi ke objek t. Ekspresi yang tersisa kemudian diinterpretasikan sebagai

setDetik( 22 );

Baris 24 juga menggunakan pemanggilan bertingkat. Pemanggilan harus ditempatkan secara berurutan seperti ditunjukkan pada baris 24, karena tampilStandard seperti didefinisikan di dalam kelas tidak mengembalikan sebuah referensi ke objek t. Penempatan pemanggilan terhadap tampilStandard sebelum pemanggilan terhadap setWaktu pada baris 24 akan menyebabkan error kompilasi.

2.6 Anggota Kelas static
Ada pengecualian terhadap aturan bahwa setiap objek suatu kelas memiliki salinan sendiri dari semua anggota data kelas tersebut. Pada beberapa kasus, hanya satu salinan dari suatu variabel yang harus dipakai secara bersama-sama oleh semua objek suatu kelas. Anggota data static digunakan karena alasan ini. Variabel semacam itu merupakan informasi skop kelas (yaitu, properti yang dipakai bersama oleh semua instans dan tidak spesifik pada salah satu objek kelas).

Skop dan Inisialisasi Anggota Data static
Meskipun kelihatannya seperti variabel global, anggota data static suatu kelas memiliki skop kelas. Juga, anggota static dapat dideklarasikan public, private, atau protected. Anggota data static bertipe fundamental diinisialisasi secara default dengan 0. Jika Anda menginginkan nilai awal yang berbeda, maka hanya diijinkan untuk menginisialisasi anggota data static sekali saja. Anggota data static const bertipa int atau enum dapat diinisialisasi di dalam deklarasinya pada definisi kelas. Namun, semua anggota data static bertipe lain harus didefinisikan dalam skop namespace global (di luar tubuh definisi kelas). dan dapat diinisialisasi hanya di dalam definisi tersebut. Jika anggota data static berupa objek suatu kelas yang menyediakan konstruktor default, maka anggota data static tidak perlu diinisialisasi karena konstruktor defaultnya akan dipanggil.

Definisi Kelas Waktu
Anggota private static dan protected static suatu kelas normalnya diakses melalui fungsi anggota public atau fungsi friend. Anggota static suatu kelas eksis meskipun tidak ada objek dari kelas tersebut yang diciptakan. Untuk mengakses anggota public static ketika tidak ada objek kelas yang diciptakan, Anda hanya perlu membubuhi prefiks nama kelas dan operator resolusi skop (::) di depan nama anggota data. Sebagai contoh, jika variabel univMataram adalah public, maka ia dapat diakses dengan ekspresi NamaKelas::univMataram ketika tidak ada objek NamaKelas yang diciptakan.

Untuk mengakses anggota private static atau private protected ketika tidak ada objek kelas yang diciptakan, Anda perlu menyediakan fungsi anggota public static dan memanggil fungsi tersebut dengan membubuhi prefiks pada nama fungsi tersebut dengan nama kelas dan operator resolusi skop. Fungsi anggota static merupakan layanan kelas, tidak spesifik untuk objek kelas tertentu.

Mendemonstrasikan Anggota Data static
Program pada Gambar 2.18 – 2.20 mendemonstrasikan anggota data private static yang dinamakan hitung (Gambar 2.18, baris 25) dan sebuah fungsi anggota public static yang dinamakan getHitung (Gambar 2.18, baris 19). Pada Gambar 2.19, baris 8 mendefinisikan dan menginisialisasi anggota data hitung dengan nol pada skop namespace global dan baris 12-15 mendefinisikan fungsi anggota static (getHitung). Perhatikan bahwa baik baris 8 maupun baris 12 tidak mencantumkan katakunci static, tetap kedua baris tersebut merujuk kepada anggota kelas static. Ketika static dipakai pada sebuah item pada skop namespace global, item tersebut menjadi hanya dikenal di dalam file tersebut saja. Anggota static harus tersedia kepada sembarang kode klien yang menggunakan kelas, jadi hanya dideklarasikan static di dalam file .h saja. Anggota data hitung menghitung jumlah objek kelas Karyawan yang telah diinstansiasi. Ketika objek kelas Karyawan eksis, jumlah hitung dapat direferensi melalui sembarang anggota objek Karyawan. Pada Gambar 2.19, hitung direferensi oleh baris 22 di dalam konstruktor dan baris 32 di dalam destruktor.

Gambar 2.18 Definisi Kelas Karyawan dengan Anggota Data static

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Gambar 2.18: Karyawan.h
// Definisi kelas Karyawan dengan anggota data static untuk
// menjejak objek Karyawan di dalam memori.
#ifndef KARYAWAN_H
#define KARYAWAN_H

#include <string>
using namespace std;

class Karyawan
{
public:
  Karyawan( const string &, const string &); // konstruktor
  ~Karyawan(); // destruktor
  string getNamaPertama() const; // mengembalikan nama pertama
  string getNamaAkhir() const; // mengembalikan nama akhir

  // fungsi anggot static
  static int getHitung(); // mengembalikan jumlah objek yang diinstansiasi
private:
  string namaPertama;
  string namaAkhir;

  // data static
  static int hitung; // jumlah objek yang diinstansiasi
}; // akhir dari kelas Karyawan

#endif

Gambar 2.19 Definisi Fungsi Anggota Kelas Karyawan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Gambar 2.19: Karyawan.cpp
// Definisi fungsi anggota kelas Karyawan.
#include <iostream>
#include "Karyawan.h" // Definisi kelas Karyawan
using namespace std;

// mendefinisikan dan menginisialisasi anggota data static pada skop global
int Karyawan::hitung = 0; // tidak mencantumkan katakunci static

// mendefinisikan fungsi anggota static yang mengembalikan jumlah objek
// Karyawan yang diinstansiasi (dideklarasikan static di dalam Karyawan.h)
int Karyawan::getHitung()
{
  return hitung;
} // akhir dari fungsi static getHitung

// konstruktor menginisialisasi anggota data non-static dan
// menginkremen anggota data static (hitung)
Karyawan::Karyawan( const string &pertama, const string &akhir )
  : namaPertama( pertama ), namaAkhir( akhir )
{
  ++hitung; // menginkremen jumlah objek Karyawan
  cout << "Konstruktor Karyawan untuk " << namaPertama
    << ' ' << namaAkhir << " dipanggil." << endl;
} // akhir dari konstruktor Karyawan

// destruktor mendealokasi memori yang dialokasikan secara dinamis
Karyawan::~Karyawan()
{
  cout << "~Karyawan() dipanggil untuk " << namaPertama
    << ' ' << namaAkhir << endl;
  --hitung; // mendekremen jumlah  objek Karyawan
} // akhir dari konstrukto ~Karyawan

// menghasilkan nama pertama dari karyawan
string Karyawan::getNamaPertama() const
{
  return namaPertama; // menghasilkan salinan dari nama pertama
} // akhir dari fungsi getNamaPertama

// menghasilkan nama akhir dari karyawan
string Karyawan::getNamaAkhir() const
{
  return namaAkhir; // menghasilkan salinan dari nama akhir
} // akhir dari fungsi getNamaAkhir

Gambar 9.20 menggunakan fungsi anggota static, getHitung, untuk menentukan jumlah objek Karyawan di dalam memori pada berbagai titik di dalam program. Program memanggil Karyawan::getHitung sebelum sembarang objek Karyawan diciptakan pada baris 12, setelah dua objek Karyawan diciptakan (baris 23) dan setelah semua objek Karyawan dihancurkan (baris 34). Baris 16-29 di dalam main mendefinisikan skop bersarang. Ingat bahwa variabel lokal eksis sampai skop dimana ia didefinisikan berhenti. Pada contoh ini, Anda menciptakan dua objek Karyawan pada baris 17-18 di dalam skop bersarang. Pada saat tiap konstruktor dieksekusi, ia menginkremen anggota data static (hitung). Objek-objek Karyawan tersebut dihancurkan ketika program mencapai baris 29. Pada titik itu, destruktor setiap objek dieksekusi dan  anggota data static (hitung) didekremen.

Gambar 2.20 Anggota Data static untuk Menjejak Jumlah Objek Kelas

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 5.20: gambar2_20.cpp
// Anggota data static untuk menjejak jumlah objek suatu kelas.
#include <iostream>
#include "Karyawan.h" // Definisi kelas Karyawan
using namespace std;

int main()
{
  // tidak ada objek yang eksis; gunakan nama kelas dan operator resolusi skop
  // untuk mengakses fungsi anggota static getHitung
  cout << "Jumlah karyawan sebelum instansiasi sembarang objek adalah "
    << Karyawan::getHitung() << endl; // menggunakan nama kelas

  // skop berikut menciptakan dan menghancurkan
  // objek-objek Karyawan sebelum main berhenti
  {
    Karyawan e1( "Robert", "Tohonan" );
    Karyawan e2( "Sintong", "Siahaan" );

    // dua objek eksis; panggil fungsi anggota static getHitung kembali
    // menggunakan nama kelas dan operator resolusi skop
    cout << "Jumlah karyawan setelah dua objek diinstansiasi adalah "
      << Karyawan::getHitung();

    cout << "\n\nKaryawan 1: "
      << e1.getNamaPertama() << " " << e1.getNamaAkhir()
      << "\nKaryawan 2: "
      << e2.getNamaPertama() << " " << e2.getNamaAkhir() << "\n\n";
 } // akhir dari skop bersarang di dalam main

// tidak ada objek yang eksis; gunakan nama kelas dan operator resolusi skop
// untuk mengakses fungsi anggota static getHitung
cout << "\nJumlah karyawan setelah objek dihapus adalah "
  << Karyawan::getHitung() << endl;
 } // akhir dari main

2.7 Kelas Proxy
Dua di antara prinsip-prinsip fundamental dari rekayasa perangkat lunak adalah memisahkan antarmuka dari implementasi dan menyembunyikan detil implementasi. Usaha ini telah dilakukan dengan mendefinisikan sebuah kelas di dalam suatu header dan mengimplementasikan fungsi-fungsi anggotanya di dalam file implementasi yang terpisah. Header tidak memuat implementasi kelas. Sebagai contoh, anggota private suatu kelas dicantumkan di dalam definisi kelas dalam suatu header, jadi anggota tersebut dapat diketahui oleh klien, meskipun klien tidak bisa mengakses anggota private. Penyingkapan data private suatu kelas dengan cara ini berpotensi pembocoran informasi kepada klien kelas. Sekarang akan dikenalkan suatu kelas proxy yang mengijinkan Anda untuk menyembunyikan data private suatu kelas dari klien kelas. Penyediaan sebuah kelas proxy, yang hanya mengetahui antarmuka public, untuk klien kelas Anda memampukan klien untuk menggunakan layanan kelas Anda tanpa perlu memberikan klien akses terhadap detil implementasi.

Pengimplementasian sebuah kelas proxy memerlukan beberapa tahapan, yang akan didemonstrasikan pada Gambar 2.21-2.24. Pertama-tama akan diciptakan definisi kelas untuk kelas yang memuat implementasi yang akan disembunyikan. Kelas contoh, yang dinamakan Implementasi, ditampilkan pada Gambar 2.21. Kelas proxy, Antarmuka, ditampilkan pada Gambar 2.22 – 2.23. Program uji dan contoh keluaran ditunjukkan pada Gambar 2.24. Kelas Implementasi (Gambar 2.21) menyediakan anggota data private, yang dinamakan nilai (data yang diinginkan untuk disimpan dari klien), sebuah konstruktor untuk menginisialisasi nilai, dan fungsi setNilai dan getNilai.

Gambar 2.21 Definisi Kelas Implementasi

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
// Gambar 2.21: Implementasi.h
// Definisi kelas Implementasi.

class Implementasi
{
public:
  // konstruktor
  Implementasi( int v )
     : nilai( v ) // menginisialisasi nilai dengan v
  {
    // tubuh kosong
  } // akhir dari konstruktor Implementasi

  // menetapkan nilai menjadi v
  void setNilai( int v )
  {
    nilai = v; // seharusnya memvalidasi v
  } // akhir dari fungsi setNilai

  // mengembalikan nilai
  int getNilai() const
  {
    return nilai;
  } // akhir dari fungsi getNilai
private:
  int nilai; // data yang akan disembunyikan dari klien
}; // akhir dari kelas Implementasi

Anda mendefinisikan sebuah kelas proxy yang dinamakan dengan Antarmuka (Gambar 2.22) dengan antarmuka public yang identik (kecuali untuk nama konstruktor dan destruktor) dengan antarmuka public kelas Implementasi. Satu-satunya anggota data private kelas proxy adalah sebuah pointer yang menunjuk ke suatu objek Implementasi. Penggunaan pointer dengan cara ini mengijinkan Anda untuk menyembunyikan detil implementasi kelas Implementasi dari klien. Ketika suatu definisi kelas hanya menggunakan sebuah pointer atau referensi ke suatu objek dari kelas lain (seperti kasus ini), header kelas kelas untuk kelas lain (yang biasanya menyingkapkan data private dari kelas tersebut) tidak perlu disertakan menggunakan #include. Hal ini karena kompiler tidak perlu menyisihkan ruang memori untuk objek dari kelas tersebut. Kompiler tidak perlu menyediakan ruang memori untuk pointer atau referensi.

Gambar 2.22 Definisi Kelas Antarmuka

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Gambar 2.22: Antarmuka.h
// Definisi antarmuka kelas proxy.
// Klien melihat kode sumber ini, tetapi kode sumber tidak menyingkapkan
// layout data kelas Implementasi.

class Implementasi; // deklarasi kelas maju yang dibutuhkan baris 17

class Antarmuka
{
public:
  Antarmuka( int ); // konstruktor
  void setNilai( int ); // antarmuka public sama dengan
  int getNilai() const; // yang dimiliki kelas Implementasi
  ~Antarmuka(); // destruktor
private:
  // memerlukan deklarasi maju (baris 6)
  Implementasi *ptr;
}; // end class Interface

File implementasi fungsi-anggota untuk kelas proxy Antarmuka (Gambar 2.23) merupakan satu-satunya file yang menyertakan header Implementasi.h (baris 5) yang memuat kelas Implementasi. File Antarmuka.cpp (Gambar 2.23) disediakan bagi klien sebagai sebuah objek kode prakompilasi bersamaan dengan header Antarmuka.h yang menyertakan prototipe-prototipe fungsi yang disediakan oleh kelas proxy. Karena file Antarmuka.cpp disediakan bagi klien hanya sebagai kode objek, maka klien tidak dapat melihat interaksi antara kelas proxy dan kelas proprietari (baris 9, 17, 23, dan 29).

Gambar 2.23 Definisi Fungsi Anggota Kelas Antarmuka

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
// Gambar 2.23: Antarmuka.cpp
// Implementasi dari kelas Antarmuka--klien menerima file ini hanya
// sebagai kode objek prakompilasi, menjaga implementasi tetap tersembunyi.
#include "Antarmuka.h" // definisi kelas Antarmuka
#include "Implementasi.h" // definisi kelas Implemenatasi

// konstruktor
Antarmuka::Antarmuka( int v )
   : ptr( new Implementasi( v ) )// menginisisialisasi ptr untuk menunjuk ke
{ // sebuah objek Implementasi baru
  // tubuh kosong
} // akhir dari konstruktor Antarmuka

// memanggil fungsi setNilai dari kelas Implementasi
void Antarmuka::setNilai( int v )
{
  ptr->setNilai( v );
} // akhir dari fungsi setNilai

// memanggil fungsi getNilai dari kelas Implementasi
int Antarmuka::getNilai() const
{
  return ptr->getNilai();
} // akhir dari fungsi getNilai

// destructor
Antarmuka::~Antarmuka()
{
  delete ptr;
} // akhir dari destruktor ~Antarmuka

Gambar 2.24 menguji kelas Antarmuka. Perhatikan bahwa hanya header untuk Antarmuka yang disertakan di dalam kode klien (baris 4).  Jadi, klien tidak pernah melihat data private dari kelas Implementasi, dan klien tidak menjadi bergantung pada kode Implementasi.

Gambar 2.24 Menyembunyikan Data private Menggunakan Kelas Proxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Gambar 2.24: gambar2_24.cpp
// Menyembunyikan data private menggunakan kelas proxy.
#include <iostream>
#include "Antarmuka.h" // definisi kelas Antarmuka
using namespace std;

int main()
{
  Antarmuka i( 5 ); // menciptakan objek Antarmuka

  cout << "Antarmuka memuat: " << i.getNilai()
    << " sebelum setNilai" << endl;

  i.setNilai( 10 );

  cout << "Antarmuka memuat: " << i.getNilai()
    << " setelah setNilai" << endl;
} // akhir dari main

Antarmuka memuat: 5 sebelum setNilai
Antarmuka memuat: 10 setelah setNilai

Kesimpulan
*      Anda dapat menggunakan katakunci const untuk menspesifikasi bahwa sebuah objek tidak bisa dimodifikasi dan bahwa sembarang usaha untuk memodifikasi objek akan menghasilkan error kompilasi.
*      C++ tidak mengijinkan pemanggilan fungsi anggota untuk objek const kecuali jika fungsi anggota tersebut dideklarasikan const. Hal ini berlaku pula untuk semua fungsi get yang tidak memodifikasi objek.
*      Suatu fungsi anggota dispesifikasi sebagai const di dalam prototipenya dengan menyisipkan katakunci const setelah daftar parameter fungsi dan di dalam definisi fungsi dengan menyisipkan katakunci const sebelum kurung kurawal kiri yang mengawali tubuh fungsi.
*      Fungsi friend suatu kelas didefinisikan di luar skop kelas tersebut, tetapi memiliki hak untuk mengakses anggota non-public dan public kelas. Fungsi, kelas atau fungsi anggota dari kelas lain dapat dideklarasikan menjadi friend bagi kelas lain. Penggunaan fungsi friend dapat meningkatkan kinerja program.
*      Setiap objek memiliki akses terhadap alamatnya sendiri melalui sebuah pointer yang dinamakan dengan this (salah satu katakunci C++). Pointer this bukan bagian dari objek. Memori yang diokupasi oleh pointer this tidak direfleksikan dalam hasi penerapan operasi sizeof pada sebuah objek. Pointer this dilewatkan (oleh kompiler) sebagai argumen implisit kepada fungsi anggota non-static dari setiap objek.
*      Penyediaan sebuah kelas proxy, yang hanya mengetahui antarmuka public, untuk klien kelas Anda memampukan klien untuk menggunakan layanan kelas Anda tanpa perlu memberikan klien akses terhadap detil implementasi.
Latihan
1)      Modifikasilah kelas Tanggal pada Gambar 2.8 sehingga memiliki kapabilitas-kapabilitas berikut:
a)      Menampilkan data dalam beberapa format seperti
DDDD  MMMM YYYY
DD/MM/YY
14 Juni 2012
b)      Menggunakan konstruktor teroverload untuk menciptakan objek-objek Tanggal yang diinisialisasi dengan tanggal-tanggal dalam format pada (a).
c)      Menciptakan sebuah konstruktor Tanggal yang membaca sistem tanggal menggunakan fungsi pustaka standard dengan header <ctime> dan menetapkan anggota-anggota Tanggal.

2)      Ciptakanlah sebuah kelas AkunSimpanan. Gunakan sebuah anggota data static (sukuBungaTahunan) untuk menyimpan laju bunga tahunan bagi setiap nasabah bank. Setiap anggota kelas memuat sebuah anggota data private (saldoSimpanan) untuk mengindikasikan jumlah uang tersimpan di dalam deposit. Sediakan fungsi anggota hitungBungaBulanan yang menghitung bunga per bulan dengan cara mengalikan saldo dengan sukuBungaTahunan dibagi dengan 12; bunga ini harus ditambahkan kepada saldoSimpanan. Sediakan sebuah fungsi anggota static (modifikasiSukuBunga) yang menetapkan static sukuBungaTahunan menjadi nilai baru. Kemudian tulislah suatu program untuk menguji kelas AkunSimpanan. Lakukan instansiasi untuk menciptakan dua objek kelas AkunSimpanan yang berbeda, nasabah1 dan nasabah2, dengan saldo Rp. 2000 dan Rp. 3000. Tetapkan sukuBungaTahunan menjadi 2 persen. Kemudian hitung bunga per bulan dan tampilkan saldo baru untuk setiap nasabah. Selanjutnya tetapkan sukuBungaTahunan menjadi 4 persen, dan hitung bunga untuk bulan berikutnya serta tampilkan saldo baru untuk setiap nasabah.


3)       Adalah masuk akal bila merepresentasikan kelas Waktu pada Gambar 2.15 – 2.16 untuk merepresentasikan waktu sebagai jumlah detik setelah tengah malam (jam 12 malam) daripada merepresentasikannya dengan tiga nilai integer jam, menit, dan detik. Klien dapat menggunakan metode-metode public yang sama dan mendapatkan hasil yang sama. Modifikasilah kelas Waktu pada Gambar 2.15 untuk mengimplementasikan waktu sebagai jumlah detik setelah tengah malam dan menunjukkan bahwa tidak terdapat perbedaan fungsionalitas yang berarti terhadap klien kelas.



No comments:

Post a Comment