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