Friday, December 23, 2016

Bab 1. C++ Untuk Programer



Bab. 1 Kelas Bagian 1

Tujuan Instruksional
·         Studi kasus kelas Waktu.
·         Skop kelas dan mengakses anggota kelas.
·         Fungsi akses dan fungsi utilitas.
·         Konstruktor dengan argumen default.
·         Destruktor.
·         Kapan konstruktor dan destruktor dipanggil.
·         Mengembalikan sebuah referensi ke suatu anggota data private.





1.1 Introduksi

Pada bab ini, akan didiskusikan tentang kelas. Akan digunakan kelas Waktu pada bab ini dan bab berikutnya. Kelas Waktu akan menyajikan beberapa fitur penting tentang pemrograman berorientasi objek. Contoh ini juga mendemonstrasikan konsep rekayasa perangkat-lunak C++ yang penting.

Selanjutnya, akan didiskusikan skop kelas dan relasi antar anggota kelas. Akan didemonstrasikan bagaimana kode klien dapat mengakses anggota public suatu kelas melalui tiga cara: lewat nama objek, lewat referensi ke sebuah objek, atau lewat pointer yang menunjuk ke suatu objek. Seperti yang akan Anda lihat, nama dan referensi dapat digunakan dengan operator penyeleksi anggota dot (.) untuk mengakses anggota public, dan pointer dapat digunakan dengan operator penyeleksi anggota tanda panah (->).

Selanjutnya akan dipelajari tentang fungsi akses yang dapat membaca atau menampilkan data di dalam sebuah objek. Kegunaan umum dari fungsi akses adalah untuk menguji kebenaran atau ketidak-benaran kondisi. Fungsi semacam itu dikenal dengan fungsi predikat. Akan didemonstrasikan juga fungsi utilitas (yang dikenal juga dengan fungsi pembantu), yaitu sebuah fungsi anggota private yang mendukung operasi dari fungsi anggota public suatu kelas, tetapi fungsi pembantu ini tidak dimaksudkan untuk digunakan oleh klien atau pengguna kelas.

Pada kelas Waktu kedua, akan didemonstrasikan bagaimana melewatkan argumen kepada konstruktor dan ditunjukkan bagaimana argumen default dapat digunakan di dalam suatu konstruktor untuk memampukan klien dalam menginisialisasi objek menggunakan berbagai argumen. Selanjutnya, akan didiskusikan fungsi anggota spesial yang dinamakan destruktor, yang merupakan bagian dari kelas, dan digunakan untuk melakukan “penghentian” dan “operasi bersih-bersih” pada sebuah objek sebelum objek tersebut dihancurkan. Kemudian akan didemonstrasikan urutan pemanggilan konstruktor dan destruktor, karena keberhasilan program Anda bergantung pada penggunaan objek-objek terinisialisasi yang belum dihancurkan.

Contoh terakhir dari studi kasus Waktu pada bab ini menunjukkan sebuah praktek pemrograman berbahaya dimana di dalamnya sebuah fungsi anggota menghasilkan nilai balik berupa sebuah referensi ke data private. Akan didiskusikan bagaimana hal ini merusak enkapsulasi suatu kelas dan mengijinkan kode klien untuk secara langsung mengakses data di dalam objek. Contoh terakhir ini menunjukkan bahwa objek sesama kelas dapat ditugaskan dari satu objek ke objek lainnya menggunakan penugasan keanggotaan default, yang menyalin anggota-anggota data di dalam objek di sisi kanan penugasan ke dalam anggota-anggota data di dalam objek di sisi kiri penugasan.

1.2 Studi Kasus Kelas Waktu
Contoh pertama yang disajikan (Gambar 1.1 – 1.3) menciptakan kelas Waktu dan sebuah program untuk menguji kelas tersebut. Akan didemonstrasikan konsep penting rekayasa perangkat-lunak C++, menggunakan preprosesor di dalam header. Karena kelas didefinisikan hanya sekali, penggunaan preprosesor semacam itu mencegah error pendefinisian-jamak.

Gambar 1.1 Definisi Kelas Waktu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Gambar 1.1:  Waktu.h
// Definisi kelas Waktu.
// Fungsi anggota didefinisikan di dalam Waktu.cpp

// mencegah penyertaan jamak header
#ifndef WAKTU_H
#define WAKTU_H

// Definisi kelas Waktu
class Waktu
{
public:
  Waktu(); // konstruktor
  void setWaktu( int, int, int ); // menetapkan jam, menit, dan detik
  void tampilUniversal(); // menampilkan waktu dalam format universal
  void tampilStandard(); // menampilkan waktu dalam format standard
private:
  int jam; // 0 - 23 (format 24-jam)
  int menit; // 0 - 59
  int detik; // 0 - 59
}; // akhir dari kelas Waktu

#endif

Definisi Kelas Waktu
Definisi kelas (Gambar 1.1) memuat beberapa prototipe (baris 13-16) untuk fungsi anggota kelas Waktu, yaitu setWaktu, tampilUniversal, dan tampilStandard. Definisi kelas ini juga menyertakan anggota integer private, yaitu jam, menit, dan detik. Anggota data private kelas Waktu hanya dapat diakses oleh keempat fungsi anggota public-nya.

Pada Gambar 1.1, definisi kelas diapit di dalam pembungkus preprosesor berikut (baris 6, 7, dan 32):

#ifndef WAKTU_H
#define WAKTU _H
...
#endif

Ketika Anda membangun program yang lebih besar, definisi dan deklarasi lain juga ditempatkan di dalam header. Pembungkus preprosesor tersebut mencegah kode yang berada di antara #ifndef (“if not defined”) dan #endif untuk disertakan jika nama WAKTU_H telah didefinisikan. Jika header tersebut tidak disertakan sebelumnya di dalam sebuah file, maka nama WAKTU_H didefinisikan menggunakan direktif #define dan statemen header dicantumkan.

Fungsi Anggota Kelas Waktu
Pada Gambar 1.2, konstruktor Waktu (baris 11-14) menginisialisasi anggota-anggota data dengan 0. Nilai tak-valid tidak bisa disimpan di dalam anggota data objek Waktu, karena konstruktor dipanggil ketika objek Waktu diciptakan, dan semua usaha oleh klien untuk memodifikasi anggota data akan diperiksa oleh fungsi setWaktu (sebentar lagi akan didiskusikan). Adalah hal penting untuk mengetahui bahwa Anda dapat mendefinisikan beberapa konstruktor teroverload untuk suatu kelas.

Gambar 1.2 Definisi Fungsi Anggota untuk Kelas Waktu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Gambar. 1.2: Waktu.cpp
// Definisi fungsi anggota untuk kelas Waktu.
#include <iostream>
#include <iomanip>
#include <stdexcept> // untuk kelas eksepsi invalid_argument
#include "Waktu.h" // menyertakan definisi kelas Waktu dari Waktu.h

using namespace std;

// konstruktor Waktu menginisialisasi setiap anggota data dengan nol.
Waktu::Waktu()
{
  jam = menit = detik = 0;
} // akhir dari konstruktor Waktu

// menetapkan Waktu baru menggunakan waktu universal
void Waktu::setWaktu( int h, int m, int s )
{
  // memvalidasi jam, menit, dan detik
  if ( ( h >= 0 && h < 24 ) && ( m >= 0 && m < 60 ) &&
     ( s >= 0 && s < 60 ) )
  {
    jam = h;
    menit = m;
    detik = s;
  } // akhir dari if
  else
    throw invalid_argument(
      "jam, menit dan/atau detik di luar rentang" );
} // akhir dari fungsi setWaktu

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

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

Kecuali anggota data static const int, anggota data suatu kelas tidak bisa diinisialisasi dimana anggota tersebut dideklarasikan di dalam tubuh kelas. Meskipun hal itu sebenarnya diijinkan, tetapi sangat direkomendasikan bahwa anggota data diinisialiasi oleh konstruktor kelas. Pada kasus ini, anggota data juga dapat ditugasi nilai oleh fungsi setWaktu.

Fungsi Anggot setWaktu dan Pelemparan Eksepsi
Fungsi setWaktu (baris 17-30) merupakan sebuah fungsi public yang mendeklarasikan tiga parameter int dan menggunakannya untuk menetapkan waktu. Baris 20-21 menguji setiap argumen untuk menentukan apakah setiap nilai berada di dalam rentang atau tidak, dan, jika tidak, baris 23-25 menugaskan nilai-nilai tersebut kepada anggota data jam, menit, dan detik. Nilai jam harus lebih dari atau sama dengan 0 dan kurang dari 24, karena format waktu universal merepresentasikan jam sebagai integer dari 0 sampai 23 (misalnya 1 PM adalah jam 13 dan 11 PM adalah jam 23; tengah malam adalah jam 0). Kedua menit dan detik harus lebih dari atau sama dengan 0 dan kurang dari 60. Untuk nilai-nilai yang berada di luar rentang tersebut, setWaktu akan melemparkan sebuah eksepsi bertipe invalid_argument (baris 28-29), yang akan memberitahu kode klien bahwa argumen tak-valid telah diterima. Anda bisa menggunakan try...catch untuk menangkap eksepsi dan mencoba memulihkan kesalahan tersebut, yang akan dilakukan pada Gambar 1.3. Statemen throw (baris 28-29) menciptakan sebuah objek baru bertipe invalid_argument. Kurung yang mengikuti nama kelas mengindikasikan pemanggilan terhadap konstruktor invalid_argument yang mengijinkan Anda untuk menspesifikasi string pesan error. Setelah objek eksepsi diciptakan, statemen throw menghentikan fungsi setWaktu dan eksepsi dikembalikan kepada kode yang mencoba menetapkan waktu (yang tak-valid) tersebut.

Fungsi Anggot tampilUniversal
Fungsi tampilUniversal (baris 33-37 pada Gambar 1.2) tidak mengambil argumen apapun dan menampilkan waktu dalam format universal, yang memuat tiga pasang dijit yang dipisahkan oleh titik-dua untuk jam, menit, dan detik. Sebagai contoh, jika waktu adalah 1:30:07 PM, fungsi tampilUniversal akan menghasilkan 13:30:07. Baris 35 menggunakan manipulator aliran terparameterisasi setfill untuk menspesifikasi karakter yang ditampilkan ketika sebuah integer ditampilkan pada suatu bidang yang lebih lebar dari jumlah dijit di dalam nilai. Secara default, karakter pengisi disisipkan di sisi kiri dijit. Pada contoh ini, jika menit adalah 2, maka akan ditampilkan sebagai 02, karena karakter pengisi ditetapkan sebagai 0 (‘0’). Jika angka yang ditampilkan memenuhi bidang yang dispesifikasi, maka karakter pengisi tidak akan ditampilkan. Begitu karakter pengisi dispesifikasi dengan setfill, maka hal itu berlaku untuk semua nilai yang akan ditampilkan pada bidang yang lebih lebar dari nilai tersebut. Hal ini berlawanan dengan setw, yang hanya berlaku pada nilai berikutnya yang akan ditampilkan.

Fungsi Anggot tampilStandard
Fungsi tampilStandard (baris 40-45) tidak mengambil argumen apapun dan menampilkan waktu dalam format standard, yang memuat jam, menit, dan detik yang dipisahkan oleh titik-dua dan diikuti oleh indikator AM atau PM (misalnya, 1:27:06 PM). Seperti fungsi tampilUniversal, fungsi tampilStandard menggunakan setfill(‘0’) untuk memformat menit dan detik sebagai dua dijit nilai dengan kepala nol jika diperlukan. Baris 42 menggunakan operator kondisional (?:) untuk menentukan nilai jam yang akan ditampilkan, jika jam bernilai 0 atau 12 (AM atau PM), maka jam akan ditampilkan sebagai 12; sebaliknya, jam akan ditampilkan sebagai suatu nilai dari 1 sampai 11. Operator kondisional pada baris 44 menentukan apakah AM atau PM yang akan ditampilkan.

Mendefinisikan Fungsi Anggota di luar Definisi Kelas
Meskipun sebuah fungsi anggota yang dideklarasikan di dalam definisi kelas dapat didefinisikan di luar definisi kelas tersebut, fungsi anggota tersebut masih berada di dalam skop kelas. Namanya dikenal oleh anggota-anggota lain di dalam kelas tersebut. Jika sebuah fungsi anggota didefinisikan di dalam tubuh suatu definisi kelas, maka ia secara implisit dideklarasikan inline.

Menggunakan Kelas Waktu
Setelah didefinisikan, Waktu dapat dipakai sebagai sebuah tipe di dalam deklarasi sebagai berikut:

Time pagi; // objek bertipe Waktu
Time arrayWaktu[ 5 ]; // array yang memuat 5 objek Waktu
Time &sarapan = pagi; // referensi ke sebuah objek Waktu
Time *waktuPtr = &sarapan; // pointer yang menunjuk ke sebuah objek Waktu

Gambar 1.3 menggunakan kelas Waktu. Baris 10 menginstansiasi sebuah objek Waktu yang dinamakan t. Ketika objek tersebut diinstansiasi, konstruktor Waku dipanggil untuk menginisialisasi setiap anggota data private dengan 0. Kemudian baris 14 dan 16 menampilkan waktu dalam format universal dan format standard untuk menegaskan bahwa setiap anggota telah diinisialisasi dengan benar. Baris 18 menetapkan waktu baru dengan memanggil fungsi setWaktu, dan baris 22 dan baris 24 menampilkan waktu kembali dalam dua format.

Gambar 1.3 Program Menguji Kelas Waktu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Gambar 1.3: gambar1_03.cpp
// Program untuk menguji kelas Waktu.
// PERHATIAN: File ini harus dikompilasi dengan Waktu.cpp.
#include <iostream>
#include "Waktu.h" // menyertakan definisi kelas Waktu dari Waktu.h
using namespace std;

int main()
{
  Waktu t; // menginstansiasi objek t dari kelas Waktu
  
  // menampilkan nilai-nilai awal objek t
  cout << "Waktu universal awal adalah ";
  t.tampilUniversal(); // 00:00:00
  cout << "\nWaktu standard awal adalah ";
  t.tampilStandard(); // 12:00:00 AM
  
  t.setWaktu( 13, 27, 6 ); // mengubah waktu

  // menampilkan nilai-nilai baru objek t
  cout << "\n\nWaktu universal setelah setWaktu adalah ";
  t.tampilUniversal(); // 13:27:06
  cout << "\nWaktu standard setelah setWaktu adalah ";
  t.tampilStandard(); // 1:27:06 PM

  // mencoba untuk menetapkan waktu dengan nilai tak-valid
  try
  {
    t.setWaktu( 99, 99, 99 ); // semua nilai diluar rentang
  } // akhir dari try
  catch ( invalid_argument &e )
  {
    cout << "Eksepsi: " << e.what() << endl << endl;
  } // akhir dari catch

  // menampilkan nilai-nilai objek t setelah menspesifikasi nilai tak-valid
  cout << "\n\nSetelah mencoba menetapkan nilai tak-valid:"
    << "\nWaktu universal: ";
  t.tampilUniversal(); // 00:00:00
  cout << "\nWaktu standard: ";
  t.tampilStandard(); // 12:00:00 AM
  cout << endl;
} // akhir dari main

Waktu universal awal adalah 00:00:00
Waktu standard awal adalah 12:00:00 AM
Waktu universal setelah setWaktu adalah 13:27:06
Waktu standard setelah setWaktu adalah 1:27:06 PM
Eksepsi terjadi: jam, menit dan/atau detik di luar rentang
Setelah mencoba menetapkan nilai tak-valid:
Waktu universal: 13:27:06
Waktu standard: 1:27:06 PM

Memanggil setWaktu dengan Nilai Tak-Valid
Untuk mengilustrasikan bahwa metode setWaktu memvalidasi argumennya, baris 29 memanggil setWaktu dengan argumen tak-valid 99 untuk jam, menit, dan detik. Statemen ini ditempatkan di dalam sebuah blok try (baris 27-30) untuk berjaga-jaga apabila setWaktu melemparkan eksepsi invalid_argument. Ketika ini terjadi, eksepsi ditangkap pada baris 31-34 dan baris 33 menampilkan pesan error eksepsi dengan memanggil fungsi anggota what. Baris 37-41 menampilkan waktu kembali dalam dua format untuk memastikan bahwa setWaktu tidak mengubah waktu ketika argumen tak-valid disuplai.

1.3 Skop Kelas dan Mengakses Anggota Kelas
Anggota data suatu kelas (variabel yang dideklarasikan di dalam definisi kelas) dan anggota kelas (fungsi yang dideklarasikan di dalam definisi kelas) berada di dalam skop kelas. Fungsi non-anggota didefinisikan dengan skop namespace global.

Dengan skop kelas, anggota kelas dapat diakses oleh semua fungsi anggota kelas tersebut dan dapat direferensi dengan nama. Di luar skop kelas, anggota public suatu kelas dapat direferensi melalui nama objek, melalui referensi ke objek, atau melalui pointer yang menunjuk ke objek.

Fungsi anggota suatu kelas dapat dioverload, tetapi hanya oleh fungsi anggota lain dari kelas tersebut. Untuk mengoverload fungsi anggota, Anda hanya perlu menyediakan prototipe di dalam definisi kelas untuk setiap versi fungsi teroverload dan menyediakan definisi fungsi terpisah untuk setiap versi fungsi teroverload. Ini juga berlaku untuk konstruktor kelas.

Variabel yang dideklarasikan di dalam sebuah fungsi anggota memiliki skop lokal dan hanya dikenal oleh fungsi tersebut. Jika sebuah fungsi anggota mendefinisikan suatu variabel dengan nama sama dengan suatu variabel dengan skop kelas, maka variabel skop kelas tersebut disembunyikan oleh variabel skop lokal. Jadi, variabel skop kelas tersebut tidak dikenal di dalam skop lokal. Variabel tersembunyi semacam itu dapat diakses dengan memberikan nama kelas di depan nama variabel dan menempatkan operator resolusi skop (::) setelah nama kelas tersebut.

Operator penyeleksi anggota dot (.) yang ditempatkan setelah nama objek atau ditempatkan setelah sebuah referensi ke suatu objek dapat dipakai untuk mengakses anggota objek itu. Operator penyeleksi anggota panah (->) yang ditempatkan setelah setelah sebuah pointer yang menunjuk ke suatu objek dapat digunakan untuk mengakses anggota objek tersebut.

Gambar 1.4 menggunakan kelas sederhana yang dinamakan Hitung (baris 7-24) dengan anggota data private x bertipe int (baris 23), fungsi anggota public setX (baris 11-14), dan fungsi anggota public tampil (baris 17-20) untuk mengilustrasikan pengaksesan anggota kelas dengan operator penyeleksi anggota. Agar lebih sederhana, kelas kecil ini ditempatkan di file yang sam dengan fungsi main. Baris 28-30 menciptakan tiga variabel yang berkaitan dengan tipe Hitung, yaitu kounter (sebuah objek Hitung), kounterPtr (sebuah pointer yang menunjuk ke objek Hitung), dan kounterRef (sebuah referensi ke sebuah objek Hitung). Pada baris 33-34 dan 37-38, perhatikan bahwa program memanggil fungsi anggota setX dan tampil dengan menggunakan operator penyeleksi anggota dot (.) yang diawali dengan nama objek (kounter) atau dengan referensi ke objek (kounterRef, yang merupakan nama alias bagi kounter). Sama halnya, baris 41-42 mendemonstrasikan bahwa program dapat memanggil fungsi setX dan tampil dengan menggunakan suatu pointer (kounterPtr) dan operator penyeleksi anggota panah (->).

Gambar 1.4 Mendemonstrasikan . dan ->

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

// definisi kelas Hitung
class Hitung
{
public: // data public berbahaya
  // menetapkan nilai untuk anggota data private x
  void setX( int nilai )
  {
    x = nilai;
  } // akhir dari fungsi setX

  // menampilkan nilai dari anggota data private x
  void tampil()
  {
    cout << x << endl;
  } // akhir dari fungsi tampil

private:
 int x;
}; // akhir dari kelas Hitung

int main()
{
  Hitung kounter; // menciptakan objek kounter
  Hitung *kounterPtr = &kounter; // menciptakan pointer ke kounter
  Hitung &kounterRef = kounter; // menciptakan referensi ke kounter

  cout << "Menetapkan x menjadi 1 dan menampilkannya menggunakan nama objek: ";
  kounter.setX( 1 ); // menetapkan anggota data x menjadi 1
  kounter.tampil(); // memanggil fungsi anggota tampil

  cout << "Menetapkan x menjadi 2 dan menampilkannya menggunakan referensi ke objek: ";
  kounterRef.setX( 2 ); // menetapkan anggota data menjadi 2
  kounterRef.tampil(); // memanggil fungsi anggota tampil

  cout << "Menetapkan x menjadi 3 menggunakan pointer ke objek: ";
  kounterPtr->setX( 3 ); // menetapkan anggota data menjadi 3
  kounterPtr->tampil(); // memanggil fungsi anggota tampil
} // akhir dari main

Menetapkan x menjadi 1 dan menampilkannya menggunakan nama objek: 1
Menetapkan x menjadi 2 dan menampilkannya menggunakan referensi ke objek: 2
Menetapkan x menjadi 3 dan menampilkannya menggunakan pointer ke objek: 3

1.4 Fungsi Akses dan Fungsi Utilitas
Fungsi akses dapat membaca atau menampilkan data. Kegunaan lain dari fungsi akses adalah untuk menguji kebenaran atau kesalahan kondisi. Fungsi ini sering juga dipanggil dengan fungsi predikat. Salah satu contoh dari fungsi predikat adalah fungsi isEmpty untuk sembarang kontainer, seperti vector. Program dapat menguji isEmpty sebelum mencoba membaca item lain dari objek kontainer. Fungsi predikat isFull dapat menguji kontainer untuk menentukan apakah objek tersebut penuh atau tidak. Fungsi predikat untuk kelas Waktu yang bisa Anda tulis adalah isAM dan isPM.

Program pada Gambar 1.5 – 1.7 mendemonstrasikan kegunaan fungsi utilitas (yang juga dipanggil dengan fungsi pembantu). Fungsi utilitas bukan bagian dari antarmuka public suatu kelas; tetapi, ia menjadi fungsi anggota private yang mendukung operasi fungsi anggota lain di dalam kelas. Fungsi utilitas tidak dimaksudkan untuk digunakan oleh kode klien.

Kelas PekerjaPemasaran (Gambar 1.5) mendeklarasikan sebuah array yang memuat jumlah pemasaran selama 12 bulan (baris 17), beberapa prototipe untuk konstruktor kelas, dan fungsi anggota yang memanipulasi array.


Gambar 1.5 Definisi Kelas PekerjaPemasaran

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Gambar 1.5: PekerjaPemasaran.h
// Definisi kelas PekerjaPemasaran.
// Fungsi anggota didefinisikan di dalam PekerjaPemasaran.cpp.
#ifndef PEKERJAPEMASARAN_H
#define PEKERJAPEMASARAN_H

class PekerjaPemasaran
{
public:
  static const int bulanPerTahun = 12; // bulan di dalam setahun
  PekerjaPemasaran(); // konstruktor
  void getPemasaranDariPengguna(); // memasukkan pemasaran dari papanketik
  void setPemasaran( int, double ); // menetapkan pemasaran untuk bulan tertentu
  void tampilPemasaranTahunan(); // menyimpulkan dan menampilkan pemasaran
private:
  double totalPemasaranTahunan(); // prototipe untuk fungsi utilitas
  double pemasaran[ bulanPerTahun ]; // nilai pemasaran 12 bulan
}; // akhir dari kelas PekerjaPemasaran

#endif

Pada Gambar 1.6, konstruktor PekerjaPemasaran (baris 9-13) menginisialisasi array pemasaran dengan nol. Fungsi anggota public setPemasaran (baris 30-37) menetapkan angka pemasaran untuk satu bulan di dalam array pemasaran. Fungsi anggota public tampilPemasaranTahunan (baris 40-45) menampilkan total pemasaran untuk 12 bulan terakhir. Fungsi utilitas private totalPemasaranTahunan (baris 48-56) menghitung total nilai pemasaran selama 12 bulan untuk ditampilkan oleh tampilPemasaranTahunan. Fungsi tampilPemasaranTahunan mengedit nilai pemasaran dalam format moneter.

Gambar 1.6 Definisi Kelas PekerjaPemasaran

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
// Gambar 1.6: PekerjaPemasaran.cpp
// Definisi fungsi anggota kelas PekerjaPemasaran.
#include <iostream>
#include <iomanip>
#include "PekerjaPemasaran.h" // menyertakan definisi kelas PekerjaPemasaran
using namespace std;

// menginisialisasi elemen-elemen array pemasaran menjadi 0.0
PekerjaPemasaran::PekerjaPemasaran()
{
  for ( int i = 0; i < bulanPerTahun; ++i )
    pemasaran[ i ] = 0.0;
} // akhir dari konstruktor PekerjaPemasaran

// mendapatkan 12 angka pemasaran dari pengguna dengan papanketik
void PekerjaPemasaran::getPemasaranDariPengguna()
{
  double angkaPemasaran;

  for ( int i = 1; i <= bulanPerTahun; ++i )
  {
    cout << "Masukkan angka pemasaran untuk bulan " << i << ": ";
    cin >> angkaPemasaran;
    setPemasaran( i, angkaPemasaran );
  } // akhir dari for
 } // akhir dari fungsi getPemasaranDariPengguna

// menetapkan  satu dari 12 angka pemasaran bulanan; fungsi mengurangkan
// satu dari nilai bulan untuk kepentingan subskript
void PekerjaPemasaran::setPemasaran( int bulan, double jumlah )
{
  // menguji validitas bulan dan jumlah
  if ( bulan >= 1 && bulan <= bulanPerTahun && jumlah > 0 )
    pemasaran[ bulan - 1 ] = jumlah; // mengubah subskript menjadi 0-11
  else // bulan atau jumlah tak-valid
    cout << "Angka pemasaran dan jumlah tak-valid" << endl;
} // akhir dari fungsi setPemasaran

// menampilkan total pemasaran tahunan (dengan bantuan fungsi utilitas)
void PekerjaPemasaran::tampilPemasaranTahunan()
{
  cout << setprecision( 2 ) << fixed
    << "\nTotal pemasaran tahunan: Rp. "
    << totalPemasaranTahunan() << endl; // memanggil fungsi utilitas
} // akhir dari fungsi tampilPemasaranTahunann

// fungsi utilitas private untuk menghitung total pemasaran tahunan
double PekerjaPemasaran::totalPemasaranTahunan()
{
  double total = 0.0; // menginisialisasi total
 
  for ( int i = 0; i < bulanPerTahun; ++i ) // total hasil pemasaran
    total += pemasaran[ i ]; // menjumlahkan pemasaran bulan ke-i kepada total
 
  return total;
} // akhir dari fungsi totalPemasaranTahunan

Pada Gambar 1.7, perhatikan bahwa fungsi main hanya mencantumkan deretan pemanggilan fungsi anggota, tidak terdapat statemen kendali. Logika pemanipulasian array pemasaran secara utuh dienkapsulasi di dalam fungsi anggota.

Gambar 1.7 Mendemonstrasikan Fungsi Utilitas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Gambar 1.7: gambar1_07.cpp
// Mendemonstrasikan fungsi utilitas.
// Kompilasi program ini dengan PekerjaPemasaran.cpp

// menyertakan definisi kelas PekerjaPemasaran dari PekerjaPemasaran.h
#include "PekerjaPemasaran.h"

int main()
{
  PekerjaPemasaran s; // menciptakan objek PekerjaPemasaran (s)

  s.getPemasaranDariPengguna();
  s.tampilPemasaranTahunan();
} // akhir dari main

Masukkan jumlah pemasaran untuk bulan 1: 5314.76
Masukkan jumlah pemasaran untuk bulan 2: 4292.38
Masukkan jumlah pemasaran untuk bulan 3: 4589.83
Masukkan jumlah pemasaran untuk bulan 4: 5534.03
Masukkan jumlah pemasaran untuk bulan 5: 4376.34
Masukkan jumlah pemasaran untuk bulan 6: 5698.45
Masukkan jumlah pemasaran untuk bulan 7: 4439.22
Masukkan jumlah pemasaran untuk bulan 8: 5893.57
Masukkan jumlah pemasaran untuk bulan 9: 4909.67
Masukkan jumlah pemasaran untuk bulan 10: 5123.45
Masukkan jumlah pemasaran untuk bulan 11: 4024.97
Masukkan jumlah pemasaran untuk bulan 12: 5923.92

Total pemasaran tahunan: Rp. 60120.59

1.5 Studi Kasus Kelas Waktu: Konstruktor dengan Argumen Default
Program pada Gambar 1.8 - 1.10 memperbaiki kelas Waktu sehingga dapat mendemonstrasikan bagaimana argumen secara implisit dilewatkan kepada sebuah konstruktor. Konstruktor yang didefinisikan pada Gambar 1.2 menginisialisasi jam, menit, dan detik dengan 0. Sama seperti fungsi, konstruktor dapat menspesifikasi argumen default. Baris 13 pada Gambar 1.8 mendeklarasikan konstruktor Waktu untuk menyertakan argumen-argumen default, yang menspesifikasi nilai default 0 untuk setiap argumen yang dilewatkan kepada konstruktor. Pada Gambar 1.9, baris 10-13 mendefinisikan versi baru dari konstruktor Waktu yang menerima nilai-nilai untuk parameter jam, menit, dan detik yang akan digunakan untuk menginisialisasi anggota data private jam, menit, dan detik. Kelas Waktu menyediakan beberapa fungsi set dan get untuk setiap anggota data. Konstruktor Waktu sekarang memanggil setWaktu, yang memanggil setJam, setMenit, dan setDetik untuk memvalidasi dan menugaskan nilai-nilai kepada anggota-anggota. Argumen default kepada konstruktor memastikan bahwa, meski jika tidak ada nilai yang disediakan di dalam pemanggilan sebuah konstruktor, konstruktor masih tetap menginisialisasi anggota data. Sebuah konstruktor yang menetapkan nilai default untuk semua argumennya disebut dengan konstruktor default. Konstruktor default dapat dipanggil tanpa argumen. Hanya boleh terdapat satu konstruktor default di dalam setiap kelas.

Gambar 1.8 Definisi Kelas Waktu dengan Konstruktor Default

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
// Gambar 1.8:  Waktu.h
// Definisi kelas Waktu yang memuat konstruktor default.
// Fungsi anggota didefinisikan di dalam Waktu.cpp

// mencegah penyertaan jamak header
#ifndef WAKTU_H
#define WAKTU_H

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

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

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

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

#endif

Pada Gambar 1.9, baris 12,  konstruktor memanggil fugsi anggota setWaktu dengan nilai-nilai yang dilewatkan kepada konstruktor (nilai-nilai default). Fungsi setWaktu memanggil setJam untuk memastikan bahwa nilai yang disuplai untuk jam berada di dalam rentang 0-23, kemudian memanggil setMenit dan setDetik untuk memastikan bahwa nilai yang disuplai untuk menit dan detik berada di dalam rentang 0-59. Fungsi setJam (baris 24-30), setMenit (baris 33-39), dan setDetik (baris 42-48) masing-masing sebuah eksepsi jika argumen yang diterima berada di luar rentang yang diijinkan.

Gambar 1.9 Definisi Fungsi Anggota Kelas Waktu

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

// konstruktor Waktu menginisialisasi setiap anggota data dengan nol.
Waktu::Waktu( int jam, int menit, int detik )
{
  setWaktu( jam, menit, detik ); // memvalidasi dan menetapkan waktu
} // akhir dari konstruktor Waktu

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

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

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

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

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

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

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

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

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

Fungsi main pada Gambar 1.10 menginisialisasi lima objek Waktu, yaitu satu dengan tiga argumen default di dalam pemanggilan konstruktor implisit (baris 10), satu dengan satu argumen yang dispesifikasi (baris 11), satu dengan dua argumen yang dispesifikasi (baris 12), satu dengan tiga argumen yang dispesifikasi (baris 13), dan satu dengan tiga argumen tak-valid yang dispesifikasi (baris 38). Program menampilkan setiap objek dalam format waktu universal dan format waktu standard. Untuk objek Waktu, t5, pada (baris 38), program menampilkan pesan error karena argumen konstruktor di luar rentang yang diijinkan.

Gambar 1.10 Mendemonstrasikan Konstruktor Default untuk Kelas Waktu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Gambar 1.10: gambar1_10.cpp
// Mendemonstrasikan konstruktor default untuk kelas Waktu.
#include <iostream>
#include <stdexcept>
#include "Waktu.h" // menyertakan definisi kelas Waktu dari Waktu.h
using namespace std;

int main()
{
  Waktu t1; // semua argumen default
  Waktu t2( 2 ); // jam dispesifikasi; menit dan detik default
  Waktu t3( 21, 34 ); // jam dan menit dispesifikasi; detik default
  Waktu t4( 12, 25, 42 ); // jam, menit, dan detik dispesifikasi

  cout << "Dikonstruksi dengan:\n\nt1: semua argumen default\n ";
  t1.tampilUniversal(); // 00:00:00
  cout << "\n ";
  t1.tampilStandard(); // 12:00:00 AM

  cout << "\n\nt2: jam dispesifikasi; menit dan detik default\n ";
  t2.tampilUniversal(); // 02:00:00
  cout << "\n ";
  t2.tampilStandard(); // 2:00:00 AM

  cout << "\n\nt3: jam dan menit dispesifikasi; detik default\n ";
  t3.tampilUniversal(); // 21:34:00
  cout << "\n ";
  t3.tampilStandard(); // 9:34:00 PM

  cout << "\n\nt4: jam, menit, dan detik dispesifikasi\n ";
  t4.tampilUniversal(); // 12:25:42
  cout << "\n ";
  t4.tampilStandard(); // 12:25:42 PM

  // mencoba menginisialisasi t5 dengan nilai-nilai tak-valid
  try
  {
    Waktu t5( 27, 74, 99 ); // semua nilai tak-valid
  } // akhir dari try
  catch ( invalid_argument &e )
  {
    cout << "\n\nEksepsi ketika menginisialisasi t5: " << e.what() << endl;
  } // akhir dari catch
} // akhir dari main

Dikonstruksi dengan:
t1: semua argumen default
  00:00:00
  12:00:00 AM

t2: jam dispesifikasi; menit dan detik default
  02:00:00
  2:00:00 AM

t3: jam dan menit dispesifikasi; detik default
  21:34:00
  9:34:00 PM

t4: jam, menit, dan detik dispesifikasi
  12:25:42
  12:25:42 PM

Eksepsi ketika menginisialisasi t5: jam harus dalam rentang 0-23

Beberapa fungsi set dan get pada kelas Waktu dipanggil melalui tubuh kelas. Secara khusus, fungsi setWaktu (baris 17-22 pada Gambar 1.9) memanggil fungsi setJam, setMenit, dan setDetik. Fungsi tampilUniversal dan tampilStandard memanggil fungsi getJam, getMenit, dan getDetik pada baris 72-73 dan baris 79-81. Pada setiap kasus, fungsi-fungsi tersebut mengakses data private kelas secara langsung.

1.6 Destruktor
Destruktor adalah jenis lain dari fungsi anggota spesial. Nama destruktor untuk suatu kelas diberikan dengan karakter ~ yang diikuti nama kelas. Konvensi penamaan ini karena destruktor adalah komplemen dari konstruktor.

Destruktor suatu kelas dipanggil secara implisit ketika sebuah objek dihancurkan. Hal ini terjadi, misalnya, ketika sebuah objek otomatis dihancurkan ketika eksekusi program meninggalkan skop dimana di dalamnya objek tersebut diinstansiasi.

Meskipun destruktor tidak disediakan untuk kelas yang disajikan sejauh ini, tetapi sebenarnya setiap kelas memiliki sebuah destruktor. Jika Anda tidak secara eksplisit menyediakan sebuah destruktor, kompiler akan menciptakan suatu destruktor kosong.

1.7 Kapan Konstruktor dan Destruktor Dipanggil
Konstruktor dan destruktor dipanggil secara implisit oleh kompiler. Urutan pemanggilannya bergantung pada urutan eksekusi memasuki dan meninggalkan skop dimana objek-objek diinstansiasi. Secara umum, pemanggilan destruktor dilakukan dengan urutan yang berkebalikan dengan pemanggilan konstruktor, tetapi seperti yang akan Anda lihat pada Gambar 1.11-1.13, urutan pemanggilan destruktor dapat diubah.

Konstruktor dan Destruktor untuk Objek dalam Skop Global
Konstruktor dipanggil untuk objek yang didefinisikan di dalam skop global sebelum sembarang fungsi (termasuk main) pada file tersebut mulai dieksekusi. Destruktor terkait dipanggil ketika main berhenti dieksekusi. Fungsi exit memaksa sebuah program untuk berhenti segera dan destruktor bagi objek otomatis tidak dieksekusi. Fugsi ini seringkali dipakai untuk menghentikan suatu program ketika error dideteksi di dalam masukan atau jika sebuah file yang akan diproses oleh program tidak bisa dibuka. Fungsi abort melakukan tugas yang sama dengan fungsi exit, tetapi tanpa mengijinkan destruktor dari sembarang objek untuk dipanggil. Fungsi abort biasanya dipakai untuk mengindikasikan penghentian abnormal atas program.


Konstruktor dan Destruktor untuk Objek Otomatis Lokal
Konstruktor untuk sebuah objek otomatis lokal dipanggil ketika eksekusi mencapai titik dimana objek tersebut didefinisikan. Destruktor terkait dipanggil ketika eksekusi meninggalkan skop objek tersebut (yaitu, blok dimana di dalamnya objek tersebut didefinisikan telah selesai dieksekusi). Konstruktor dan destruktor untuk objek otomatis dipanggil setiap kali eksekusi memasuki dan meninggalkan skop objek tersebut. Destruktor tidak dipanggil untuk objek otomatis jika program berhenti karena pemanggilan terhadap fungsi exit atau fungsi abort.

Konstruktor dan Destruktor untuk Objek Lokal static
Konstruktor untuk sebuah objek lokal static dipanggil hanya sekali, ketika eksekusi pertama-kali mencapai titik dimana objek tersebut didefinisikan. Destruktor terkait dipanggil ketika main berhenti atau ketika program memanggil fungsi exit. Objek global dan objek static dihancurkan dengan urutan yang berbeda dengan ketika diciptakan. Destruktor tidak dipanggil untuk objek static jika program berhenti karena pemanggilan fungsi abort.

Mendemonstrasikan Kapan Konstruktor dan Destruktor Dipanggil
Program pada Gambar 1.11 – 1.13 mendemonstrasikan urutan dimana konstruktor dan destruktor dipanggil untuk objek-objek kelas CiptakanDanHancurkan (Gambar 1.11 dan Gambar 1.12). Setiap objek kelas CiptakanDanHancurkan memuat sebuah integer (objekID) dan sebuah string (pesan) yang digunakan di dalam keluaran program untuk mengidentifikasi objek (Gambar 1.11 baris 16-17). Contoh ini murni digunakan untuk tujuan pengajaran dan penjelasan saja. Pada Gambar 1.12, destruktor (baris 21) menentukan apakah objek yang sedang dihancurkan memiliki objekID 1 atau 6 dan, jika ya, keluaran menampilkan karakter garis-baru. Baris ini membuat keluaran program lebih mudah diikuti.

Gambar 1.11 Definisi Kelas CiptakanDanHancurkan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Gambar 1.11: CiptakanDanHancurkan.h
// Definisi kelas CiptakanDanHancurkan.
// Fungsi-fungsi anggota didefinisikan di dalam CiptakanDanHancurkan.cpp.
#include <string>
using namespace std;

#ifndef CIPTAKAN_H
#define CIPTAKAN_H

class CiptakanDanHancurkan
{
public:
  CiptakanDanHancurkan( int, string ); // konstruktor
  ~CiptakanDanHancurkan(); // destruktor
private:
  int objekID; // nomor ID untuk objek
  string pesan; // pesan mendeskripsikan objek
}; // akhir dari kelas CiptakanDanHancurkan

#endif

Gambar 1.12 Definisi Fungsi Anggota Kelas CiptakanDanHancurkan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Gambar 1.12: CiptakanDanHancurkan.cpp
// Definisi fungsi anggota kelas CiptakanDanHancurkan.
#include <iostream>
#include "CiptakanDanHancurkan.h" //menyertakan definisi kelas CiptakanDanHancurkan
using namespace std;

// konstruktor
CiptakanDanHancurkan::CiptakanDanHancurkan( int ID, string pesanString )
{
  objekID = ID; // menetapkan nomor ID objek
  pesan = pesanString; // menetapkan pesan deskriptif objek

  cout << "Objek " << objekID << " konstruktor dipanggil "
    << pesan << endl;
} // akhir dari CiptakanDanHancurkan

// destruktor
CiptakanDanHancurkan::~CiptakanDanHancurkan()
{
  // menampilkan garis-baru untuk objek tertentu; agar lebih mudah dibaca
  cout << ( objekID == 1 || objekID == 6 ? "\n" : "" );
 
  cout << "Objek " << objekID << " destruktor dipanggil "
    << pesan << endl;
} // akhir dari destruktor ~CiptakanDanHancurkan

Gambar 1.13 mendefinisikan objek pertama (baris 10) di dalam skop global. Konstruktornya sebenarnya dipanggil sebelum sembarang statemen di dalam main dieksekusi dan destruktornya dipanggil setelah program berhenti dan setelah destruktor untuk semua objek dipanggil.

Fungsi main (baris 12-23) mendeklarasikan tiga objek. Objek kedua (baris 15) dan keempat (baris 21) merupakan objek otomatis lokal, dan objek ketiga (baris 16) adalah sebuah objek lokal static. Konstruktor bagi tiap objek ini dipanggil ketika eksekusi meraih titik dimana objek tersebut dideklarasikan. Destruktor bagi objek keempat kemudian objek kedua dipanggil (urutan pemanggilan destruktor berkebalikan dengan urutan pemanggilan konstruktor) ketika eksekusi mencapai akhir main. Karena objek ketiga adalah static, ia tetap ada sampai program berhenti. Destruktor untuk objek ketiga dipanggil sebelum destruktor untuk objek global pertama dipanggil, tetapi setelah semua objek lainnya dihancurkan.

Fungsi ciptakan (baris 26 – 33) mendeklarasikan tiga objek; objek kelima (baris 29) dan objek ketujuh (baris 31) sebagai objek otomatis lokal, dan objek keenam (baris 30) sebagai objek lokal static. Destruktor untuk objek ketujuh kemudian untuk objek kelima dipanggil (urutan pemanggilan destruktor berkebalikan dengan urutan pemanggilan konstruktor) ketika fungsi ciptakan berhenti dieksekusi. Karena objek keenam adalah static, maka ia tetap ada sampai program berhenti. Destruktor untuk objek keenam dipanggil sebelum destruktor untuk objek ketiga dan pertama, tetapi setelah semua objek dihancurkan.

Gambar 1.13 Mendemonstrasikan Urutan Pemanggilan Konstruktor dan Destruktor

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
// Gambar 1.13: gambar1_13.cpp
// Mendemonstrasikan urutan pemanggilan konstruktor
// dan destruktor.
#include <iostream>
#include "CiptakanDanHancurkan.h" //menyertakan definisi kelas CiptakanDanHancurkan
using namespace std;

void ciptakan( void ); // prototipe

CiptakanDanHancurkan pertama( 1, "(global sebelum main)" ); //objek global

int main()
{
  cout << "\nFUNGSI MAIN: EKSEKUSI DIMULAI" << endl;

  CiptakanDanHancurkan kedua( 2, "(otomatis lokal di dalam main)" );
  static CiptakanDanHancurkan ketiga( 3, "(lokal static di dalam main)" );

  ciptakan(); // memanggil fungsi untuk menciptakan objek

  cout << "\nFUNGSI MAIN: EKSEKUSI DIMULAI KEMBALI" << endl;
  CiptakanDanHancurkan keempat( 4, "(otomatis lokal di dalam main)" );
  cout << "\nFUNGSI MAIN: EKSEKUSI BERAKHIR" << endl;
} // akhir dari main

// fungsi untuk menciptakan objek-objek
void ciptakan( void )
{
  cout << "\nFUNGSI CIPTAKAN: EKSEKUSI DIMULAI" << endl;
  CiptakanDanHancurkan kelima( 5, "(otomatis lokal di dalam ciptakan)" );
  static CiptakanDanHancurkan keenam( 6, "(lokal static di dalam ciptakan)" );
  CiptakanDanHancurkan ketujuh( 7, "(otomatis lokal di dalam ciptakan)" );
  cout << "\nFUNGSI CIPTAKAN: EKSEKUSI BERAKHIR" << endl;
} // akhir dari fungsi ciptakan

Objek 1 konstruktor dipanggil (global sebelum main)

FUNGSI MAIN: EKSEKUSI DIMULAI
Objek 2 konstruktor dipanggil (otomatis lokal di dalam main)
Objek 3 konstruktor dipanggil (lokal static di dalam main)

FUNGSI CIPTAKAN: EKSEKUSI DIMULAI
Objek 5 konstruktor dipanggil (otomatis lokal di dalam ciptakan)
Objek 6 konstruktor dipanggil (lokal static di dalam ciptakan)
Objek 7 konstruktor  (otomatis lokal di dalam ciptakan)

FUNGSI CIPTAKAN: EKSEKUSI BERAKHIR
Objek 7 destruktor dipanggil (otomatis lokal di dalam ciptakan)
Objek 5 destruktor dipanggil (otomatis lokal di dalam ciptakan)

FUNGSI MAIN: EKSEKUSI DIMULAI KEMBALI
Objek 4 konstruktor dipanggil (otomatis lokal di dalam main)

FUNGSI MAIN: EKSEKUSI BERAKHIR
Objek 4 destruktor dipanggil (otomatis lokal di dalam main)
Objek 2 destruktor dipanggil (otomatis lokal di dalam main)

Objek 6 destruktor dipanggil (otomatis lokal di dalam ciptakan)
Objek 3 destruktor dipanggil (lokal static di dalam main)

Objek 1 destruktor dipanggil (global sebelum main)

1.8 Studi Kasus Kelas Waktu: Nilai Balik Referensi ke Anggota Data private
Referensi ke sebuah objek merupakan nama alias bagi objek tersebut dan, oleh karena itu, dapat digunakan di sisi kiri suatu statemen penugasan. Pada konteks ini, referensi merupakan lvalue yang dapat menerima sebuah nilai. Salah satu cara untuk menggunakan kapabilitas ini adalah dengan menggunakan suatu fungsi anggota public yang menghasilkan nilai balik berupa referensi ke sebuah anggota data private. Jika suatu fungsi menghasilkan nilai balik berupa referensi const, maka referensi tersebut tidak bisa dipakai sebagai lvalue yang dapat dimodifikasi.

Program pada Gambar 1.14 – 1.16 menggunakan kelas Waktu yang disederhanakan (Gambar 1.14 dan Gambar 1.15) untuk mendemonstrasikan pengembalian referensi ke sebuah anggota data private dengan fungsi anggota burukSetJam (dideklarasikan pada Gambar 1.14, baris 15 dan didefinisikan pada Gambar 1.15, baris 37-45). Nilai balik berupa referensi semacam itu sebenarnya menjadikan pemanggilan terhadap fungsi anggota buruksetJam sebagai alias untuk anggota data private (jam).

Gambar 1.14 Definisi Kelas Waktu

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

// mencegah penyertaan jamak header
#ifndef WAKTU_H
#define WAKTU_H

class Waktu
{
public:
   Waktu( int = 0, int = 0, int = 0 ); // konstruktor default
   void setWaktu( int, int, int ); // menetapkan jam, menit, dan detik
   int getJam();
   int &burukSetJam( int ); // pengembalian referensi yang berbahaya
private:
   int jam;
   int menit;
   int detik;
}; // akhir dari kelas Waktu

#endif

Gambar 1.15 Definisi Fungsi Anggota Kelas Waktu

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

// konstruktor Waktu menginisialisasi setiap anggota data private
// dengan nol.
Waktu::Waktu( int jam, int menit, int detik )
{
  setWaktu( jam, menit, detik ); // memvalidasi dan menetapkan waktu
} // akhir dari konstruktor Waktu

// menetapkan nilai Waktu baru
void Waktu::setWaktu( int h, int m, int s )
{
  // memvalidasi jam, menit, dan detik
  if ( ( h >= 0 && h < 24 ) && ( m >= 0 && m < 60 ) &&
     ( s >= 0 && s < 60 ) )
  {
    jam = h;
    menit = m;
    detik = s;
  } // akhir dari if
  else
    throw invalid_argument(
      "jam, menit dan/atau detik di luar rentang" );
} // akhir dari fungsi setWaktu

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

// teknik pemrograman buruk: pengembalian referensi yang menunjuk ke anggota data private.
int &Waktu::burukSetJam( int hh )
{
  if ( hh >= 0 && hh < 24 )
    jam = hh;
  else
    throw invalid_argument( "jam harus dalam rentang 0-23" );

  return jam; // nilai balik referensi BERBAHAYA
} // akhir dari fungsi burukSetJam

Gambar 1.16 mendeklarasikan objek Waktu, t, pada baris 10 dan referensi jamRef (baris 13), yang diinisialisasi dengan referensi yang dijadikan nilai balik oleh pemanggilan t.burukSetJam(20). Baris 15 menampilkan nilai dari jamRef. Ini menunjukkan bagaimana jamRef merusak enkapusulasi kelas, dimana sembarang statemen di dalam main seharusnya tidak dapat mengakses anggota private dari suatu kelas. Selanjutnya, baris 16 menggunakan alias untuk menetapkan nilai jam menjadi 30 (nilai tak-valid) dan baris 17 menampilkan nilai yang dijadikan nilai balik oleh fungsi getJam untuk menunjukkan bahwa penugasan suatu nilai kepada jamRef sebenarnya memodifikasi anggota data private di dalam objek Waktu, t. Terakhir, baris 21 menggunakan fungsi burukSetJam untuk memanggil dirinya sendiri sebagai lvalue dan menugaskan 74 (nilai tak-valid lainnya) kepada referensi yang dijadikan nilai balik oleh fungsi tersebut. Baris 26 kembali menampilkan nilai balik yang dikembalikan oleh fungsi getJam untuk menunjukkan bahwa penugasan suatu nilai akibat pemanggilan fungsi pada baris 21 memodifikasi data private di dalam objek Waktu, t.

Gambar 1.16 Mendemonstrasikan Referensi ke Anggota Data private

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Gambar 1.16: gambar1_16.cpp
// Mendemonstrasikan sebuah fungsi anggota public yang
// mengembalikan sebuah referensi ke anggota data private.
#include <iostream>
#include "Waktu.h" // menyertakan definisi kelas Waktu
using namespace std;

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

  // menginisialisasi jamRef dengan referensi yang dikembalikan oleh burukSetJam
  int &jamRef = t.burukSetJam( 20 ); // 20 adalah nilai valid

  cout << "Jam valid sebelum modifikasi: " << jamRef;
  jamRef = 30; // menggunakan jamRef untuk menetapkan nilai tak-valid
  cout << "\nJam tak-valid setelah modifikasi: " << t.getJam();

  // Berbahaya: Pemanggilan fungsi yang menjadikan nilai balik
  // sebuah referensi dapat digunakan sebagai lvalue!
  t.burukSetJam( 12 ) = 74; // menugaskan nilai tak-valid lain kepada jam

  cout << "\n\n*************************************************\n"
    << "TEKNIK PEMROGRAMAN YANG BURUK!!!!!!!!\n"
    << "t.burukSetJam( 12 ) sebagai lvalue, jam tak-valid: "
    << t.getJam()
    << "\n*************************************************" << endl;
} // akhir dari main

Jam valid sebelum modifikasi: 20
Jam tak-valid setelah modifikasi:30
*************************************************
TEKNIK PEMROGRAMAN YANG BURUK!!!!!!!!
t.burukSetJam( 12 ) sebagai lvalue, jam tak-valid: 74
*************************************************

1.9 Penugasan Keanggotaan Default
Operator penugasan (=) dapat dipakai untuk menugaskan sebuah objek kepada objek lainnya sesama tipe. Secara default, penugasan semacam itu dilakukan dengan penugasan keanggotaan, dimana setiap anggota data objek di sisi kanan operator penugasan ditugaskan secara individual ke anggota data yang sama di dalam objek di sisi kiri operator penugasan. Gambar 1.14-1.18 mendefinisikan kelas Tanggal untuk digunakan pada contoh ini. Baris 18 pada Gambar 1.19 menggunakan penugasan keanggotaan default untuk menugaskan anggota data dari objek tanggal1 (tipe Tanggal) kepada anggota data objek tanggal2 (tipe Tanggal). Pada kasus ini, anggota bulan dari objek tanggal1 ditugaskan kepada anggota bulan dari objek tanggal2, anggota hari dari objek tanggal1 ditugaskan kepada anggota hari dari objek tanggal2, dan anggota tahun dari objek tanggal1 ditugaskan kepada anggota tahun dari objek tanggal2.

Objek dapat dilewatkan sebagai argumen fungsi dan bisa pula dijadikan nilai balik oleh fungsi. Pelewatan dan pengembalian nilai balik semacam itu dilakukan menggukanan pelewatan dengan nilai secara default, dimana salinan dari objek dilewatkan atau dijadikan nilai balik. Pada kasus semacam itu, C++ menciptakan sebuah objek baru dan menggunakan konstruktor penyalin untuk menyalin nilai-nilai objek asli ke dalam objek baru. Untuk setiap kelas, kompiler menyediakan konstruktor penyalin default yang menyalin setiap anggota dari objek asli.

Gambar 1.17 Definisi Kelas Tanggal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Gambar 1.17: Tanggal.h
// Definisi kelas Tanggal dengan operator inkremen teroverload.

// mencegah pencantuman jamak atas header
#ifndef TANGGAL_H
#define TANGGAL_H

// Definisi kelas Tanggal
class Tanggal
 {
public:
  Tanggal( int m = 1, int d = 1, int y = 1900 ); // konstruktor default
  void tampil();
private:
  int bulan;
  int hari;
  int tahun;
}; // akhir dari kelas Tanggal

#endif

Gambar 1.18 Definisi Fungsi Anggota Kelas Tanggal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Gambar 1.18: Tanggal.cpp
// Definisi fungsi anggota kelas Tanggal.
#include <iostream>
#include "Tanggal.h" // menyertakan kelas Tanggal dari Tanggal.h
using namespace std;

// konstruktor Tanggal (seharusnya melakukan pengecekan rentang)
Tanggal::Tanggal( int m, int d, int y )
{
  bulan = m;
  hari = d;
  tahun = y;
} // akhir dari konstruktor Tanggal

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

Gambar 1.19 Mendemonstrasikan Penugasan Keanggotaan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Gambar 1.19: gambar1_19.cpp
// Mendemonstrasikan bahwa objek kelas dapat ditugaskan
// kepada satu sama lain menggunakan penugasan keanggotaan.
#include <iostream>
#include "Tanggal.h" // menyertakan definisi kelas Tanggal dari Tanggal.h
using namespace std;

int main()
{
  Tanggal tanggal1( 7, 4, 2004 );
  Tanggal tanggal2; // tanggal2 default dengan 1/1/2000

  cout << "tanggal1 = ";
  tanggal1.tampil();
  cout << "\tanggal2 = ";
  tanggal2.tampil();

  tanggal2 = tanggal1; // penugasan keanggotaan default

  cout << "\n\nSetelah penugasan keanggotaan default, tanggal2 = ";
  tanggal2.tampil();
  cout << endl;
 } // akhir dari main

Kesimpulan
*        Kode klien dapat mengakses anggota public suatu kelas melalui tiga cara: lewat nama objek, lewat referensi ke sebuah objek, atau lewat pointer yang menunjuk ke suatu objek.

*        Kegunaan umum dari fungsi akses adalah untuk menguji kebenaran atau ketidak-benaran kondisi. Fungsi semacam itu dikenal dengan fungsi predikat.

*        Fungsi utilitas (yang dikenal juga dengan fungsi pembantu) merupakan sebuah fungsi anggota private yang mendukung operasi dari fungsi anggota public suatu kelas. Fungsi pembantu ini tidak dimaksudkan untuk digunakan oleh klien atau pengguna kelas.

*        Kecuali anggota data static const int, anggota data suatu kelas tidak bisa diinisialisasi dimana anggota tersebut dideklarasikan di dalam tubuh kelas. Meskipun hal itu sebenarnya diijinkan, tetapi sangat direkomendasikan bahwa anggota data diinisialiasi oleh konstruktor kelas.

*        Anggota data suatu kelas (variabel yang dideklarasikan di dalam definisi kelas) dan anggota kelas (fungsi yang dideklarasikan di dalam definisi kelas) berada di dalam skop kelas. Fungsi non-anggota didefinisikan dengan skop namespace global.

*        Dengan skop kelas, anggota kelas dapat diakses oleh semua fungsi anggota kelas tersebut dan dapat direferensi dengan nama. Di luar skop kelas, anggota public suatu kelas dapat direferensi melalui nama objek, melalui referensi ke objek, atau melalui pointer yang menunjuk ke objek.

*        Fungsi anggota suatu kelas dapat dioverload, tetapi hanya oleh fungsi anggota lain dari kelas tersebut. Untuk mengoverload fungsi anggota, Anda hanya perlu menyediakan prototipe di dalam definisi kelas untuk setiap versi fungsi teroverload dan menyediakan definisi fungsi terpisah untuk setiap versi fungsi teroverload. Ini juga berlaku untuk konstruktor kelas.

*        Konstruktor dan destruktor dipanggil secara implisit oleh kompiler. Urutan pemanggilannya bergantung pada urutan eksekusi memasuki dan meninggalkan skop dimana objek-objek diinstansiasi. Secara umum, pemanggilan destruktor dilakukan dengan urutan yang berkebalikan dengan pemanggilan konstruktor.

*        Konstruktor dipanggil untuk objek yang didefinisikan di dalam skop global sebelum sembarang fungsi (termasuk main) pada file tersebut mulai dieksekusi. Destruktor terkait dipanggil ketika main berhenti dieksekusi. Fungsi exit memaksa sebuah program untuk berhenti segera dan destruktor bagi objek otomatis tidak dieksekusi.

*        Konstruktor untuk sebuah objek otomatis lokal dipanggil ketika eksekusi mencapai titik dimana objek tersebut didefinisikan. Destruktor terkait dipanggil ketika eksekusi meninggalkan skop objek tersebut (yaitu, blok dimana di dalamnya objek tersebut didefinisikan telah selesai dieksekusi). Konstruktor dan destruktor untuk objek otomatis dipanggil setiap kali eksekusi memasuki dan meninggalkan skop objek tersebut.

*        Konstruktor untuk sebuah objek lokal static dipanggil hanya sekali, ketika eksekusi pertama-kali mencapai titik dimana objek tersebut didefinisikan. Destruktor terkait dipanggil ketika main berhenti atau ketika program memanggil fungsi exit. Objek global dan objek static dihancurkan dengan urutan yang berbeda dengan ketika diciptakan. Destruktor tidak dipanggil untuk objek static jika program berhenti karena pemanggilan fungsi abort.

*        Operator penugasan (=) dapat dipakai untuk menugaskan sebuah objek kepada objek lainnya sesama tipe. Secara default, penugasan semacam itu dilakukan dengan penugasan keanggotaan, dimana setiap anggota data objek di sisi kanan operator penugasan ditugaskan secara individual ke anggota data yang sama di dalam objek di sisi kiri operator penugasan.
Latihan
1)      Sediakan sebuah konstruktor yang mampu menggunakan waktu sekarang dari fungsi time dan localtime (yang dideklarasikan di dalam header C++ STL <ctime>) untuk menginisialisasi suatu objek dari kelas Waktu.

2)      Ciptakanlah sebuah kelas yang dinamakan Rasional untuk melakukan aritmatika terhadap angka pecahan. Tulislah program untuk menguji kelas Anda. Gunakan variabel integer untuk merepresentasikan data private kelas, numerator dan denominator. Sediakan sebuah konstruktor yang memampukan objek kelas ini untuk diinisialisasi ketika dideklarasikan. Konstruktor harus memuat nilai-nilai default jika tidak terdapat penginisialisasi disediakan dan harus menyimpan pecahan dalam format tereduksi. Sebagai contoh, pecahan 2/4 harus disimpan di dalam objek sebagai 1 di dalam numerator dan 2 di dalam denominator. Sediakan beberapa fungsi anggota public yang melakukan setiap tugas berikut:
a)      Menjumlahkan dua angka Rasional. Hasil yang diperoleh disimpan dalam format tereduksi.
b)      Mengurangkan dua angka Rasional. Hasil yang diperoleh disimpan dalam format tereduksi.
c)      Mengalikan dua angka Rasional. Hasil yang diperoleh disimpan dalam format tereduksi.
d)      Membagi dua angka Rasional. Hasil yang diperoleh disimpan dalam format tereduksi.
e)      Menampilkan angka Rasional dalam format a/b, dimana a adalah numerator dan b adalah denominator.
f)       Menampilkan angka Rasional dalam format pecahan.

3)      Modifikasilah kelas Waktu pada Gambar 2.8 – 2.9 untuk menyertakan fungsi anggota berdetak yang menginkremen waktu yang disimpan di dalam suatu objek Waktu sebesar satu detik. Tulis sebuah program yang menguji fungsi anggota berdetak di dalam suatu loop yang menampilkan waktu dalam format standard selama tiap iterasi untuk mengilustrasikan bahwa fungsi anggota berdetak bekerja dengan benar. Pastikan untuk menguji beberapa kasus berikut:
a)      Menginkremen ke menit berikutnya.
b)      Menginkremen ke jam berikutnya.
c)       Menginkremen ke hari berikutnya.

4)      Modifikasilah kelas Tanggal pada Gambar 2.17 – 2.18 untuk melakukan pemeriksaan error pada penginisialisasi nilai-nilai untuk anggota-anggota data bulan, hari, dan tahun. Di samping itu, sediakan sebuah fungsi anggota hariBerikutnya untuk menginkremen hari sebesar satu. Tulis sebuah program yang menguji fungsi hariBerikunya di dalam suatu loop yang menampilkan tanggal selama setiap iterasi untuk mengilustrasikan bahwa hariBerikutnya bekerja dengan benar. Pastikan untuk menguji beberapa kasus berikut:
d)      Menginkremen ke bulan berikutnya.
e)      Menginkremen ke tahun berikutnya.





No comments:

Post a Comment