Friday, December 23, 2016

Bab 3. C++ Untuk Programer


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