Bab. 3 Overloading Operator
Tujuan
Instruksional
|
|
·
Overloading operator
kelas string.
·
Overloading operator
biner.
·
Overloading operator
ekstraksi aliran dan operator penyisipan aliran biner.
·
Overloading operator
unary.
|
·
Overloading operator
prefiks dan postfiks.
·
Manajemen memori
dinamis.
·
Studi kasus: kelas Array.
·
Konstruktor explicit.
|
3.1 Introduksi
Bab ini akan mengenalkan bagaimana
memampukan operator C++ untuk bekerja dengan objek kelas, sebuah proses yang
dikenal dengan overloading operator.
Satu contoh dari operator teroverload
di dalam C++ adalah operator <<, yang digunakan sebagai operator penyisipan
aliran dan sebagai operator geser-kiri bitwise. Sama halnya, >> adalah
operator teroverload; digunakan
sebagai operator ekstraksi aliran dan operator geser-kanan bitwise. Kedua
operator tersebut dioverload di dalam
pustaka standard C++.
Anda dapat mengoverload kebanyakan operator agar dapat digunakan dengan objek
kelas. Kompiler akan membangkitkan kode berdasarkan tipe operand. Contoh pada
bab ini akan dimulai dengan mendemonstrasikan kelas string STL, yang memiliki
banyak operator teroverload. Ini
memampukan Anda untuk melihat bagaimana operator teroverload digunakan sebelum mengimplementasikan operator teroverload Anda sendiri. Selanjutnya, Anda
akan menciptakan sebuah kelas NomorTelepon
yang memampukan Anda untuk mengoverload
operator << dan >> agar dapat menampilkan keluaran dengan lebih
baik dan memasukkan nomor telepon 10-dijit secara terformat. Selanjutnya akan
disajikan kelas Tanggal yang mengoverload operator inkremen (++) prefiks
dan postfiks untuk menginkremen sebanyak satu hari terhadap nilai di dalam
sebuah objek Tanggal. Kelas ini juga
mengoverload operator += untuk
mengijinkan program menginkremen objek Tanggal sebanyak jumlah hari yang
dispesifikasi pada sisi kanan operator.
Berikutnya, akan disajikan sebuah kelas
Array yang menggunakan beberapa
operator teroverload dan kapabilitas
lainnya untuk menyelesaikan beberapa permasalahan dengan array berbasis
pointer. Ini merupakan kasus terpenting pada buku ini. Banyak mahasiswa
mengindikasikan bahwa studi kasus Array
cukup bisa mengatrol pemahaman mereka tentang overloading operator.
3.2 Operator Teroverload Kelas string
Gambar 3.1 mendemonstrasikan beberapa
operator teroverload pada kelas string dan beberapa fungsi anggota lain,
termasuk empty, substr, dan at. Fungsi empty menentukan apakah sebuah objek string kosong atau tidak, fungsi substr menghasilkan sebuah objek string yang merepresentasikan sepenggal
string yang sudah ada sebagai nilai balik, dan fungsi at menghasilkan nilai balik berupa karakter pada indeks tertentu di
dalam sebuah objek string (setelah
memeriksa apakah indeks berada di dalam rentang atau tidak).
Gambar 3.1 Program Menguji Operator Teroverload pada Kelas string
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
|
// Gambar 3.1: gambar3_01.cpp
// Program menguji kelas string STL.
#include <iostream>
#include <string>
using namespace std;
int main()
{
string
s1( "selamat" );
string
s2( " ultah" );
string
s3;
//
menguji operator teroverload untuk ekualitas dan relasional
cout << "s1 adalah
\"" <<
s1 << "\"; s2 adalah \"" << s2
<< "\"; s3 adalah
\"" <<
s3 << '\"'
<< "\n\nHasil pembandingan
s2 dan s1:"
<< "\ns2 == s1 menghasilkan "
<< ( s2 == s1
? "true" : "false" )
<< "\ns2 != s1 menghasilkan "
<< ( s2 != s1
? "true" : "false" )
<< "\ns2 > s1
menghasilkan " << ( s2 > s1 ? "true" : "false"
)
<< "\ns2 < s1
menghasilkan " << ( s2 < s1 ? "true" : "false"
)
<< "\ns2 >= s1
menghasilkan " << ( s2 >= s1 ? "true" : "false"
)
<< "\ns2 <= s1
menghasilkan " << ( s2 <= s1 ? "true" : "false"
);
// menguji fungsi anggota empty
cout << "\n\nMenguji
s3.empty():" << endl;
if ( s3.empty() )
{
cout << "s3 kosong;
menugaskan s1 kepada s3;" << endl;
s3 = s1; // menugaskan s1 kepada s3
cout << "s3 adalah
\"" <<
s3 << "\"";
} // akhir dari if
// menguji operator penyambungan teroverload
cout << "\n\ns1 += s2
menghasilkan s1 = ";
s1 += s2; // menguji penyambungan teroverload
cout << s1;
// menguji operator penyambungan teroverload dengan
sebuah string char *
cout << "\n\ns1 += \" pada
anda\" menghasilkan" << endl;
s1 += " pada anda";
cout << "s1 = " << s1
<< "\n\n";
// menguji fungsi anggota substr
cout << "Substring dari s1
dimulai pada lokasi 0 untuk\n"
<< "13 karakter,
s1.substr(0, 13), adalah:\n"
<<
s1.substr( 0, 13 )
<< "\n\n";
// menguji substr sampai akhir string
cout << "Substring dari s1
dimulai pada\n"
<< "lokasi 14, s1.substr(14),
adalah:\n"
<< s1.substr( 14 ) << endl;
// menguji konstruktor penyalin
string s4( s1 );
cout << "\ns4 = "
<< s4 << "\n\n";
// menguji operator penugasan (=)
teroverload penugasan-diri
cout << "menugaskan s4 ke s4"
<< endl;
s4 = s4;
cout << "s4 = " << s4
<< endl;
// menguji operator subskript teroverload
untuk menciptakan lvalue
s1[ 0 ] = 'S';
s1[ 6 ] = 'U';
cout << "\ns1 setalah s1[0] =
'S' dan s1[8] = 'U' adalah: "
<< s1 << "\n\n";
// menguji subskript
apakah di luar rentang dengan fungsi anggota at
try
{
cout << "Percobaan
menugaskan 'd' kepada s1.at( 30 ) menghasilkan:" <<
endl;
s1.at( 30 ) = 'd'; // ERROR:
subskript di luar rentang
} // akhir dari try
catch ( out_of_range &ex )
{
cout << "Sebuah eksepsi
terjadi: " << ex.what() << endl;
} // akhir dari catch
} // akhir dari main
|
s1 adalah “selamat”; s2 adalah “ ulang tahun”; s3
adalah “”
Hasil pembandingan s2 dan s1:
s2 == s1 menghasilkan false
s2 != s1 menghasilkan true
s2 > s1 menghasilkan false
s2 < s1 menghasilkan true
s2 >= s1 menghasilkan false
s2 <= s1 menghasilkan true
Menguji s3.empty():
s3 kosong; menugaskan s1 kepada s3;
s3 adalah “selamat”
s1 += s2 menghasilkan s1 = selamat ultah
s1 += “ pada anda” menghasilkan
s1 = selamat ultah pada anda
Substring dari s1 dimulai pada lokasi 0 untuk
13 karakter, s1.substr(0,13), adalah:
selamat ultah
Substring dari s1 dimulai pada
lokasi 14, s1.substring(14), adalah
pada anda
s4 = selamat ultah pada anda
s1 setelah s1[0] = ‘S’dan s1[8] = ‘U’adalah: Selamat
Ultah pada anda
Percobaan menugaskan ‘d’ kepada s1.at(30)
menghasilkan:
Sebuah eksepsi terjadi: invalid string position
Baris 9-11 menciptakan tiga objek string, yaitu s1 diinisialisasi dengan literal “selamat”, s2
diinisialisasi dengan literal “ ultah”,
dan s3 menggunakan konstruktor string default untuk menciptakan sebuah
objek string kosong. Baris 14-15
menampilkan ketiga objek tersebut, menggunakan cout dan operator <<, yang dioverload oleh para perancang kelas string untuk menangani objek kelas string. Baris 16-22 menunjukkan hasil pembandingan s2 dengan s1 dengan menggunakan operator ekualitas dan relasional teroverload pada kelas string, yang melakukan pembandingan menggunakan nilai numerik atas
himpunan karakter ASCII.
Kelas string menyediakan fungsi anggota empty untuk menentukan apakah sebuah objek string kosong atau tidak, yang didemonstrasikan pada baris 27.
Fungsi anggota empty menghasilkan
nilai balik true jika string kosong; sebaliknya, menghasilkan
nilai balik false.
Baris 30 mendemonstrasikan operator
penugasan teroverload dengan
menugaskan s1 kepada s3. Baris 31 menampilkan s3 untuk mendemonstrasikan bahwa
penugasan telah berhasil dilakukan.
Baris 36 mendemonstrasikan operator teroverload += untuk penyambungan string.
Pada kasus ini, isi dari s2
disambungkan ke dalam s1. Kemudaian
baris 37 menampilkan string yang dihasilkan di dalam s1. Baris 41 mendemonstrasikan bahwa sebuah literal string dapat
disambungkan ke dalam sebuah objek string
menggunakan operator +=. Baris 42 menampilkan hasilnya.
Kelas string menyediakan fungsi anggota substr (baris 47 dan 52) untuk menghasilkan nilai balik berupa
sepotong string sebagai sebuah objek string.
Pemanggilan terhadap substr pada
baris 47 dilakukan untuk mendapatkan sebuah substring 13-karakter
(dispesifikasi oleh argumen kedua) dari s1
dimulai pada posisi 0 (yang dispesifikasi oleh argumen pertama). Pemanggilan
terhadap substr pada baris 52
dilakukan untuk mendapatkan sebuah substring dimulai dari posisi 14 dari s1. Ketika argumen kedua tidak
dispesifikasi, substr akan
menghasilkan nilai balik berupa sisa penggalan sebuah objek string.
Baris 55 menciptakan objek string s4 dan menginisialisasinya dengan salinan dari s1. Ini menghasilkan pemanggilan terhadap konstruktor penyalin pada
kelas string. Baris 60 menggunakan
operator teroverload = untuk
mendemonstrasikan bahwa telah terjadi penugasan-diri.
Baris 64-65 menggunakan operator teroverload [ ] untuk menciptakan lvalue yang memampukan karakter-karakter
baru untuk menggantikan karakter-karakter yang sudah ada di dalam s1. Baris 67 menampilkan nilai baru dari
s1. Operator teroverload [ ] tidak melakukan pemeriksaan terhadap batas rentang.
Oleh karena itu, Anda harus memastikan bahwa operasi yang menggunakan operator
teroverload [ ] tidak secara tidak sengaja memanipulasi elemen-elemen di luar
rentang objek string. Kelas string menyediakan pemeriksaan batas
rentang di dalam fungsi anggotanya at,
yang melemparkan eksepsi jika argumennya berupa subskript tak-valid. Secara
default, ini menyebabkan program C++ untuk berhenti dan menampilkan pesan
error. Jika subskript valid, fungsi at
menghasilkan nilai balik berupa karakter pada lokasi yang dispesifikasi.
3.3 Dasar Overloading Operator
Seperti yang Anda lihat pada Gambar
3.1, operator menyediakan notasi singkat untuk memanipulasi objek string. Anda dapat menggunakan operator
teroverload buatan sendiri. Meskipun
C++ tidak mengijinkan penciptaan operator baru, tetapi ia mengijinkan hampir semua
operator untuk dioverload, sehingga
ketika dipakai dengan objek, operator tersebut mempunyai makna yang sesuai
terhadap objek tersebut.
Overloading operator tidak otomatis, karena Anda
harus menulis fungsi overloading
operator untuk melakukan operasi yang diinginkan. Sebuah operator dioverload dengan menulis definisi fungsi
anggota non-static atau definisi
fungsi non-anggota, dimana nama fungsi harus diawali dengan katakunci operator yang diikuti dengan operator
yang sedang dioverload. Sebagai
contoh, nama fungsi operator+ bisa
digunakan untuk mengoverload operator
penjumlahan (+) agar digunakan dengan objek dari kelas tertentu. Ketika
operator dioverload sebagai fungsi
anggota, maka ia harus berupa non-static,
karena operator itu harus dipanggil pada objek kelas tersebut.
Untuk menggunakan operator pada objek
suatu kelas, operator harus dioverload
untuk kelas tersebut, dengan tiga pengecualian:
·
Operator penugasan (=) dapat dipakai dengan setiap kelas untuk melakukan
penugasan keanggotaan atas anggota data kelas. Setiap anggota data ditugasi
dari penugasan objek sumber (sebalah kanan) kepada objek target (sebelah kiri).
Penugasan keanggotaan berbahaya untuk kelas dengan anggota pointer, jadi Anda
harus secara eksplisit mengoverload
operator penugasan untuk kelas semacam itu.
·
Operator alamat (&) menghasilkan nilai balik berupa pointer kepada
objek; operator ini juga dapat dioverload.
·
Operator koma mengevaluasi ekspresi ke kiri kemudian ekspresi ke kanan,
dan menghasilkan nilai balik berupa ekspresi paling akhir. Operator ini juga
dapat dioverload.
Operator yang
tidak dapat Dioverload
Hampir semua operator C++ dapat dioverload. Gambar 2.2 menunjukkan
operator-operator yang tidak dapat dioverload.
Gambar
2.2 Semua Operator yang Tidak Bisa Dioverload
Operator-operator yang tidak bisa
dioverload
|
|||
.
|
.*
|
::
|
?:
|
Aturan dan
Batasan Overloading Operator
Ketika Anda telah bersiap untuk mengoverload kelas Anda sendiri, ada
beberapa aturan dan batasan yang perlu diketahui:
·
Derajat keutamaan sebuah operator tidak bisa diubah lewat overloading. Tetapi, sepasang kurung
dapat dipakai untuk memaksa urutan evaluasi atas operator-operator teroverload di alam sebuah ekspresi.
·
Asosiatifitas suatu operator tidak bisa diubah lewat overloading. Jika
suatu operator berasosiasi dari kiri ke kanan, maka semua versi teroverloadnya juga berasosiasi dari kanan
ke kiri.
·
Anda tidak bisa mengubah “aritas” suatu operator (yaitu, jumlah operand
yang diperlukan sebuah operator); operator unary teroverload tetap menjadi operator unary; operator biner teroverload tetap menjadi operator biner.
Operator &, *, +, dan – semuanya memiliki versi unary dan biner yang dapat
dioverload secara terpisah.
·
Anda tidak bisa menciptakan operator baru; hanya operator yang sudah ada
yang bisa dioverload.
·
Makna tentang bagaimana sebuah operator bekerja pada tipe-tipe
fundamental tidak bisa diubah lewat overloading.
Sebagai contoh, Anda tidak bisa membuat operator + untuk mengurangkan dua int. Overloading
operator hanya bisa diterapkan dengan objek yang didefinisikan pengguna atau
dengan gabungan antara objek yang didefinisikan pengguna dan objek bertipe
fundamental.
·
Operator yang berelasi, seperti + dan +=, harus dioverload secara terpisah.
·
Ketika mengoverload ( ), [ ],
-> atau sembarang operator penugasan, fungsi overloading operator harus dideklarasikan sebagai fungsi anggota.
Untuk operator lain (yang bisa dioverload),
fungsi overloading operator dapat
dideklarasikan sebagai fungsi anggota atau fungsi non-anggota.
3.4 Overloading Operator Biner
Sebuah operator biner dapat dioverload sebagai fungsi anggota non-static dengan satu parameter atau
sebagai fungsi non-anggota dengan dua parameter (salah satu dari parameter tersebut
harus berupa objek kelas atau referensi ke objek kelas tertentu). Fungi
operator non-anggota seringkali dideklarasikan sebagai friend dari suatu kelas untuk meningkatkan kinerja.
Operator
Teroverload Biner Sebagai Fungsi Anggota
Perhatikan penggunaan < untuk
membandingkan dua objek kelas String
yang Anda definisikan. Ketika mengoverload
operator biner < sebagai fungsi anggota non-static dari kelas String,
jika y dan z adalah objek kelas String, maka y < z diperlakukan seperti y.operator<
(z). Pemanggilan fungsi anggota operator< dengan satu argumen dideklarasikan
berikut:
class String
{
public:
bool operator<( const String & ) const;
...
}; // akhir dari kelas String
Fungsi operator teroverload untuk operator biner dapat dideklarasikan sebagai fungsi
anggota hanya jika operand kiri adalah objek kelas dimana di dalamnya fungsi
tersebut sebagai anggotanya.
Operator
Teroverload Biner Sebagai Fungsi Non-Anggota
Sebagai fungsi non-anggota, operator
biner < harus mengambil dua argumen, satu di antaranya harus berupa objek
(atau referensi ke suatu objek) kelas. Jika y
dan z adalah objek kelas String atau referensi ke objek kelas String, maka y < z diperlakukan seperti operator<(y,
z). Pemanggilan operator< dilakukan sebagai berikut:
bool operator<( const String &, const String & );
3.5 Overloading Operator Penyisipan dan
Operator Ekstraksi Aliran
Anda dapat memasukkan dan menampilkan
data bertipe fundamental menggunakan operator ekstraksi aliran >> dan
operator penyisipan aliran <<. Pustaka kelas C++ mengoverload kedua operator tersebut untuk semua data bertipe
fundamental, termasuk pointer dan string char
*. Anda juga dapat mengoverload kedua operator itu untuk melakukan pemasukan
dan pengeluaran tipe data buatan sendiri. Program pada Gambar 3.3 – 3.11 mengoverload kedua operator itu untuk
memasukkan dan mengeluarkan objek NomorTelepon
di dalam format “(000) 000-0000”. Program mengasumsikan bahwa nomor telepon
dientrikan dengan benar.
Gambar 3.3 Definisi Kelas NomorTelepon
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// Gambar 3.3: NomorTelepon.h
// Definisi kelas NomorTelepon
#ifndef NOMORTELEPON_H
#define NOMORTELEPON_H
#include <iostream>
#include <string>
using namespace std;
class NomorTelepon
{
friend ostream
&operator<<( ostream &, const NomorTelepon & );
friend istream
&operator>>( istream &, NomorTelepon & );
private:
string kodeArea; // kode area 3-dijit
string antarKode; // antar kode 3-dijit
string jalur; // jalur 4-dijit
}; // akhir dari kelas NomorTelepon
#endif
|
Gambar 3.4 Operator Penyisipan dan Ektraksi Aliran
Teroverload
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 3.4: NomorTelepon.cpp
// Operator penyisipan aliran dan operator
ekstraksi aliran teroverload
// untuk kelas NomorTelepon.
#include <iomanip>
#include "NomorTelepon.h"
using namespace std;
// operator penyisipan aliran teroverload;
tidak bisa menjadi
// fungsi anggota jika memanggilnya dengan
// cout << nomorTeleponTertentu;
ostream &operator<<(
ostream &keluaran, const NomorTelepon &nomor )
{
keluaran
<< "(" << nomor.kodeArea << ")
"
<<
nomor.antarKode << "-" << nomor.jalur;
return
keluaran; // memampukan cout << a << b << c;
} // akhir dari fungsi operator<<
// operator ekstraksi aliran teroverload;
tidak bisa menjadi
// fungsi anggota jika memanggilnya dengan
// cin >> nomorTeleponTertentu;
istream &operator>>(
istream &masukan, NomorTelepon &nomor )
{
masukan.ignore();
// lompati (
masukan
>> setw( 3 ) >> nomor.kodeArea; // memasukkan kode area
masukan.ignore(
2 ); // lompati ) dan spasi
masukan
>> setw( 3 ) >> nomor.antarKode; // memasukkan antar kode
masukan.ignore();
// melompati dash (-)
masukan
>> setw( 4 ) >> nomor.jalur; // memasukkan jalur
return
masukan; // memampukan cin >> a >> b >> c;
} // akhir dari fungsi operator>>
|
Gambar 3.5 Mendemonstrasikan Operator Penyisipan dan Ektraksi
Aliran Teroverload
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// Gambar 3.5: gambar3_05.cpp
// Mendemonstrasikan
operator penyisipan aliran dan operator ekstraksi aliran
// teroverload kelas NomorTelepon.
#include <iostream>
#include "NomorTelepon.h"
using namespace std;
int main()
{
NomorTelepon telepon; // menciptakan objek
telepon
cout << "Masukkan
nomor telepon dalam format (123) 456-7890:" << endl;
//
cin >> telepon memanggil operator>> secara implisit memanggil
//
fungsi non-anggota operator>>( cin, telepon )
cin >> telepon;
cout << "Nomor telepon yang
dimasukkan adalah: ";
// cout << telepon memanggil operator<<
secara implisit memanggil
//
fungsi non-anggota operator<<( cout, telepon )
cout << telepon <<
endl;
} // akhir dari main
|
Overloading
Operator Ekstraksi Aliran >>
Fungsi operator ekstraksi aliran operator>> (Gambar 3.4, baris
21-30) mengambil referensi masukan bertipe istream
dan referensi nomor bertipe NomorTelepon
sebagai argumen dan menghasilkan nilai balik berupa referensi bertipe istream. Fungsi operator operator>> memasukkan nomor
telepon dalam format
(800) 555-1212
ke dalam objek dari kelas NomorTelepon. Ketika kompiler
menggunakan ekspresi
cin >>
telepon
pada baris 16 pada Gambar 3.5, kompiler
membangkitkan pemanggilan fungsi non-anggota
operator>>( cin,
telepon );
Ketika panggilan ini dieksekusi,
parameter referensi masukan (Gambar
3.4, baris 21) menjadi alias bagi cin
dan parameter referensi nomor menjadi
alias bagi telepon. Fungsi operator
membaca tiga bagian nomor telepon (sebagai string)
ke dalam anggota kodeArea (baris 24),
antarKode (baris 26), dan jalur (baris 28) dari objek NomorTelepon yang direferensi oleh nomor. Manipulator aliran setw membatasi jumlah karakter yang
dibaca ke dalam setiap string. Ketika
digunakan dengan cin dan string, setw membatasi jumlah karakter yang dibaca sebanyak jumlah karakter
yang dispesifikasi oleh argumennya (misalnya, setw(3) mengijinkan tiga karakter untuk dibaca). Karakter kurung,
spasi, dan dash dilompati dengan memanggil fungsi ignore dari kelas istream
(Gambar 3.4, baris 23, 25, dan 27), yang membuang sejumlah karakter yang
dispesifikasi di dalam aliran masukan (satu karakter secara default). Fungsi operator>> menghasilkan nilai
balik berupa referensi masukan bertipe istream (misalnya, cin). Ini memampukan operasi masukan pada objek NomorTelepon untuk dijadikan bertingkat
dengan operasi-operasi masukan pada objek-objek NomorTelepon lain atau dengan tipe data lain. Sebagai contoh,
sebuah program dapat memasukkan dua objek NomorTelepon
di dalam satu statemen sebagai berikut:
cin >>
telepon1 >> telepon2;
Pertama, ekspresi cin>>telepon1 dieksekusi dengan memanggil fungsi non-anggota
operator>>(
cin, telepon1 );
Pemanggilan ini kemudian menghasilkan
nilai balik berupa sebuah referensi ke cin sebagai nilai dari cin>>telepon1, sehingga ekspresi
yang tersisa diinterpretasikan sebagai cin>>telepon2.
Ini dieksekusi dengan pemanggilan fungsi non-anggota
operator>>( cin, telepon2 );
Overloading
Operator Penyisipan Aliran <<
Fungsi operator penyisipan (Gambar 3.4,
baris 11-16) mengambil sebuah referensi bertipe ostream (keluaran) dan sebuah referensi bertipe const NomorTelepon (nomor) sebagai argumen dan menghasilkan nilai balik
berupa referensi bertipe ostream.
Fungsi operator<< menampilkan
objek bertipe NomorTelepon. Ketika
kompiler melihat ekspresi
cout << telepon
pada baris 22 pada Gambar 3.5, kompiler
membangkitkan pemanggilan fungsi non-anggota
operator<<( cout,
telepon );
Fungsi operator<< menampilkan bagian-bagian nomor telepon sebagai string, karena disimpan sebagai objek string.
Operator
Teroverload Sebagai Fungsi friend Non-Anggota
Fungsi operator>> dan operator<<
dideklarasikan di dalam NomorTelepon
sebagai fungsi friend non-anggota
(Gambar 3.3, baris 12-13). Keduanya merupakan fungsi non-anggota karena objek
dari kelas NomorTelepon harus berupa
operand kanan dari operator. Jika kedua fungsi dijadikan fungsi anggota di
dalam kelas NomorTelepon, maka statemen
berikut akan menjadi salah:
telepon
<< cout;
telepon >>
cin;
Statemen seperti itu akan menjadi
membingungkan bagi para programer C++, yang familiar dengan cout dan cin sebagai operand kiri bagi << dan >>.
Fungsi operator teroverload untuk operator biner dapat dideklarasikan sebagai fungsi
anggota hanya jika operand kiri berupa sebuah objek dari kelas yang di dalamnya
fungsi tersebut sebagai anggota. Operator masukan dan keluaran teroverload dideklarasikan sebagai friend jika keduanya perlu mengakses
anggota non-public secara langsung
atau karena kelas tidak menyediakan fungsi-fungsi get yang dibutuhkan. Referensi bertipe NomorTelepon di dalam daftar parameter fungsi operator<< (Gambar 3.4, baris 11) dideklarasikan const, karena objek NomorTelepon hanya untuk ditampilkan, dan Referensi bertipe NomorTelepon di dalam daftar parameter
fungsi operator>> (baris 21)
dideklarasikan non-const, karena
objek NomorTelepon perlu dimodifikasi
untuk menyimpan nomor telepon yang dimasukkan di dalam objek.
Mengapa Operator
Penyisipan Aliran dan Operator Ekstraksi Aliran Dioverload Sebagai Fungsi
Non-Anggota
Operator penyisipan aliran teroverload (<<) digunakan di dalam
sebuah ekspresi dimana di dalamnya operand kiri memiliki tipe ostream &, seperti cout << objekKelas. Untuk
menggunakan operator dengan cara ini dimana operand kanan berupa sebuah objek
dari kelas yang didefinisikan pengguna, operator tersebut harus dioverload sebagai fungsi non-anggota.
Untuk menjadi fungsi anggota, operator<<
harus menjadi anggota dari kelas ostream.
Hal ini tidak memungkinkan untuk kelas yang didefinisikan sendiri oleh
pengguna, karena Anda sebagai pengguna tidak diijinkan untuk memodifikasi kelas
STL C++.
Operator ekstraksi aliran teroverload (>>) digunakan di dalam
sebuah ekspresi dimana di dalamnya operand kiri memiliki tipe istream &, seperti cin>> objekKelas. Untuk
menggunakan operator dengan cara ini dimana operand kanan berupa sebuah objek
dari kelas yang didefinisikan pengguna, operator tersebut harus dioverload sebagai fungsi non-anggota.
Untuk menjadi fungsi anggota, operator>>
harus menjadi anggota dari kelas istream.
Hal ini tidak memungkinkan untuk kelas yang didefinisikan sendiri oleh
pengguna, karena Anda sebagai pengguna tidak diijinkan untuk memodifikasi kelas
STL C++. Agar kedua fungsi operator teroverload
tersebut dapat mengakses anggota private
dari objek kelas yang sedang dioperasikan, keduanya dijadikan fungsi friend.
3.6 Overloading Operator Unary
Operator unary suatu kelas dapat dioverload sebagai fungsi anggota non-static dengan tanpa argumen atau sebagai
fungsi non-anggota dengan satu argumen (yang harus berupa objek kelas atau
referensi ke sebuah objek kelas). Fungsi anggota yang mengimplementasikan
operator teroverload harus
dideklarasikan non-static sehingga ia
dapat mengakses data non-static di
dalam setiap objek kelas.
Operator Unary
Teroverload Sebagai Fungsi Anggota
Perhatikan bagaimana mengoverload operator unary ! untuk menguji
apakah sebuah objek dari kelas String
(buatan Anda sendiri) kosong atau tidak. Fungsi semacam itu akan menghasilkan
nilai balik bertipe bool. Ketika
sebuah operator unary seperti ! dioverload
sebagai fungsi anggota tanpa argumen dan kompiler melihat ekspresi !s (dimana s adalah objek dari kelas String),
kompiler akan membangkitkan pemanggilan fungsi s.operator!(). Operand s
adalah objek String yang terhadapnya
akan diterapkan fungsi anggota operator!.
Fungsi ini dideklarasikan sebagai berikut:
class String
{
public:
bool operator!() const;
...
}; // akhir dri kelas String
Operator Unary
Teroverload Sebagai Fungsi Non-Anggota
Operator unary seperti ! dapat dioverload sebagai fungsi non-anggota
dengan satu parameter dalam dua cara yang berbeda. Cara pertama adalah fungsi
non-anggota dengan satu parameter yang merupakan sebuah objek (ini memerlukan
salinan dari objek, jadi efek samping fungsi tidak diterapkan pada objek asli),
atau cara kedua adalah fungsi non-anggota dengan satu parameter yang berupa
sebuah referensi ke suatu objek (tidak salinan dari objek asli diperlukan,
sehingga semua efek samping dari fungsi ini diterapkan langsung pada objek
asli). Jika s adalah objek kelas String (atau sebuah referensi ke objek
kelas String), maka !s diperlakukan seperti pemanggilan
operator!(s). Pemanggilan fungsi non-anggota operator! dideklarasikan sebagai berikut:
bool operator!( const String & );
3.7 Overloading Operator Unary Prefiks
dan Postfiks ++ dan --
Versi prefiks dan postfiks dari
operator inkremen dan dekremen dapat dioverload.
Akan dilihat bagaimana kompiler membedakan antara versi prefiks dan versi
postfiks dari operator inkremen atau dekremen.
Untuk mengoverload operator inkremen prefiks dan postfiks, setiap fungsi
operator teroverload harus memiliki
sidik atau tanda-tangan yang berbeda, sehingga kompiler dapat menentukan versi
mana dari ++ yang diinginkan. Versi prefiks dioverload sama persis dengan sembarang operator unary prefiks
lainnya. Semuanya yang dijelaskan pada bagian ini dalam mengoverload operator inkremen prefiks dan
postfiks berlaku juga dalam mengoverload
operator dekremen prefiks dan postfiks. Pada bagian berikutnya, akan disajikan
kelas Tanggal dengan operator
inkremen prefiks dan postfiks teroverload.
Mengoverload
Operator Inkremen Prefiks
Dimisalkan, bahwa Anda ingin
menambahkan 1 kepada hari di dalam objek Tanggal,
d1. Ketika kompiler melihat ekspresi
preinkremen ++d1, kompiler akan membangkitkan
pemanggilan fungsi anggota
d1.operator++()
Prototipe untuk fungsi anggota operator
harus berupa
Tanggal &operator++();
Jika operator inkremen prefiks
diimplementasikan sebagai fungsi non-anggota, maka, ketika kompiler melihat
ekspresi ++d1, kompiler akan
membangkitkan pemanggilan fungsi
operator++( d1 )
Prototipe untuk fungsi operator
non-anggota ini harus dideklarasikan sebagai
Tanggal &operator++( Tanggal &
);
Mengoverload
Operator Inkremen Postfiks
Mengoverload
operator inkremen postfiks menjadi tantangan tersendiri, karena kompiler harus
bisa membedakan sidik antara fungsi operator inkremen prefiks teroverload dengan fungsi operator inkremen
postfiks teroverload. Konvensi yang
diadopsi adalah bahwa, ketika kompiler melihat ekspresi postinkremen d1++, kompiler akan membangkitkan
pemanggilan fungsi anggota
d1.operator++( 0 )
Prototipe untuk fungsi anggota operator
harus berupa
Tanggal operator++( int )
Argumen 0 hanyalah nilai sampah yang
memampukan kompiler untuk membedakan antara fungsi operator inkremen prefiks
dengan fungsi operator inkremen postfiks. Sintaks yang sama digunakan untuk
membedakan antara fungsi operator dekremen prefiks dengan fungsi operator
dekremen postfiks.
Jika operator inkremen postfiks
diimplementasikan sebagai fungsi non-anggota, maka, ketika kompiler melihat
ekspresi d1++, kompiler akan
membangkitkan pemanggilan fungsi
operator++( d1, 0 )
Prototipe untuk fungsi operator
non-anggota ini harus dideklarasikan sebagai
Tanggal operator++( Tanggal &,
int );
Sekali lagi dikatakan bahwa argumen 0
digunakan oleh kompiler untuk membedakan antara fungsi operator inkremen
prefiks dengan fungsi operator inkremen postfiks yang diimplementasikan sebagai
fungsi non-anggota. Perhatikan bahwa operator inkremen postfiks menghasilkan
nilai balik objek Tanggal lewat
nilai, sedangkan operator inkremen prefiks menghasilkan nilai balik objek Tanggal lewat referensi. Hal ini
dilakukan karena operator inkremen postfiks biasanya menghasilkan nilai balik
berupa objek sementara yang memuat nilai asli dari objek sebelum inkremen
terjadi. C++ memperlakukan objek semacam itu sebagai rvalue, yang tidak dapat digunakan pada sisi kiri penugasan.
Operator inkremen prefiks menghasilkan nilai balik berupa objek terinkremen
aktual dengan nilai barunya. Objek semacam itu dapat dipakai sebagai lvalue.
3.8 Studi Kasus: Kelas Tanggal
Program pada Gambar 3.6 - 3.8
mendemonstrasikan sebuah kelas Tanggal,
yang menggunakan operator postfiks dan prefiks teroverload untuk menambahkan 1 kepada hari di dalam sebuah objek Tanggal, yang bisa menyebabkan
penginkremenan pada bulan dan tahun (bila diperlukan). Header Tanggal (Gambar 3.6) menspesifikasi
antarmuka public kelas Tanggal yang menyertakan operator
penyisipan aliran teroverload (baris
11), sebuah konstruktor default (baris 13), sebuah fungsi setTanggal (baris 14), sebuah operator inkremen prefiks teroverload (baris 15), sebuah operator
inkremen postfiks teroverload (baris
16), sebuah operator penugasan penjumlahan += teroverload (baris 17), sebuah fungsi untuk menguji tahun leap (baris 18), dan sebuah fungsi untuk
menentukan apakah suatu hari adalah hari terakhir dalam bulan tertentu (baris
19).
Gambar 3.6 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
23
24
25
26
27
28
29
|
// Gambar 3.6: Tanggal.h
// Definisi kelas Tanggal dengan operator
inkremen teroverload.
#ifndef TANGGAL_H
#define TANGGAL_H
#include <iostream>
using namespace std;
class Tanggal
{
friend ostream &operator<<(
ostream &, const Tanggal & );
public:
Tanggal( int m = 1, int d = 1,
int y = 1900 ); // konstruktor default
void setTanggal( int, int,
int ); // menetapkan bulan, hari, dan tahun
Tanggal &operator++(); // operator
inkremen prefiks
Tanggal operator++( int ); // operator inkremen postfiks
const Tanggal &operator+=( int
); // menambah hari, memodifikasi objek
static bool tahunLeap( int );
// apakah tanggal di dalam tahun leap?
bool akhirBulan( int ) const;
// apakah tanggal hari akhir di dalam bulan?
private:
int bulan;
int hari;
int tahun;
static const int hari2[]; // array
hari2 tiap bulan
void tolongInkremen(); // fungsi utilitas untuk menginkremen tanggal
}; //
akhir dari kelas Tanggal
#endif
|
Gambar 3.7 Definisi Fungsi Anggota dan Fungsi
friend 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
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
// Gambar 3.7: Tanggal.cpp
// Definisi fungsi anggota dan fungsi friend
kelas Tanggal.
#include <iostream>
#include <string>
#include "Tanggal.h"
using namespace std;
// menginisialisasi anggota static
const int Tanggal::hari2[] =
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31,
30, 31 };
// konstruktor Tanggal
Tanggal::Tanggal( int bulan, int
hari, int tahun )
{
setTanggal( bulan, hari, tahun );
} // akhir dari konstruktor Tanggal
// menetapkan bulan, hari, dan tahun
void Tanggal::setTanggal( int mm, int
dd, int yy )
{
if ( mm >= 1 && mm <=
12 )
bulan = mm;
else
throw invalid_argument( "Bulan
harus 1-12" );
if ( yy >= 1900 && yy
<= 2100 )
tahun = yy;
else
throw invalid_argument( "Tahun
harus >= 1900 dan <= 2100" );
// menguji apakah tahun leap atau tidak
if ( ( bulan == 2 &&
tahunLeap( tahun ) && dd >= 1 && dd <= 29 ) ||
( dd >= 1 && dd <= hari2[ bulan
] ) )
hari = dd;
else
throw invalid_argument(
"Hari di luar rentang untuk
bulan dan tahun sekarang" );
} // akhir dari fungsi setTanggal
// operator inkremen prefiks teroverload
Tanggal
&Tanggal::operator++()
{
tolongInkremen();
// menginkremen tanggal
return
*this; // referensi untuk menciptakan lvalue
} // akhir dari fungsi operator++
// operator inkremen postfiks teroverload;
perhatikan bahwa
// parameter integer sampah tidak memiliki
nama parameter
Tanggal
Tanggal::operator++( int )
{
Tanggal
temp = *this; // menampung keadaan objek sekarang
tolongInkremen();
//
mengembalikan objek temporer, tak-terinkremen
return
temp; // nilai yang dikembalikan; bukan referensi
yang dijadikan nilai balik
} // akhir dari fungsi operator++
// menambahkan sejumlah hari tertentu pada
tanggal
const Tanggal &Tanggal::operator+=( int
hariTambahan )
{
for
( int i = 0; i < hariTambahan; ++i )
tolongInkremen();
return
*this; // memampukan untuk kaskade
} // akhir dari fungsi operator+=
// jika tahun adalah
tahun leap, maka dihasilkan true; sebaliknya, false
bool Tanggal::tahunLeap( int ujiTahun )
{
if ( ujiTahun % 400 == 0 ||
( ujiTahun % 100 != 0 && ujiTahun
% 4 == 0 ) )
return true; // sebuah tahun
leap
else
return false; // bukan tahun
leap
} // akhir dari fungsi tahunLeap
//
menentukan apakah hari adalah hari terakhir dalam bulan tertentu
bool Tanggal::akhirBulan( int ujiHari ) const
{
if ( bulan == 2 && tahunLeap(
tahun ) )
return ujiHari == 29; // hari terakhir Pebruari dalam tahun leap
else
return ujiHari == hari2[ bulan ];
} // akhir dari fungsi akhirBulan
// fungsi untuk membantu menginkremen
tanggal
void Tanggal::tolongInkremen()
{
// hari bukan hari terakhir di dalam bulan
tertentu
if ( !akhirBulan( hari ) )
++hari; // menginkremen hari
else
if ( bulan < 12 ) // hari adalah hari terakhir dalam bulan dan bulan
< 12
{
++bulan; // menginkremen bulan
hari = 1; // hari pertama bulan baru
} // akhir dari if
else // hari terakhir tahun
{
++tahun; // menginkremen tahun
bulan = 1; // bulan pertama di tahun
baru
hari = 1; // hari pertama tahun baru
} // akhir dari else
} // akhir dari fungsi tolongInkremen
// operator keluaran teroverload
ostream &operator<<( ostream
&keluaran, const Tanggal &d )
{
static string namaBulan[ 13 ] = {
"", "Januari", "Pebruari",
"Maret", "April",
"Mei", "Juni", "Juli",
"Agustus",
"September", "Oktober",
"Nopember", "Desember" };
keluaran << namaBulan[ d.bulan ]
<< ' ' << d.hari << ", " << d.tahun;
return keluaran; // memampukan
kaskade
} // akhir dari fungsi operator<<
|
Gambar 3.8 Menguji 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
|
// Gambar 3.8: gambar3_08.cpp
// Program menguji kelas Tanggal.
#include <iostream>
#include "Tanggal.h" // Definisi kelas
Tanggal
using namespace std;
int main()
{
Tanggal d1( 12, 27, 2010 ); // 27 Desember
2010
Tanggal d2; // default menjadi 1 Januari
1900
cout << "d1 adalah "
<< d1 << "\nd2 adalah " << d2;
cout << "\n\nd1 += 7 adalah
" << ( d1 += 7 );
d2.setTanggal( 2, 28, 2008 );
cout << "\n\n d2 adalah "
<< d2;
cout << "\n++d2 adalah "
<< ++d2 << "(tahun leap mengijinkan hari ke-29)";
Tanggal
d3( 7, 13, 2010 );
cout
<< "\n\nMenguji operator inkremen prefiks:\n"
<<
" d3 adalah " << d3 << endl;
cout
<< "++d3 adalah " << ++d3 << endl;
cout
<< " d3 adalah " << d3;
cout
<< "\n\nMenguji operator inkremen postfiks:\n"
<<
" d3 adalah " << d3 << endl;
cout
<< "d3++ adalah " << d3++ << endl;
cout
<< " d3 adalah " << d3 << endl;
} // akhir dari main
|
d1 adalah Desember 27, 2010
d2 adalah Januari 1, 1900
d1 += 7 adalah Januari 3, 2011
d2 adalah
Pebruari 28, 2008
++d2 adalah Pebruari 29, 2008 (tahun leap
mengijinkan hari ke-29)
Menguji operator inkremen prefiks:
d3 adalah
Juli 13, 2010
++d3 adalah Juli 14, 2010
Menguji Operator inkremen postfiks:
d3 adalah
Juli 14, 2010
d3++ adalah Juli 14, 2010
d3 adalah
Juli 15, 2010
Fungsi main (Gambar 3.8) menciptakan dua objek Tanggal (baris 9-10), d1
diinisialisasi dengan Desember 27, 2010 dan d2
diinisialisasi secara default dengan Januari 1, 1900. Konstruktor Tanggal
(didefinisikan pada Gambar 3.11, baris 13-16) memanggil setTanggal (didefinisikan pada Gambar 3.7, baris 19-38) untuk
memvalidasi bulan, hari, dan tahun yang dispesifikasi. Nilai-nilai yang
tak-valid untuk bula, hari, atau tahun akan melemparkan eksepsi invalid_argument.
Baris 12 dari main menampilkan setiap objek Tanggal,
menggunakan operator penyisipan teroverload
(didefinisikan pada Gambar 3.7, baris 107-114). Baris 13 dari main menggunakan operator += teroverload (didefinisikan pada Gambar 3.7,
baris 59-65) untuk menambahkan tujuh hari pada d1. Baris 15 menggunakan fungsi setTanggal
untuk menetapkan d2 menjadi Pebruari
28, 2008, yang merupakan tahun leap. Kemudian, baris 17 melakukan preinkremen
terhadap d2 untuk menunjukkan bahwa
tanggal diinkremen secara tepat menjadi Pebruari 29, 2008. Selanjutnya, baris
19 menciptakan sebuah objek Tanggal, d3, yang diinisialisasi dengan Juli 13,
2010. Kemudian baris 23 menginkremen d3
sebesar 1 menggunakan operator inkremen prefiks teroverload. Baris 21-24 menampilkan d3 sebelum dan sesudah dilakukan operasi preinkremen untuk
menegaskan bahwa telah bekerja dengan tepat. Terakhir, baris 28 menginkremen d3 dengan operator inkremen postfiks teroverload. Baris 26-29 menampilkan d3
sebelum dan sesudah operasi postinkremen untuk menegaskan bahwa operasi
tersebut telah bekerja dengan benar.
Operator
Inkremen Prefiks Kelas Tanggal
Pengoverloadan
operator inkremen prefiks cukup sederhana. Operator inkremen prefiks
(didefinisikan pada Gambar 3.7, baris 41-45) memanggil fungsi utilitas tolongInkremen (didefinisikan pada
Gambar 3.7, baris 87-104) untuk menginkremen tanggal. Fungsi ini bukan hanya
menginkremen hari, tetapi juga akan menginkremen bulan (jika hari yang
diinkremen adalah hari terakhir dalam bulan tertentu) dan akan menginkremen
tahun (jika bulan yang diinkremen adalah bulan 12). Fungsi tolongInkremen menggunakan fungsi akhirBulan untuk menginkremen hari secara tepat.
Operator inkremen prefiks teroverload menghasilkan nilai balik berupa
sebuah referensi ke objek Tanggal
sekarang (objek yang baru saja diinkremen). Ini terjadi karena objek sekarang, *this, dijadikan nilai balik sebagai Date &. Ini memampukan objek Tanggal yang dipreinkremen untuk
digunakan sebagai sebuah lvalue.
Operator
Inkremen Postfiks Kelas Tanggal
Pengoverloadan
operator inkremen postfiks (didefinisikan pada Gambar 3.7, baris 49-56) sedikit
lebih rumit. Untuk mengamati efek postinkremen, Anda harus mengetahui bahwa
nilai balik dari postinkremen adalah salinan tak-terinkremen dari objek Tanggal. Sebagai contoh, jika variabel int, x,
memiliki nilai 7, statemen
cout << x++
<< endl;
menampilkan nilai asli dari variabel x. Jadi, diinginkan bahwa operator
inkremen postfiks untuk bekerja dengan cara yang sama pada objek Tanggal. Pada statemen operator++, objek sekarang (*this) disimpan di dalam temp (baris 51). Selanjutnya, Anda
memanggil tolongInkremen untuk
menginkremen objek Tanggal sekarang.
Kemudian, baris 55 menghasilkan nilai balik berupa salinan dari objek yang
sebelumnya disimpan di dalam temp. Fungsi
ini tidak bisa menghasilkan nilai balik berupa sebuah referensi ke objek Tanggal lokal, temp, karena variabel lokal akan dihancurkan ketika fungsi, dimana
variabel tersebut dideklarasikan, telah selesai dieksekusi. Jadi,
pendeklarasian tipe nilai balik pada fungsi ini sebagai Tanggal & akan menghasilkan nilai balik berupa sebuah referensi
ke suatu objek yang sudah tidak ada lagi.
3.9 Manajemen Memori Dinamis
Struktur data array pada pustaka C++
standard memiliki ukuran tetap setelah array diciptakan. Ukuran dispesifikasi
dengan sebuah konstanta pada waktu kompilasi. Kadangkala adalah hal yang
berguna untuk menentukan ukuran sebuah array secara dinamis pada waktu eksekusi
dan kemudian menciptakan array yang diinginkan. C++ memampukan Anda untuk
mengendalikan alokasi dan dealokasi memori di dalam suatu program bagi objek
dan array bertipe fundamental atau bertipe terdefinisi oleh pengguna. Ini
dikenal dengan manajemen memori dinamis dan dilakukan menggunakan operator new dan delete.
Memperoleh
Memori Dinamis dengan new
Perhatikan statemen berikut:
Waktu *waktuPtr = new Waktu;
Operator new mengalokasikan memori penyimpanan untuk objek bertipe Waktu, memanggil konstruktor default
untuk menginisialisasi objek tersebut, dan menghasilkan nilai balik berupa
sebuah pointer yang menunjuk ke tipe yang dispesifikasi di sebelah kanan
operator new (Waktu *). Jika new tidak
bisa menemukan ruang memori yang cukup untuk objek, maka operator itu akan
mengindikasikan bahwa error telah terjadi dengan melemparkan sebuah eksepsi.
Membebaskan
Memori Dinamis dengan delete
Untuk menghancurkan objek yang
teralokasi secara dinamis dan membebaskan ruang memori untuk objek tersebut,
digunakan operator delete sebagai
berikut:
delete waktuPtr;
Statemen ini pertama-tama memanggil
destruktor untuk objek yang ditunjuk oleh waktuPtr,
kemudian membebaskan memori yang diasosiasikan dengan objek tersebut.
Menginisialisasi
Memori Dinamis
Anda dapat menyediakan sebuah
penginisialisasi untuk variabel bertipe fundamental yang baru saja diciptakan,
seperti
double *ptr = new double( 3.14159 );
yang menginisialisasi variabel double dengan 3.14159 dan menugaskan
pointer ptr untuk menunjuk ke
variabel tersebut. Sintaks yang sama dapat dipakai untuk menspesifikasi daftar
argumen yang dipisahkan koma pada konstruktor suatu objek. Sebagai contoh
Waktu *waktuPtr = new Waktu( 12, 45, 0 );
menginisialisasi objek Waktu yang baru diciptakan dengan nilai
12, 45, dan 0 dan menugaskan pointer waktuPtr
untuk menunjuk kepada objek tersebut.
Mengalokasikan
Array Secara Dinamis dengan new[ ]
Anda dapat juga menggunakan operator new untuk mengalokasikan array secara
dinamis. Sebagai contoh, sebuah array integer 10-elemen dapat dialokasikan dan
ditugaskan kepada arrayNilai sebagai
berikut:
int *arrayNilai[] = new int[ 10 ];
yang mendeklarasikan pointer arrayNilai untuk menunjuk variabel int dan menugaskan pointer tersebut
untuk menunjuk ke elemen pertama dari array integer 10-elemen.
Membebaskan
Array Secara Dinamis dengan delete[ ]
Untuk mendealokasi memori yang ditunjuk
oleh pointer arrayNilai, digunakan statemen
delete [] arrayNilai;
Jika pointer menunjuk ke array yang
memuat objek-objek, statemen tersebut pertama-tama memanggil destruktor untuk
setiap objek di dalam array, kemudian mendealokasi memori. Jika statemen
tersebut tidak mencantumkan [ ], maka hasilnya tidak bisa diketahui. Beberapa
kompiler memanggil destruktor hanya untuk objek pertama di dalam array.
Penggunaan delete pada pointer null
tidak akan berdampak apapun.
3.10 Studi Kasus: Kelas Array
Pada contoh ini, akan diciptakan sebuah
kelas Array yang dapat melakukan
pemeriksaan rentang untuk memastikan bahwa subskript masih berada di dalam
batas Array. Kelas ini mengijinkan
satu objek Array ditugaskan kepada
objek Array lainnya dengan operator
penugasan. Objek Array mengetahui
ukurannya, jadi ukuran tidak perlu dilewatkan secara terpisah kepada fungsi
yang menerima parameter Array. Objek Array dapat dimasukkan atau dikeluarkan
dengan operator ekstraksi aliran dan operator penyisipan aliran. Anda bisa
membandingkan objek Array menggunakan
operator ekualitas == dan !=. Ingat bahwa template kelas vector di dalam STL C++ menyediakan beberapa kapabilitas yang sama
dengan kelas Array ini.
3.10.1 Menggunakan Kelas Array
Program pada Gambar 3-9 sampai Gambar
3.11 mendemonstrasikan kelas Array
dan beberapa operator teroverloadnya. Pertama-tama Anda akan diajak untuk
mempelajari fungsi main pada Gambar
3.9 dan keluaran program, kemudian akan diberikan definisi kelas Array (Gambar 3.10) dan definisi setiap
fungsi anggota pada kelas tersebut.
Gambar 3.9 Program Menguji Kelas Array
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
|
// Gambar 3.9: gambar3_09.cpp
// Program menguji kelas Array.
#include <iostream>
#include "Array.h"
using namespace std;
int main()
{
Array integer1( 7 ); // Array
tujuh-elemen
Array integer2; // Array
10-elemen secara default
// menampilkan ukuran dan isi integer1
cout << "Ukuran Array integer1
adalah "
<< integer1.getUkuran()
<< "\nArray setelah
inisialisasi:\n" << integer1;
// menampilkan ukuran dan isi integer2
cout << "Ukuran Array integer2
adalah "
<< integer2.getUkuran()
<< "\nArray setelah
inisialisasi:\n" << integer2;
// memasukkan dan menampilkan integer1 dan
integer2
cout << "\nMasukkan 17
integer:" << endl;
cin >> integer1 >> integer2;
cout << "\nSetelah masukan,
Array memuat:\n"
<<
"integer1:\n" << integer1
<< "integer2:\n" << integer2;
// menggunakan operator inekualitas
teroverload (!=)
cout << "\nMengevaluasi:
integer1 != integer2" << endl;
if( integer1 != integer2 )
cout << "integer1 dan
integer2 tidak sama" << endl;
// menciptakan Array integer3 menggunakan
integer1 sebagai
// penginisialisasi; menampilkan ukuran dan
isinya
Array integer3( integer1 ); // memanggil konstruktor penyalin
cout << "\nUkuran Array
integer3 adalah "
<< integer3.getUkuran()
<< "\nArray setelah
inisialisasi:\n" << integer3;
// menggunakan operato penugasan (=)
teroverload
cout << "\nMenugaskan integer2
kepada integer1:" << endl;
integer1 = integer2; // perhatikan Array target lebih kecil
cout << "integer1:\n"
<< integer1
<< "integer2:\n" << integer2;
// menggunakan operator ekualitas (=)
teroverload
cout << "\nMengevaluasi:
integer1 == integer2" << endl;
if( integer1 == integer2 )
cout << "integer1 dan
integer2 sama" << endl;
// menggunakan operator subskrit untuk
menciptakan rvalue
cout << "\ninteger1[5] adalah
" << integer1[
5 ];
// menggunakan
operator subskript teroverload untuk menciptakan lvalue
cout << "\n\nMenugaskan 1000
kepada integer1[5]" << endl;
integer1[ 5 ] = 1000;
cout << "integer1:\n"
<< integer1;
// mencoba untuk menggunakan subskript di
luar rentang Array
try
{
cout << "\nMencoba
untuk menugaskan 1000 kepada integer1[15]" << endl;
integer1[ 15 ] = 1000; // ERROR: subskript di luar rentang
} // akhir dari try
catch ( out_of_range &ex )
{
cout << "Sebuah eksepsi
terjadi: " << ex.what() << endl;
} // akhir dari catch
} // akhir dari main
|
Ukuran Array integer1 adalah 7
Array setelah inisialisasi:
0 0 0 0
0 0 0
Ukuran Array integer2 adalah 10
Array setelah inisialisasi:
0 0 0 0
0 0 0 0
0 0
Masukkan 17 integer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Setelah masukan, Array memuat:
integer1:
1 2 3 4
5 6 7
integer2:
8 9 10 11
12 13 14 15
16 17
Mengevaluasi: integer1 != integer2
integer1 dan integer2 tidak sama
Ukuran Array integer3 adalah 7
Array setelah inisialisasi:
1 2 3 4
5 6 7
Menugaskan integer2 kepada integer1:
integer1:
8 9 10 11
12 13 14 15
16 17
integer2:
8 9 10 11
12 13 14 15
16 17
Mengevaluasi: integer1 == integer2
integer1 dan integer2 sama
integer1[15] adalah 13
Menugaskan 100 kepada integer1[5]
integer1:
8 9 10 11
12 1000 14 15
16 17
Mencoba menugaskan 1000 kepada integer1[15]
Sebuah eksepsi terjadi: Subscript out of range
Menciptakan
Array, Menampilkan Ukuran dan Isinya
Program dimulai dengan menginstansiasi
dua objek kelas Array: integer1
(Gambar 3.11, baris 9) dengan tujuh elemen dan integer2 (baris 10) dengan ukuran Array default 10 elemen (yang dispesifikasi oleh prototipe
konstruktor default Array pada Gambar
3.10 pada baris 14). Baris 13-15 menggunakan fungsi anggota getUkuran untuk menentukan ukuran integer1 kemudian menampilkan isi integer1, menggunakan operator
penyisipan aliran teroverload. Contoh
keluaran menegaskan bahwa elemen-elemen Array
ditetapkan menjadi nol oleh konstruktor. Berikutnya, baris 18-20 menampilkan
ukuran Array integer2 kemudian menampilkan isi integer2, menggunakan operator penyisipan teroverload.
Menggunakan
Operator Penyisipan Aliran Teroverload Untuk Mengisi Array
Baris 23 mendesak pengguna untuk
memasukkan 17 integer. Baris 24 menggunakan operator ekstraksi aliran teroverload untuk membaca tujuh nilai pertama
ke dalam integer1 dan 10 nilai
lainnya ke dalam integer2. Baris
26-28 menampilkan dua array menggunakan operator penyisipan teroverload untuk menegaskan bahwa masukan
dilakukan secara benar.
Menggunakan
Operator Operator Inekualitas Teroverload
Baris 33 menguji operator inekualitas
teroverload dengan mengevaluasi
kondisi
integer1 !=
integer2
Program menampilkan bahwa kedua objek Array tidak sama.
Menginisialisasi
Array Baru dengan Salinan dari Isi Array yang sudah ada
Baris 38 menginstansiasi objek Array ketiga, integer3, dan menginisialisasinya dengan salinan dari objek Array integer1. Ini menyebabkan pemanggilan konstruktor penyalin pada
kelas Array untuk menyalin
elemen-elemen dari integer1 ke dalam integer3. Konstruktor penyalin dapat
pula dipanggil dengan menulis baris 38 sebagai berikut:
Array integer3 =
integer1;
Tanda sama dengan di dalam statemen
tersebut bukanlah operator penugasan. Ketika tanda sama dengan muncul di dalam
deklarasi sebuah objek, maka hal itu akan menyebabkan pemanggilan suatu
konstruktor untuk objek tersebut. Format ini dapat dipakai untuk melewatkan
satu argumen kepada sebuah konstruktor, khususnya nilai pada sisi kanan simbol
=.
Baris 40-42 menampilkan ukuran integer3 kemudian menampilkan isi integer3, menggunakan operator
penyisipan aliran teroverload untuk
memastikan bahwa elemen-elemen integer3
telah ditetapkan secara benar menggunakan konstruktor penyalin.
Menggunakan
Operator Penugasan Teroverload
Baris 46 menguji operator penugasan teroverload (=) dengan menugaskan integer2 kepada integer1. Baris 48-49 menampilkan kedua isi objek Array untuk memastikan bahwa penugasan
telah berhasil dilakukan. Objek Array
integer1 awalnya memuat 7 integer,
tetapi ukurannya diperbesar untuk menampung salinan dari integer2 10 elemen. Seperti yang akan Anda lihat, operator
penugasan teroverload melakukan
operasi pengubahan ukuran dengan cara yang transparan bagi kode klien.
Menggunakan
Operator Ekualitas Teroverload
Baris 54 menggunakan operator ekualitas
teroverload (==) untuk memastikan
bahwa objek integer1 dan integer2 identik satu sama lain setelah
penugasan pada baris 46.
Menggunakan
Operator Subskript Teroverload
Baris 58 menggunakan operator subskript
teroverload untuk merujuk ke integer1[5], sebuah elemen integer1 yang masih berada di dalam
rentang yang diijinkan. Nama dengan subskript ini digunakan sebagai lvalue untuk menampilkan nilai yang
disimpan di dalam integer1[5]. Baris
62 menggunakan integer[5] sebagai lvalue yang dapat dimodifikasi pada sisi
kiri sebuah statemen penugasan untuk menugaskan suatu nilai baru, 100, kepada
elemen 5 dari integer1. Anda akan
melihat bahwa operator[ ]
menghasilkan nilai balik berupa sebuah referensi untuk menggunakan lvalue yang dapat dimodifikasi setelah
operator memastikan bahwa 5 merupakan subskript valid untuk integer1. Baris 69 mencoba untuk
menugaskan nilai 1000 kepada integer1[15], sebuah elemen di luar rentang yang
diijinkan. Pada contoh ini, operator[ ]
menentukan bahwa subskript tersebut berada di luar rentang dan melempatkan
sebuah eksepsi out_of_range.
3.10.2 Definisi Kelas Array
Sekarang karena Anda telah mengerti
bagaimana program ini beroperasi, Anda akan diajak untuk menengok header kelas
Array pada Gambar 3.10. Untuk setiap fungsi anggota di dalam header,
implementasinya diberikan pada Gambar 3.1. Pada Gambar 3.10, baris 34-35
merepresentasikan anggota data private
dari kelas Array. Setiap objek Array memuat anggota ukuran yang mengindikasikan jumlah
elemen di dalam Array dan sebuah
pointer int yang menunjuk ke array
integer berbasis pointer (yang dialokasikan secara dinamis).
Gambar 3.10 Definisi Kelas Array
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
|
// Gambar 3.10: Array.h
// Definisi kelas Array dengan beberapa
operator teroverload.
#ifndef ARRAY_H
#define ARRAY_H
#include <iostream>
using namespace std;
class Array
{
friend
ostream &operator<<( ostream &, const Array & );
friend
istream &operator>>( istream &, Array & );
public:
Array(
int = 10 ); // konstruktor default
Array(
const Array & ); // konstruktor penyalin
~Array();
// destruktor
int
getUkuran() const; // menghasilkan ukuran
const
Array &operator=( const Array & ); // operator penugasan
bool
operator==( const Array & ) const; // operator ekualitas
// operator inekualitas; menghasilkan kebalikan dari
operator ==
bool operator!=( const
Array &right ) const
{
return
! ( *this == right ); // memanggil Array::operator==
} //
akhir dari fungsi operator!=
// operator subskript untuk objek non-const menghasilkan
lvalue yang dapat dimodifikasi
int &operator[]( int
);
//
operator subskript untuk objek const yang menghasilkan rvalue
int operator[]( int ) const;
private:
int ukuran; // ukuran array berbasis
pointer
int *ptr; // pointer ke elemen
pertama dari array berbasis pointer
}; //
akhir dari kelas Array
#endif
|
Gambar 3.11 Definisi Fungsi Anggota dan friend
Kelas Array
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
// Gambar 3.11: Array.cpp
// Definisi fungi anggota dan friend kelas
Array.
#include <iostream>
#include <iomanip>
#include <cstdlib> // prototipe fungsi exit
#include "Array.h" // definisi kelas
Array
using namespace std;
// konstruktor default untuk kelas Array
(ukuran default 10)
Array::Array( int ukuranArray )
{
// memvalidasi ukuranArray
if ( ukuranArray > 0 )
ukuran = ukuranArray;
else
throw invalid_argument( "Ukuran
Array harus lebih dari 0" );
ptr = new int[ ukuran ]; // menciptakan ruang untuk array berbasis pointer
for ( int i = 0; i <
ukuran; ++i )
ptr[ i ] = 0; // menetapkan elemen array
berbasis pointer
} // akhir dari konstruktor default Array
// konstruktor penyalin untuk kelas Array;
// harus menerima sebuah referensi untuk
mencegah rekursi tak-berhingga
Array::Array( const Array
&arrayDisalin )
: ukuran( arrayDisalin.ukuran )
{
ptr = new int[ ukuran ]; // menciptakan ruang untuk array berbasis pointer
for ( int i = 0; i <
ukuran; ++i )
ptr[ i ] = arrayDisalin.ptr[ i ]; //
menyalin ke objek
} // akhir dari konstruktor penyalian Array
// destruktor untuk kelas Array
Array::~Array()
{
delete [] ptr; // membebaskan ruang
array berbasis pointer
} // akhir dari destruktor
// menghasilkan jumlah elemen Array
int Array::getUkuran() const
{
return ukuran; // jumlah elemen Array
} // akhir dari fungsi getUkuran
// operator penugasan teroverload;
// menghindari: ( a1 = a2 ) = a3
const Array &Array::operator=( const Array
&kanan )
{
if ( &kanan != this ) //
menghindari penugasan-diri
{
// untuk Array berbeda ukuran, dealokasi
array sisi-kiri awal
// kemudian alokasikan array sisi-kiri baru
if ( ukuran != kanan.ukuran )
{
delete [] ptr; // membebaskan
memori
ukuran = kanan.ukuran; // mengubah
ukuran objek ini
ptr = new int[ ukuran ]; // menciptakan spasi untuk menyalin array
} // akhir dari if sebelah dalam
for ( int i = 0; i <
ukuran; ++i )
ptr[ i ] = kanan.ptr[ i ]; // menyalin
array ke dalam objek
} // akhir dari if sebelah luar
return *this; // memampukan x = y =
z,
} // akhir dari fungsi operator=
// menentukan jika dua Array sama dan
// menghasilkan true, sebaliknya
menghasilkan false
bool Array::operator==( const Array &kanan
) const
{
if ( ukuran != kanan.ukuran )
return false; // arrays
dengan berbeda jumlah elemen
for ( int i = 0; i <
ukuran; ++i )
if ( ptr[ i ] != kanan.ptr[ i ] )
return false; // Isi Array tidak
sama
return true; // Array sama
} // akhir dari fungsi operator==
// operator subskript teroverload untuk
Array non-const;
// referensi yang
dijadikan nilai balik menciptakan lvalue yang dapat dimodifikasi
int &Array::operator[]( int subskript )
{
// memeriksa apakah subskript menyebabkan
error out-of-range
if ( subskript < 0 || subskript
>= ukuran )
throw out_of_range( "Subskript
di luar rentang" );
return ptr[ subskript ]; // nilai
balik referensi
} // akhir dari fungsi operator[]
// operator subskript teroverload untuk
Array const
// referensi const
yang dijadikan nilai balik menciptakan rvalue
int Array::operator[]( int subskript ) const
{
// memeriksa apakah subskript menyebabkan
error out-of-range
if ( subskript < 0 || subskript
>= ukuran )
throw out_of_range( "Subskript
di luar rentang" );
return ptr[ subskript ]; //
menghasilkan salinan dari elemen ini
} // akhir dari fungsi operator[]
// operator masukan teroverload untuk kelas
Array;
// memasukkan nilai-nilai untuk Array
istream &operator>>(
istream &masukan, Array &a )
{
for ( int i = 0; i <
a.ukuran; ++i )
masukan >> a.ptr[ i ];
return masukan; // memampukan cin
>> x >> y;
} // akhir dari fungsi
// operator keluaran teroverload untuk kelas
Array
ostream &operator<<(
ostream &keluaran, const Array &a )
{
int i;
// menampilkan array berbasis pointer
private
for ( i = 0; i < a.ukuran; ++i )
{
keluaran << setw( 12 ) <<
a.ptr[ i ];
if ( ( i + 1 ) % 4 == 0 ) // 4
angka per baris keluaran
keluaran << endl;
} // akhir dari for
if ( i % 4 != 0 ) // akhir dari baris
terakhir pada keluaran
keluaran << endl;
return keluaran; // memampukan cout
<< x << y;
} // akhir dari fungsi operator<<
|
Mengoverload
Operator Penyisipan Aliran dan Operator Ekstraksi Aliran sebagai
friend
Baris 11-12 pada Gambar 3.10
mendeklarasikan operator penyisipan aliran teroverload dan operator ekstraksi aliran teroverload sebagai friend
untuk kelas Array. Ketika kompiler
melihat sebuah ekspresi seperti cout
<< objekArray, maka ia akan memanggil fungsi non-anggota operator<< dengan
operator<<( cout,
objekArray )
Ketika kompiler melihat ekspresi
seperti cin >> objekArray, maka
ia memanggil fungsi non-anggota operator>>
dengan pemanggilan
operator>>( cin,
objekArray )
Lagi, fungsi operator penyisipan aliran
dan fungsi operator ekstraksi aliran tidak bisa dijadikan anggota kelas Array, karena objek Array selalu ditempatkan di sebelah kanan operator.
Fungsi operator<< (didefinisikan pada Gambar 3.11, baris 116-133)
menampilkan sejumlah elemen yang diindikasikan oleh ukuran dari array integer yang ditunjuk oleh pointer ptr. Fungsi operator>> (didefinisikan pada Gambar 3.11, baris 107-113)
memasukkan secara langsung ke dalam array yang ditunjuk oleh ptr. Setiap fungsi operator ini
menghasilkan nilai balik berupa sebuah referensi yang memampukan masukan atau
keluaran bertingkat atau kaskade. Kedua fungsi dapat mengakses data private kelas Array karena dideklarasikan sebagai friend dari kelas Array.
Anda dapat juga menggunakan fungsi getUkuran
dan operator[ ] di dalam tubuh operator<< dan operator>>.
Konstruktor
Default Kelas Array
Baris 14 pada Gambar 3.10
mendeklarasikan konstruktor default bagi kelas Array dan menspesifikasikan ukuran default sebanyak 10 elemen.
Ketika kompiler melihat deklarasi seperti baris 10 pada Gambar 3.9, ia akan
memanggila konstruktor default kelas Array
untuk menetapkan ukuran Array menjadi
10 elemen. Konstruktor default (didefinisikan pada Gambar 3.11, baris 10-22)
memvalidasi dan menugaskan argumen kepada anggota data ukuran, menggunakan new
untuk mendapatkan memori untuk array berbasis pointer, dan menugaskan pointer
yang dijadikan nilai balik oleh new
untuk menunjuk ke anggota data ptr.
Kemudian konstruktor menggunakan statemen for
untuk menetapkan semua elemen array menjadi nol.
Konstruktor
Penyalin Kelas Array
Baris 15 pada Gambar 3.10
mendeklarasikan sebuah konstruktor penyalin (didefinisikan pada Gambar 3.11,
baris 26-23) yang menginisialisasi sebuah Array dengan cara membuat salinan
dari objek Array yang sudah ada.
Penyalinan semacam itu harus dilakukan secara hati-hati untuk menghindari
kesalahan membiarkan dua objek Array
menempati memeri yang sama. Konstruktor penyalin dipanggil kapansaja salinan
dari sebuah objek dibutuhkan, seperti
·
pelewatan sebuah objek dengan nilai kepada suatu fungsi
·
pengembalian nilai balik berupa sebuah objek lewat nilai dari suatu
fungsi
·
penginisialisasian sebuah objek dengan salinan dari objek lain sesama
kelas.
Konstruktor penyalin dipanggil di dalam
suatu deklarasi ketika sebuah objek kelas Array
diinstansiasi dan diinisialisasi dengan objek lain dari kelas Array, seperti di dalam deklarasi pada
baris 38 pada Gambar 3.9.
Konstruktor penyalin bagi kelas Array menggunakan penginisialisasi
anggota (Gambar 3.11, baris 27) untuk menyalin ukuran pada penginisialisasi Array
ke dalam anggota data ukuran,
menggunakan new (baris 29) untuk
mendapatkan memori bagi representasi berbasis pointer dari Array ini, dan menugaskan pointer yang dijadikan nilai balik oleh new kepada anggota data ptr. Kemudian konstruktor penyalin
menggunakan statemen for untuk
menyalin semua elemen dari penginisialisasi Array
ke dalam objek Array yang baru. Objek
suatu kelas dapat menggunakan data private
dari sembarang objek lain dari kelas tersebut.
Destruktor Kelas
Array
Baris 16 pada Gambar 3.10 mendeklarasikan
destruktor kelas Array (didefinisikan
pada Gambar 3.11, baris 36-39). Destruktor dipanggil ketika sebuah objek kelas Array keluar dari skop (sudah tidak
eksis). Destruktor menggunakan delete[
] untuk membebaskan memori yang dialokasikan secara dinamis oleh new di dalam konstruktor.
Fungsi Anggota
getUkuran
Baris 17 pada Gambar 3.10
mendeklarasikan fungsi getUkuran
(didefinisikan pada Gambar 3.11, baris 42-45) yang menjadikan jumlah elemen di
dalam Array sebagai nilai balik.
Operator
Penugasan Teroverload
Baris 19 pada Gambar 3.10
mendeklarasikan fungsi operator penugasan teroverload untuk kelas Array.
Ketika kompiler melihat ekspresi integer1
= integer2 pada baris 46 pada Gambar 3.9, kompiler memanggil fungsi anggota
operator= dengan pemanggilan
integer1.operator=( integer2 )
Implementasi fungsi anggota operator= (Gambar 3.11, baris 49-67)
menguji untuk penugasan-diri (baris 51) dimana di dalamnya sebuah objek Array ditugaskan kepada dirinya sendiri.
Ketika this sama dengan alamat operand kanan,
penugasan diri akan coba dilakukan, sehingga penugasan semacam ini akan
dilompati. Jika bukan penugasan-diri, maka fungsi menentukan apakah ukuran dua
array identik atau tidak (baris 55); pada kasus itu, array integer asli pada
sisi kiri tidak dialokasikan-ulang; sebaliknya, operator= menggunakan delete
(baris 57) untuk membebaskan memori yang semula dialokasikan untuk array
target, menyalin ukuran dari array
sumber ke dalam ukuran pada array
target (baris 58), menggunakan new
untuk mengalokasikan memori untuk array target, dan menempatkan pointer yang
dijadikan nilai balik oleh new kepada
anggota ptr. Kemudian statemen for pada baris 62-63 menyalin
elemen-elemen array dari array sumber ke dalam array target.
Operator
Ekualitas dan Operator Inekualitas Teroverload
Baris 20 pada Gambar 3.10
mendeklarasikan operator ekualitas teroverload
(==) untuk kelas Array. Ketika
kompiler melihat ekspresi integer1 ==
integer2 pada baris 54 pada Gambar 3.9, kompiler memanggil fungsi anggota operator== dengan pemanggilan
integer1.operator==( integer2 )
Fungsi anggota operator== (didefinisikan pada Gambar 3.11, baris 71-81)
menghasilkan nilai balik false jika
anggota ukuran tidak sama.
Sebaliknya, operator== membandingkan
setiap pasangan elemen. Jika setiap pasangan elemen sama, fungsi ini
menghasilkan true. Pasangan pertama
yang berbeda akan menyebabkan fungsi menghasilkan false dengan segera.
Baris
23-26 pada header mendefinisikan operator inekualitas (!=) untuk kelas Array. Fungsi anggota operator!= menggunakan fungsi operator== teroverload untuk menentukan apakah satu Array sama dengan Array
lainnya, kemudian menghasilkan kebalikan dari hasil yang didapatkan (dari operator==). Penulisan operator!= dengan cara ini memampukan
Anda untuk mendaur-ulang operator==,
yang dapat mereduksi jumlah kode yang harus ditulis di dalam kelas. Juga,
definisi fungsi utuh untuk operator!=
berada di dalam header Array. Hal ini
memampukan kompiler untuk memberikan definisi inline untuk operator!=.
Operator
Subskript Teroverload
Baris 29 dan 32 pada Gambar 3.10
mendeklarasikan dua operator subskript teroverload
(didefinisikan pada Gambar 3.11, baris 85-92 dan baris 95-103). Ketika
kompiler melihat ekspresi integer1[5]
(Gambar 3.9, baris 58), ia memanggil fungsi anggota operator[ ] teroverload
yang sesuai dengan membangkitkan pemanggilan
integer1.operator[]( 5 )
Kompiler akan menciptakan pemanggilan
terhadap versi const dari operator[ ] (Gambar 3.11, baris 95-103)
ketika operator subskript digunakan pada objek const Array. Sebagai contoh, jika objek const, z, diinstansiasi dengan statemen
const Array z( 5 );
maka versi const dari operator[ ]
diperlukan untuk mengeksekusi statemen seperti
cout << z[ 3 ] << endl;
Ingat, program hanya dapat memanggil
fungsi anggota const dari suatu objek
const. Setiap definisi dari operator[ ] menentukan apakah subskript
yang diterimanya sebagai argumen berada di dalam rentang atau tidak. Jika
tidak, maka fungsi ini akan melemparkan eksepsi out_of_range. Jika subskript berada di dalam rentang, versi non-const dari operator[ ] akan menghasilkan nilai balik berupa elemen array
sesuai (sebagai referensi) yang dapat digunakan sebagai lvalue yang bisa dimodifikasi (sisi kiri suatu statemen penugasan).
Jika subskript berada di dalam rentang, versi const dari operator[ ]
menghasilkan nilai balik berupa salinan dari elemen sesuai di dalam array.
Karakter yang dijadikan nilai balik adalah sebuah rvalue.
Kesimpulan
Anda dapat mengoverload
kebanyakan operator agar dapat digunakan dengan objek kelas. Kompiler akan
membangkitkan kode berdasarkan tipe operand.
Fungsi empty menentukan apakah
sebuah objek string kosong atau
tidak, fungsi substr menghasilkan
sebuah objek string yang
merepresentasikan sepenggal string yang sudah ada sebagai nilai balik, dan
fungsi at menghasilkan nilai balik
berupa karakter pada indeks tertentu di dalam sebuah objek string (setelah memeriksa apakah indeks berada di dalam rentang
atau tidak).
Overloading operator tidak otomatis, karena Anda
harus menulis fungsi overloading
operator untuk melakukan operasi yang diinginkan. Sebuah operator dioverload dengan menulis definisi fungsi
anggota non-static atau definisi
fungsi non-anggota, dimana nama fungsi harus diawali dengan katakunci operator yang diikuti dengan operator
yang sedang dioverload.
Anda dapat memasukkan dan menampilkan data bertipe fundamental menggunakan
operator ekstraksi aliran >> dan operator penyisipan aliran <<.
Pustaka kelas C++ mengoverload kedua
operator tersebut untuk semua data bertipe fundamental, termasuk pointer dan
string char *. Anda juga dapat mengoverload kedua operator itu untuk
melakukan pemasukan dan pengeluaran tipe data buatan sendiri.
Operator unary suatu kelas dapat dioverload
sebagai fungsi anggota non-static
dengan tanpa argumen atau sebagai fungsi non-anggota dengan satu argumen (yang
harus berupa objek kelas atau referensi ke sebuah objek kelas). Fungsi anggota
yang mengimplementasikan operator teroverload
harus dideklarasikan non-static
sehingga ia dapat mengakses data non-static
di dalam setiap objek kelas.
Untuk mengoverload operator
inkremen prefiks dan postfiks, setiap fungsi operator teroverload harus memiliki sidik atau tanda-tangan yang berbeda,
sehingga kompiler dapat menentukan versi mana dari ++ yang diinginkan. Versi
prefiks dioverload sama persis dengan
sembarang operator unary prefiks lainnya.
C++ memampukan Anda untuk mengendalikan alokasi dan dealokasi memori di
dalam suatu program bagi objek dan array bertipe fundamental atau bertipe
terdefinisi oleh pengguna. Ini dikenal dengan manajemen memori dinamis dan
dilakukan menggunakan operator new
dan delete.
Latihan
1)
Ciptakanlah sebuah kelas BilanganRasional (bilangan pecahan)
dengan beberapa kapabilitas sebagai berikut:
a)
Ciptakanlah sebuah konstruktor
yang mencegah denominator bernilai 0 di dalam sebuah pecahan, yang mereduksi
atau menyederhanakan pecahan yang tidak dalam format tereduksi (misalnya, 6/12 menjadi format tereduksi 1/2 atau 5/15 menjadi format tereduksi 1/3), dan
yang menghindari denominator negatif.
b)
Lakukan overloading terhadap operator penjumlahan, pengurangan, perkalian,
dan pembagian untuk kelas ini.
c)
Lakukan operator relasional dan
ekualitas untuk kelas ini.
2)
Bangunlah kelas Polinomial. Representasi internal atas
sebuah Polinomial adalah array yang
memuat suku-suku polinomial. Setiap suku memuat koefisien dan koefisien,
misalnya suku
memuat koefisien 2 dan eksponen 4.
Kembangkanlah kelas tersebut dengan utuh yang memuat konstruktor dan destruktor
yang dibutuhkan begitu juga dengan beberapa fungsi get dan set. Kelas Polinomial harus menyediakan beberapa
kapabilitas operator teroverload
sebagai berikut:
a)
Lakukan overloading terhadap operator penjumlahan (+) untuk menjumlahkan
dua Polinomial.
b)
Lakukan overloading terhadap operator pengurangan (-) untuk mengurangkan
dua Polinomial.
c)
Lakukan overloading terhadap operator penugasan untuk menugaskan satu Polinomial kepada objek Polinomial lain.
d)
Lakukan overloading terhadap operator perkalian (*) untuk mengalikan dua Polinomial.
e)
Lakukan overloading terhadap operator penugasan penjumlahan (+=), operator
penugasan pengurangan (-=), dan operator penugasan perkalian (*=).
No comments:
Post a Comment