Langkah Berikutnya

Pengantar Pemrograman dan C++

Tutorial online ini berlanjut dengan konsep yang lebih lanjut - baca Bagian III. Fokus kita dalam modul ini adalah menggunakan {i>pointer<i}, dan memulai dengan objek.

Belajar dengan Contoh #2

Fokus kita dalam modul ini adalah untuk mendapatkan lebih banyak latihan dengan dekomposisi, memahami pointer, serta mulai menggunakan objek dan class. Pelajari contoh-contoh berikut. Tulis sendiri program saat ditanya, atau lakukan eksperimen. Kami sangat menekankan bahwa kunci untuk menjadi {i>programmer<i} yang baik adalah berlatih, berlatih, dan berlatih.

Contoh #1: Praktik Dekomposisi Lainnya

Pertimbangkan output berikut dari game sederhana:

Welcome to Artillery.
You are in the middle of a war and being charged by thousands of enemies.
You have one cannon, which you can shoot at any angle.
You only have 10 cannonballs for this target..
Let's begin...

The enemy is 507 feet away!!!
What angle? 25<
You over shot by 445
What angle? 15
You over shot by 114
What angle? 10
You under shot by 82
What angle? 12
You under shot by 2
What angle? 12.01
You hit him!!!
It took you 4 shots.
You have killed 1 enemy.
I see another one, are you ready? (Y/N) n

You killed 1 of the enemy.

Pengamatan pertama adalah teks pengantar yang ditampilkan sekali per program dalam proses eksekusi. Kita memerlukan generator angka acak untuk menentukan jarak musuh untuk setiap putaran. Kita membutuhkan mekanisme untuk mendapatkan input sudut dari pemain dan ini jelas dalam struktur lingkaran karena berulang sampai kita mengenai musuh. Kita juga membutuhkan {i>function<i} untuk menghitung jarak dan sudut. Akhirnya, kita harus melacak jumlah tembakan yang diperlukan untuk mengintai musuh, jumlah musuh yang selama pelaksanaan program. Berikut adalah kemungkinan garis besar untuk program utama.

StartUp(); // This displays the introductory script.
killed = 0;
do {
  killed = Fire(); // Fire() contains the main loop of each round.
  cout << "I see another one, care to shoot again? (Y/N) " << endl;
  cin >> done;
} while (done != 'n');
cout << "You killed " << killed << " of the enemy." << endl;

Prosedur Kebakaran menangani permainan game. Dalam fungsi itu, kita memanggil generator angka acak untuk mengetahui jarak musuh, lalu atur loop untuk mendapatkan input pemain dan menghitung apakah mereka telah mengenai musuh atau belum. Tujuan kondisi penjaga di loop adalah seberapa dekat kita sudah bisa mengenai musuh.

In case you are a little rusty on physics, here are the calculations:

Velocity = 200.0; // initial velocity of 200 ft/sec Gravity = 32.2; // gravity for distance calculation // in_angle is the angle the player has entered, converted to radians. time_in_air = (2.0 * Velocity * sin(in_angle)) / Gravity; distance = round((Velocity * cos(in_angle)) * time_in_air);

Karena panggilan ke cos() dan sin(), Anda perlu menyertakan compute.h. Coba menulis program ini - ini adalah praktik yang baik dalam dekomposisi masalah dan praktik tentang C++ dasar. Ingatlah untuk hanya melakukan satu tugas di setiap fungsi. Ini adalah program tercanggih yang telah kami tulis sejauh ini, jadi mungkin Anda perlu waktu untuk melakukannya.Berikut adalah solusi kami.

Contoh #2: Berlatih dengan Pointer

Ada empat hal yang perlu diingat saat bekerja dengan pointer:
  1. Pointer adalah variabel yang menyimpan alamat memori. Saat program dieksekusi, semua variabel disimpan dalam memori, masing-masing di alamat atau lokasi uniknya sendiri. Pointer adalah jenis variabel khusus yang berisi alamat memori, bukan nilai data. Sama seperti data yang dimodifikasi ketika variabel normal digunakan, nilai alamat yang disimpan dalam pointer diubah sebagai variabel pointer dimanipulasi. Berikut contohnya:
    int *intptr; // Declare a pointer that holds the address
                 // of a memory location that can store an integer.
                 // Note the use of * to indicate this is a pointer variable.
    
    intptr = new int; // Allocate memory for the integer.
    *intptr = 5; // Store 5 in the memory address stored in intptr.
          
  2. Kita biasanya mengatakan bahwa pointer "menunjuk" ke lokasi yang disimpannya ("pointee"). Jadi, dalam contoh di atas, intptr mengarah ke pointee 5.

    Perhatikan penggunaan operator "new" untuk mengalokasikan memori bagi penunjuk bilangan bulat kita. Ini adalah hal yang harus kita lakukan sebelum mencoba mengakses pointer.

    int *ptr; // Declare integer pointer.
    ptr = new int; // Allocate some memory for the integer.
    *ptr = 5; // Dereference to initialize the pointee.
    *ptr = *ptr + 1; // We are dereferencing ptr in order
                     // to add one to the value stored
                     // at the ptr address.
          

    Operator * digunakan untuk dereferensi di C. Salah satu kesalahan paling umum Pemrogram C/C++ saat bekerja dengan pointer lupa untuk menginisialisasi orang yang ditunjuk. Hal ini terkadang dapat menyebabkan error runtime karena kita mengakses lokasi dalam memori yang berisi data yang tidak diketahui. Jika mencoba mengubah data ini, kita dapat menyebabkan kerusakan memori yang halus sehingga bug ini sulit dilacak. 

  3. Penunjukan kursor di antara dua pointer akan menunjuk ke pointer yang sama. Jadi penugasannya y = x; menunjukkan y titik ke titik yang sama dengan x. Penetapan pointer tidak menyentuh pointer. Tindakan ini hanya mengubah satu pointer agar memiliki lokasi yang sama dengan pointer lain. Setelah penetapan pointer, kedua pointer "share" tindakan penerima tugas. 
  4. void main() {
     int* x; // Allocate the pointers x and y
     int* y; // (but not the pointees).
    
     x = new int; // Allocate an int pointee and set x to point to it.
    
     *x = 42; // Dereference x and store 42 in its pointee
    
     *y = 13; // CRASH -- y does not have a pointee yet
    
     y = x; // Pointer assignment sets y to point to x's pointee
    
     *y = 13; // Dereference y to store 13 in its (shared) pointee
    }
      

Berikut adalah trace kode tersebut:

1. Alokasikan dua pointer x dan y. Mengalokasikan pointer tidak  mengaloksikan pointer apa pun.
2. Alokasikan pointee dan tetapkan x untuk mengarah ke pointee tersebut.
3. Batalkan referensi x untuk menyimpan 42 pada titik aksesnya. Ini adalah contoh dasar dari operasi penghapusan referensi. Mulai pada x, ikuti panah di atas untuk mengakses orang yang ditunjuk.
4. Cobalah untuk membatalkan referensi y untuk menyimpan 13 pada titik aksesnya. Error ini terjadi karena y melakukan tidak memiliki penunjuk arah -- mereka tidak pernah ditugaskan.
5. Tetapkan y = x; sehingga y mengarah ke pointer x. Sekarang x dan y menunjuk ke pointee yang sama -- keduanya "berbagi".
6. Cobalah untuk membatalkan referensi y untuk menyimpan 13 pada titik aksesnya. Kali ini berhasil, karena penetapan sebelumnya memberi y pointee.

Seperti yang Anda lihat, gambar sangat membantu dalam memahami penggunaan pointer. Berikut adalah contoh lainnya.

int my_int = 46; // Declare a normal integer variable.
                 // Set it to equal 46.

// Declare a pointer and make it point to the variable my_int
// by using the address-of operator.
int *my_pointer = &my_int;

cout << my_int << endl; // Displays 46.

*my_pointer = 107; // Derefence and modify the variable.

cout << my_int << endl; // Displays 107.
cout << *my_pointer << endl; // Also 107.

Perhatikan dalam contoh ini bahwa kita tidak pernah mengalokasikan memori dengan operator "new". Kami mendeklarasikan variabel bilangan bulat normal dan memanipulasinya melalui pointer.

Dalam contoh ini, kami menggambarkan penggunaan operator {i>delete<i} yang melakukan de-alokasi memori heap, dan bagaimana kita bisa alokasikan untuk struktur yang lebih kompleks. Kita akan membahas pengaturan memori (tumpukan tumpukan dan runtime) dalam pelajaran lain. Untuk saat ini, cukup menganggap heap sebagai penyimpanan memori yang tersedia untuk menjalankan program.

int *ptr1; // Declare a pointer to int.
ptr1 = new int; // Reserve storage and point to it.

float *ptr2 = new float; // Do it all in one statement.

delete ptr1; // Free the storage.
delete ptr2;

Pada contoh terakhir ini, kita menunjukkan cara pointer digunakan untuk meneruskan nilai melalui referensi ke fungsi. Ini adalah cara kita mengubah nilai variabel dalam fungsi.

// Passing parameters by reference.
#include <iostream>
using namespace std;

void Duplicate(int& a, int& b, int& c) {
  a *= 2;
  b *= 2;
  c *= 2;
}

int main() {
  int x = 1, y = 3, z = 7;
  Duplicate(x, y, z);
  // The following outputs: x=2, y=6, z=14.
  cout << "x="<< x << ", y="<< y << ", z="<< z;
  return 0;
}

Jika kita menghilangkan & menonaktifkan argumen pada definisi fungsi Duplicate, kita meneruskan variabel "berdasarkan nilai", yaitu, salinan dibuat dari nilai variabel tersebut. Setiap perubahan yang dibuat pada variabel dalam fungsi akan mengubah salinan. Perintah ini tidak memodifikasi variabel asli.

Ketika sebuah variabel diteruskan melalui referensi, kita tidak meneruskan salinan nilainya, kita meneruskan alamat variabel ke fungsi. Setiap modifikasi yang yang kita lakukan pada variabel lokal benar-benar mengubah variabel asli yang diteruskan.

Jika Anda seorang {i>programmer<i} C, ini adalah hal baru. Kita bisa melakukan hal yang sama di C mendeklarasikan Duplicate() sebagai Duplicate(int *x), dalam hal ini x adalah pointer ke int, lalu memanggil Duplicate() dengan argumen &x (alamat x), dan menggunakan referensi x dalam Duplicate() (lihat di bawah). Namun, C++ menyediakan cara yang lebih sederhana untuk meneruskan nilai ke fungsi dengan referensi, meskipun cara "C" lama masih berfungsi.

void Duplicate(int *a, int *b, int *c) {
  *a *= 2;
  *b *= 2;
  *c *= 2;
}

int main() {
  int x = 1, y = 3, z = 7;
  Duplicate(&x, &y, &z);
  // The following outputs: x=2, y=6, z=14.
  cout << "x=" << x << ", y=" << y << ", z=" << z;
  return 0;
}

Perhatikan dengan referensi C++, kita tidak perlu meneruskan alamat variabel, atau kita perlu menghapus referensi variabel di dalam fungsi yang dipanggil.

Apa yang dihasilkan dari program berikut? Buat gambar kenangan untuk mengetahuinya.

void DoIt(int &foo, int goo);

int main() {
  int *foo, *goo;
  foo = new int;
  *foo = 1;
  goo = new int;
  *goo = 3;
  *foo = *goo + 3;
  foo = goo;
  *goo = 5;
  *foo = *goo + *foo;
  DoIt(*foo, *goo);
  cout << (*foo) << endl;
}

void DoIt(int &foo, int goo) {
  foo = goo + 3;
  goo = foo + 4;
  foo = goo + 3;
  goo = foo;
} 

Jalankan program untuk melihat apakah Anda mendapatkan jawaban yang benar.

Contoh #3: Meneruskan Nilai dengan Referensi

Tulis fungsi yang disebut accelerate() yang menggunakan kecepatan kendaraan dan jumlah sebagai input. Fungsi ini menambahkan jumlah ke kecepatan untuk mempercepat kendaraan. Parameter kecepatan harus diteruskan melalui referensi, dan jumlah berdasarkan nilai. Berikut adalah solusi kami.

Contoh #4: Class dan Objek

Pertimbangkan class berikut:

// time.cpp, Maggie Johnson
// Description: A simple time class.

#include <iostream>
using namespace std;

class Time {
 private:
  int hours_;
  int minutes_;
  int seconds_;
 public:
  void set(int h, int m, int s) {hours_ = h; minutes_ = m; seconds_ = s; return;}
  void increment();
  void display();
};

void Time::increment() {
  seconds_++;
  minutes_ += seconds_/60;
  hours_ += minutes_/60;
  seconds_ %= 60;
  minutes_ %= 60;
  hours_ %= 24;
  return;
}

void Time::display() {
  cout << (hours_ % 12 ? hours_ % 12:12) << ':'
       << (minutes_ < 10 ? "0" :"") << minutes_ << ':'
       << (seconds_ < 10 ? "0" :"") << seconds_
       << (hours_ < 12 ? " AM" : " PM") << endl;
}

int main() {
  Time timer;
  timer.set(23,59,58);
  for (int i = 0; i < 5; i++) {
    timer.increment();
    timer.display();
    cout << endl;
  }
}

Perhatikan bahwa variabel anggota class memiliki garis bawah di akhir. Ini dilakukan untuk membedakan antara variabel lokal dan variabel class.

Tambahkan metode pengurangan ke class ini. Berikut adalah solusi kami.

Keajaiban Ilmu Pengetahuan: Ilmu Komputer

Latihan

Seperti di modul pertama kursus ini, kami tidak memberikan solusi untuk latihan dan project.

Ingatlah bahwa Program yang Baik...

... diuraikan secara logis menjadi fungsi dengan setiap fungsi melakukan satu dan hanya satu tugas.

... memiliki program utama yang dibaca seperti garis besar tentang apa yang akan dilakukan program tersebut.

... memiliki nama fungsi deskriptif, konstanta, dan variabel.

... menggunakan konstanta untuk menghindari angka "ajaib" dalam program.

... memiliki antarmuka pengguna yang mudah digunakan.

Latihan Pemanasan

  • Latihan 1

    Bilangan bulat 36 memiliki properti yang aneh: bilangan bulat ini merupakan bilangan kuadrat sempurna, dan juga merupakan jumlah bilangan bulat dari 1 hingga 8. Angka berikutnya adalah 1225 yang adalah 352, dan jumlah bilangan bulat dari 1 hingga 49. Temukan nomor berikutnya itu adalah kuadrat sempurna dan juga jumlah dari deret 1...n. Angka berikutnya dapat lebih besar dari 32767. Anda dapat menggunakan fungsi library yang Anda ketahui, (atau formula matematika) untuk membuat program berjalan lebih cepat. Dimungkinkan juga untuk menulis program ini menggunakan for-loop untuk menentukan apakah suatu angka adalah sempurna kuadrat atau jumlah dari suatu deret. (Catatan: bergantung pada komputer dan program Anda, mungkin perlu waktu cukup lama untuk menemukan angka ini.)

  • Latihan 2

    Toko buku kuliah Anda membutuhkan bantuan Anda dalam memperkirakan bisnisnya untuk tahun ini. Pengalaman menunjukkan bahwa penjualan sangat bergantung pada apakah buku diperlukan atau tidak untuk mata pelajaran atau sekedar opsional, dan apakah sudah digunakan atau belum di dalam kelas sebelumnya. Buku teks baru yang disyaratkan akan terjual hingga 90% dari calon pendaftaran, tetapi jika telah digunakan di kelas sebelumnya, hanya 65% yang akan membeli. Demikian pula, 40% calon pendaftaran akan membeli buku teks baru yang bersifat opsional, tetapi jika telah digunakan di kelas sebelumnya, hanya 20% yang akan membeli. (Perhatikan bahwa "bekas" di sini bukan berarti buku bekas).

  • Tulis program yang menerima serangkaian buku sebagai input (hingga pengguna memasukkan penjaga). Untuk setiap buku meminta: kode untuk buku, biaya satu salinan untuk buku, jumlah buku yang tersedia saat ini, pendaftaran kelas calon dan data yang menunjukkan apakah buku tersebut diperlukan/opsional, baru/digunakan sebelumnya. Sebagai menampilkan semua informasi input dalam layar yang diformat dengan baik bersama dengan berapa banyak buku yang harus dipesan (jika ada, perhatikan bahwa hanya buku baru yang dipesan), total biaya setiap pesanan.

    Kemudian, setelah semua input selesai, tampilkan biaya total semua pesanan buku, dan laba yang diharapkan jika toko membayar 80% dari harga jual. Karena kita belum membahas cara menangani kumpulan data besar yang masuk ke program (nantikan info terbarunya), cukup proses satu buku pada satu waktu dan tampilkan layar output untuk buku tersebut. Kemudian, ketika pengguna telah selesai memasukkan semua data, program Anda akan menghasilkan nilai total dan keuntungan.

    Sebelum Anda mulai menulis kode, luangkan waktu untuk memikirkan desain program ini. Uraikan menjadi satu set fungsi, dan buat fungsi main() yang berbunyi seperti garis besar solusi untuk masalah tersebut. Pastikan setiap fungsi melakukan satu tugas.

    Berikut adalah contoh output:

    Please enter the book code: 1221
     single copy price: 69.95
     number on hand: 30
     prospective enrollment: 150
     1 for reqd/0 for optional: 1
     1 for new/0 for used: 0
    ***************************************************
    Book: 1221
    Price: $69.95
    Inventory: 30
    Enrollment: 150
    
    This book is required and used.
    ***************************************************
    Need to order: 67
    Total Cost: $4686.65
    ***************************************************
    
    Enter 1 to do another book, 0 to stop. 0
    ***************************************************
    Total for all orders: $4686.65
    Profit: $937.33
    ***************************************************

Project Database

Dalam proyek ini, kita membuat program C++ yang berfungsi penuh yang mengimplementasikan aplikasi database.

Dengan program ini, kami dapat mengelola database komposer dan informasi yang relevan mereka. Fitur program ini meliputi:

  • Kemampuan untuk menambahkan komposer baru
  • Kemampuan untuk menentukan peringkat komposer (yaitu, menunjukkan seberapa besar kami suka atau tidak suka musik komposer)
  • Kemampuan untuk melihat semua komposer dalam database
  • Kemampuan untuk melihat semua komposer berdasarkan peringkat

"Ada dua cara untuk membuat desain software: Salah satu caranya adalah membuatnya sangat sederhana sehingga tidak ada kekurangan yang jelas, dan cara lainnya adalah membuatnya sangat rumit sehingga tidak ada kekurangan yang jelas. Metode pertama jauh lebih sulit." - C.A.R. Hoare

Banyak dari kita belajar merancang dan mengodekan menggunakan "prosedur" pendekatan. Pertanyaan utama yang hendak kita mulai adalah "Apa yang harus dilakukan program ini?". Rab menguraikan solusi masalah menjadi tugas-tugas, yang masing-masing memecahkan sebagian menyelesaikan masalah. Tugas ini dipetakan ke fungsi dalam program kita yang dipanggil secara berurutan dari main() atau dari fungsi lain. Pendekatan langkah demi langkah ini ideal untuk beberapa permasalahan yang perlu kita selesaikan. Namun, sering kali program kita bukan hanya urutan tugas atau peristiwa linear.

Dengan pendekatan berorientasi objek ({i>object-oriented<i}, OO), kita mulai dengan pertanyaan "Apa yang objek yang saya modelkan?" Alih-alih membagi program menjadi tugas-tugas seperti yang dijelaskan di atas, kita membaginya menjadi model objek fisik. Objek-objek fisik ini memiliki status yang didefinisikan oleh serangkaian atribut, dan serangkaian perilaku atau tindakan yang yang bisa mereka lakukan. Tindakan tersebut dapat mengubah status objek, atau dapat memanggil tindakan objek lain. Premis dasarnya adalah bahwa objek "tahu" cara melakukan sesuatu dengan sendirinya. 

Dalam desain OO, kita menentukan objek fisik dalam hal class dan objek; atribut dan perilaku. Umumnya ada banyak objek dalam program OO. Namun, banyak objek ini pada dasarnya sama. Pertimbangkan hal berikut.

Class adalah serangkaian atribut dan perilaku umum untuk suatu objek, yang mungkin ada secara fisik di dunia nyata. Dalam ilustrasi di atas, kita memiliki class Apple. Semua apel, apa pun jenisnya, memiliki atribut warna dan rasa. Kita memiliki juga menentukan perilaku di mana Apple menampilkan atributnya.

Dalam diagram ini, kita telah menentukan dua objek yang merupakan class Apple. Setiap objek memiliki atribut dan tindakan yang sama dengan class, tetapi objek menentukan atribut untuk jenis apel tertentu. Selain itu, Display action menampilkan atribut untuk objek tertentu, mis., "Hijau" dan "Asam".

Desain OO terdiri dari kumpulan kelas, data yang terkait dengan kelas-kelas ini, dan serangkaian tindakan yang dapat dilakukan class. Kita juga perlu mengidentifikasi cara interaksi berbagai kelas. Interaksi ini dapat dilakukan oleh objek suatu class yang memanggil tindakan objek dari class lain. Sebagai contoh, kita telah dapat memiliki kelas AppleOutputer yang menghasilkan warna dan rasa dari sebuah array objek Apple, dengan memanggil metode Display() dari setiap objek Apple.

Berikut adalah langkah-langkah yang kami lakukan dalam membuat desain OO:

  1. Mengidentifikasi class dan menentukan secara umum objek dari setiap class disimpan sebagai data dan apa yang dapat dilakukan oleh sebuah objek.
  2. Menentukan elemen data setiap class
  3. Tentukan tindakan setiap class dan cara beberapa tindakan dari satu class dapat diimplementasikan menggunakan tindakan dari class terkait lainnya.

Untuk sistem yang besar, langkah-langkah ini terjadi secara iteratif pada tingkat detail yang berbeda.

Untuk sistem database composer, kita memerlukan class Composer yang mengenkapsulasi semua data yang ingin disimpan di setiap composer. Objek class ini dapat mempromosikan atau mendemosikan dirinya (mengubah peringkatnya), dan menampilkan atributnya.

Kita juga memerlukan kumpulan objek Composer. Untuk itu, kita menentukan class Database yang mengelola catatan individu. Objek class ini dapat menambahkan atau mengambil objek Composer, dan menampilkan setiap objek dengan memanggil tindakan tampilan objek Composer.

Terakhir, kita memerlukan semacam antarmuka pengguna untuk menyediakan operasi interaktif pada database. Ini adalah kelas placeholder, artinya kita benar-benar tidak tahu apa antarmuka pengguna akan terlihat seperti apa, tetapi kita tahu kita akan membutuhkannya. Mungkin data akan berbentuk grafis, mungkin berbasis teks. Untuk saat ini, kita menentukan placeholder yang dapat kita isi nanti.

Setelah kita mengidentifikasi class untuk aplikasi database composer, langkah berikutnya adalah menentukan atribut dan tindakan untuk class. Dalam aplikasi yang lebih kompleks, kita akan duduk dengan pensil dan kertas atau UML atau kartu CRC atau OOD untuk memetakan hierarki class dan cara objek berinteraksi.

Untuk database composer, kita menentukan class Composer yang berisi data relevan yang ingin kita simpan di setiap composer. {i>Software<i} ini juga berisi metode untuk memanipulasi peringkat, dan menampilkan datanya.

Class Database memerlukan semacam struktur untuk menyimpan objek Composer. Kita harus bisa menambahkan objek Composer baru ke struktur, serta mengambil objek Composer tertentu. Kita juga ingin menampilkan semua objek dalam urutan masuk, atau berdasarkan peringkat.

Class Antarmuka Pengguna menerapkan antarmuka yang didorong menu, dengan pengendali yang memanggil tindakan di class Database.

Jika kelas mudah dipahami dan atribut serta tindakannya jelas, seperti dalam aplikasi komposer, merancang class relatif mudah. Namun, jika ada pertanyaan di benak Anda tentang bagaimana class terkait dan berinteraksi, sebaiknya gambar terlebih dahulu, dan pelajari detailnya sebelum mulai membuat kode.

Setelah kami memiliki gambaran yang jelas tentang desain dan mengevaluasinya (lebih lanjut tentang segera), kita akan menentukan antarmuka untuk setiap class. Kita tidak perlu khawatir dengan detail implementasi pada tahap ini - hanya atribut dan tindakan, serta bagian status dan tindakan class yang tersedia untuk class lain.

Di C++, kita biasanya melakukannya dengan menentukan file header untuk setiap class. Composer memiliki anggota data pribadi untuk semua data yang ingin kita simpan di komposer. Kita memerlukan pengakses (“get” methods) dan pengubah (“set” methods), serta tindakan utama untuk class.

// composer.h, Maggie Johnson
// Description: The class for a Composer record.
// The default ranking is 10 which is the lowest possible.
// Notice we use const in C++ instead of #define.
const int kDefaultRanking = 10;

class Composer {
 public:
  // Constructor
  Composer();
  // Here is the destructor which has the same name as the class
  // and is preceded by ~. It is called when an object is destroyed
  // either by deletion, or when the object is on the stack and
  // the method ends.
  ~Composer();

  // Accessors and Mutators
  void set_first_name(string in_first_name);
  string first_name();
  void set_last_name(string in_last_name);
  string last_name();
  void set_composer_yob(int in_composer_yob);
  int composer_yob();
  void set_composer_genre(string in_composer_genre);
  string composer_genre();
  void set_ranking(int in_ranking);
  int ranking();
  void set_fact(string in_fact);
  string fact();

  // Methods
  // This method increases a composer's rank by increment.
  void Promote(int increment);
  // This method decreases a composer's rank by decrement.
  void Demote(int decrement);
  // This method displays all the attributes of a composer.
  void Display();

 private:
  string first_name_;
  string last_name_;
  int composer_yob_; // year of birth
  string composer_genre_; // baroque, classical, romantic, etc.
  string fact_;
  int ranking_;
};

Class Database juga mudah.

// database.h, Maggie Johnson
// Description: Class for a database of Composer records.
#include  <iostream>
#include "Composer.h"

// Our database holds 100 composers, and no more.
const int kMaxComposers = 100;

class Database {
 public:
  Database();
  ~Database();

  // Add a new composer using operations in the Composer class.
  // For convenience, we return a reference (pointer) to the new record.
  Composer& AddComposer(string in_first_name, string in_last_name,
                        string in_genre, int in_yob, string in_fact);
  // Search for a composer based on last name. Return a reference to the
  // found record.
  Composer& GetComposer(string in_last_name);
  // Display all composers in the database.
  void DisplayAll();
  // Sort database records by rank and then display all.
  void DisplayByRank();

 private:
  // Store the individual records in an array.
  Composer composers_[kMaxComposers];
  // Track the next slot in the array to place a new record.
  int next_slot_;
};

Perhatikan bagaimana kita telah mengenkapsulasi data khusus composer dengan cermat dalam class terpisah. Kita bisa menempatkan struct atau kelas di kelas Database untuk mewakili catatan Composer, dan mengaksesnya langsung di sana. Tapi itu akan menjadi "di bawah objektif", yaitu kita tidak memodelkan objek sebanyak sebaik yang kami bisa.

Anda akan melihat saat mulai mengerjakan implementasi class Composer dan Database, bahwa akan lebih rapi jika memiliki class Composer terpisah. Secara khusus, kita akan membuat memiliki operasi atomik yang terpisah pada objek Composer sangat menyederhanakan implementasi dari metode Display() di class Database.

Tentu saja, ada juga istilah "over-objectification", yaitu kita mencoba membuat semuanya menjadi class, atau kita memiliki lebih banyak class daripada yang diperlukan. Diperlukan latihan untuk menemukan keseimbangan yang tepat, dan Anda akan mendapati bahwa setiap programmer akan memiliki pendapat yang berbeda. 

Menentukan apakah terlalu banyak atau kurang objektif dapat diselesaikan dengan hati-hati membuat diagram kelas. Seperti yang disebutkan sebelumnya, penting untuk melatih class sebelum memulai {i>coding<i} dan ini dapat membantu menganalisis pendekatan Anda. Tanda umum notasi yang digunakan untuk tujuan ini adalah UML (Unified Modeling Language) Setelah memiliki class yang ditentukan untuk objek Composer dan Database, kita perlu antarmuka yang memungkinkan pengguna untuk berinteraksi dengan {i>database<i}. Menu sederhana akan menyelesaikan masalah ini:

Composer Database
---------------------------------------------
1) Add a new composer
2) Retrieve a composer's data
3) Promote/demote a composer's rank
4) List all composers
5) List all composers by rank
0) Quit

Kita dapat menerapkan antarmuka pengguna sebagai class, atau sebagai program prosedural. Tidak semua hal dalam program C++ harus berupa class. Bahkan, jika pemrosesan bersifat berurutan atau berorientasi pada tugas, seperti dalam program menu ini, Anda dapat menerapkannya secara terprogram. Penting untuk menerapkannya sedemikian rupa sehingga tetap menjadi "placeholder", yaitu, jika kita ingin membuat antarmuka pengguna grafis, kita harus tidak harus mengubah apa pun dalam sistem kecuali antarmuka pengguna.

Hal terakhir yang kita perlukan untuk menyelesaikan aplikasi adalah program untuk menguji class. Untuk class Composer, kita menginginkan program main() yang mengambil input, mengisi objek composer, lalu menampilkannya untuk memastikan class berfungsi dengan benar. Kita juga ingin memanggil semua metode class Composer.

// test_composer.cpp, Maggie Johnson
//
// This program tests the Composer class.

#include <iostream>
#include "Composer.h"
using namespace std;

int main()
{
  cout << endl << "Testing the Composer class." << endl << endl;

  Composer composer;

  composer.set_first_name("Ludwig van");
  composer.set_last_name("Beethoven");
  composer.set_composer_yob(1770);
  composer.set_composer_genre("Romantic");
  composer.set_fact("Beethoven was completely deaf during the latter part of "
    "his life - he never heard a performance of his 9th symphony.");
  composer.Promote(2);
  composer.Demote(1);
  composer.Display();
}

Kita memerlukan program pengujian serupa untuk class Database.

// test_database.cpp, Maggie Johnson
//
// Description: Test driver for a database of Composer records.
#include <iostream>
#include "Database.h"
using namespace std;

int main() {
  Database myDB;

  // Remember that AddComposer returns a reference to the new record.
  Composer& comp1 = myDB.AddComposer("Ludwig van", "Beethoven", "Romantic", 1770,
    "Beethoven was completely deaf during the latter part of his life - he never "
    "heard a performance of his 9th symphony.");
  comp1.Promote(7);

  Composer& comp2 = myDB.AddComposer("Johann Sebastian", "Bach", "Baroque", 1685,
    "Bach had 20 children, several of whom became famous musicians as well.");
  comp2.Promote(5);

  Composer& comp3 = myDB.AddComposer("Wolfgang Amadeus", "Mozart", "Classical", 1756,
    "Mozart feared for his life during his last year - there is some evidence "
    "that he was poisoned.");
  comp3.Promote(2);

  cout << endl << "all Composers: " << endl << endl;
  myDB.DisplayAll();
}

Perhatikan bahwa program pengujian sederhana ini adalah langkah pertama yang baik, tetapi mereka mengharuskan kita memeriksa {i>output<i} secara manual untuk memastikan program bekerja dengan benar. Sebagai sistem menjadi semakin besar, maka pemeriksaan {i> output<i} secara manual menjadi tidak praktis. Pada pelajaran berikutnya, kita akan memperkenalkan program pengujian verifikasi mandiri dalam bentuk pengujian unit.

Desain untuk aplikasi kita sekarang sudah selesai. Langkah selanjutnya adalah mengimplementasikan file .cpp untuk class dan antarmuka pengguna.Untuk memulai, lanjutkan dan salin/tempel .h dan kode {i>test driver<i} di atas ke dalam file, dan kompilasi.Gunakan {i>test driver<i} untuk menguji class Anda. Kemudian, implementasikan antarmuka berikut:

Composer Database
---------------------------------------------
1) Add a new composer
2) Retrieve a composer's data
3) Promote/demote a composer's rank
4) List all composers
5) List all composers by rank
0) Quit

Gunakan metode yang Anda tentukan di class Database untuk mengimplementasikan antarmuka pengguna. Buat metode Anda bebas error. Misalnya, peringkat harus selalu berada dalam rentang 1-10. Jangan biarkan siapa pun menambahkan 101 komposer, kecuali jika Anda berencana mengubah struktur data di class Database.

Ingat - semua kode Anda harus mengikuti konvensi coding kami, yang diulang di sini untuk kenyamanan Anda:

  • Setiap program yang kita tulis dimulai dengan komentar {i>header<i}, memberikan nama penulis, informasi kontak mereka, deskripsi singkat, dan penggunaan (jika relevan). Setiap fungsi/metode dimulai dengan komentar tentang operasi dan penggunaan.
  • Kami menambahkan komentar penjelasan menggunakan kalimat lengkap, setiap kali kode bukan mendokumentasikan dirinya sendiri, misalnya, jika pemrosesannya rumit, tidak jelas, menarik, atau penting.
  • Selalu gunakan nama deskriptif: variabel adalah kata-kata berhuruf kecil yang dipisahkan oleh _, seperti dalam my_variable. Nama fungsi/metode menggunakan huruf besar untuk menandai kata, seperti MyExcitingFunction(). Konstanta dimulai dengan "k" dan menggunakan huruf besar untuk menandai kata, seperti kDaysInWeek.
  • Indentasi dalam kelipatan dua. Tingkat pertama adalah dua spasi; jika indentasi lebih lanjut diperlukan, kita menggunakan empat spasi, enam spasi, dll.

Selamat Datang di Dunia Nyata!

Dalam modul ini, kami memperkenalkan dua alat sangat penting yang digunakan dalam sebagian besar tidak di organisasi lain. Yang pertama adalah alat {i>build<i}, dan yang kedua adalah manajemen konfigurasi sistem file. Kedua alat ini sangat penting dalam rekayasa software industri, tempat banyak engineer sering kali mengerjakan satu sistem besar. Alat ini membantu mengoordinasikan dan mengontrol perubahan pada code base, serta menyediakan cara yang efisien untuk mengompilasi dan menautkan sistem dari banyak file header dan program.

Makefile

Proses membangun program biasanya dikelola dengan alat build, yang mengompilasi dan menautkan file yang diperlukan, dalam urutan yang benar. Sering kali, file C++ memiliki dependensi, misalnya, sebuah fungsi yang dipanggil dalam satu program berada di program lain program ini. Atau, mungkin file header diperlukan oleh beberapa file .cpp yang berbeda. J alat build mengetahui urutan kompilasi yang benar dari dependensi ini. Tindakan ini juga hanya akan mengompilasi file yang telah berubah sejak build terakhir. Hal ini dapat menyimpan banyak waktu dalam sistem yang terdiri dari ratusan atau ribuan file.

Alat build open source yang disebut make umum digunakan. Untuk mempelajarinya, baca melalui ini artikel ini. Lihat apakah Anda dapat membuat grafik dependensi untuk aplikasi Database Composer, dan kemudian menerjemahkan ini menjadi makefile.Berikut adalah solusi kami.

Sistem Manajemen Konfigurasi

Alat kedua yang digunakan dalam software engineering industri adalah Configuration Management (CM). Ini digunakan untuk mengelola perubahan. Misalnya Bob dan Susan adalah penulis teknologi dan keduanya sedang mengerjakan pembaruan dalam panduan teknis. Selama rapat, mereka manajer menugaskan mereka setiap bagian dari dokumen yang sama untuk diperbarui.

Panduan teknis disimpan di komputer yang dapat diakses oleh Bob dan Susan. Tanpa adanya alat atau proses CM, sejumlah masalah bisa muncul. paket Premium AI skenario yang mungkin terjadi adalah komputer menyimpan dokumen mungkin diatur sehingga Bob dan Susan tidak dapat mengerjakan manual secara bersamaan. Ini akan memperlambat mereka turun secara signifikan.

Situasi yang lebih berbahaya akan muncul saat komputer penyimpanan mengizinkan dokumen dibuka oleh Bob dan Susan pada saat yang sama. Berikut ini yang mungkin terjadi:

  1. Bob membuka dokumen di komputernya dan mengerjakan bagiannya.
  2. Susan membuka dokumen di komputernya dan mengerjakan bagiannya.
  3. Bob menyelesaikan perubahannya dan menyimpan dokumennya di komputer penyimpanan.
  4. Susan menyelesaikan perubahan dan menyimpan dokumennya di komputer penyimpanan.

Ilustrasi ini menunjukkan masalah yang dapat terjadi jika tidak ada kontrol pada satu salinan manual teknis. Saat menyimpan perubahannya, Susan menimpa perubahan yang dibuat oleh Bob.

Inilah jenis situasi yang dapat dikontrol oleh sistem CM. Dengan CM berbeda, baik Bob maupun Susan "check out" salinan dokumen teknis manual dan mengerjakannya. Saat Bob mengembalikan perubahannya, sistem akan mengetahui bahwa Susan telah meminjam salinannya sendiri. Saat Susan memeriksa salinannya, sistem menganalisis perubahan yang dibuat Bob dan Susan dan membuat versi baru yang menggabungkan dua set perubahan.

Sistem CM memiliki sejumlah fitur selain mengelola perubahan serentak seperti yang dijelaskan di atas. Banyak sistem menyimpan arsip dari semua versi dokumen, mulai dari waktu pembuatannya. Dalam kasus manual teknis, hal ini bisa sangat membantu ketika pengguna memiliki panduan versi lama dan mengajukan pertanyaan kepada penulis teknologi. Sistem CM memungkinkan penulis teknologi untuk mengakses versi lama dan dapat untuk melihat apa yang dilihat pengguna.

Sistem CM sangat berguna dalam mengontrol perubahan yang dibuat pada software. Seperti sistem yang disebut sistem {i> Software Configuration Management<i} (SCM). Jika Anda mempertimbangkan jumlah besar file kode sumber individual di organisasi rekayasa software besar dan jumlah besar engineer yang harus melakukan perubahan pada file tersebut, jelas bahwa sistem SCM sangat penting.

Software Configuration Management

Sistem SCM didasarkan pada ide sederhana: salinan definitif file Anda disimpan di repositori pusat. Pengguna memeriksa salinan file dari repositori, mengerjakan salinan tersebut, dan kemudian memeriksanya kembali setelah selesai. Sistem SCM mengelola dan melacak revisi oleh beberapa orang terhadap satu set master tunggal. 

Semua sistem SCM menyediakan fitur-fitur penting berikut:

  • Pengelolaan Konkurensi
  • Pembuatan Versi
  • Sinkronisasi

Mari kita lihat masing-masing fitur ini secara lebih rinci.

Pengelolaan Konkurensi

Konkurensi mengacu pada pengeditan file secara serentak oleh lebih dari satu orang. Dengan repositori yang besar, kami ingin pengguna dapat melakukannya, tetapi hal ini dapat menyebabkan beberapa masalah.

Bayangkan sebuah contoh sederhana di domain engineering: Misalkan kita mengizinkan engineer memodifikasi file yang sama secara bersamaan di repositori pusat kode sumber. Client1 dan Client2 harus membuat perubahan pada file secara bersamaan:

  1. Client1 membuka bar.cpp.
  2. Client2 membuka bar.cpp.
  3. Client1 mengubah file dan menyimpannya.
  4. Client2 mengubah file dan menyimpannya dengan menimpa perubahan Client1.

Tentu saja, kami tidak menginginkan hal ini terjadi. Bahkan jika kita mengendalikan situasi dengan meminta dua insinyur untuk mengerjakan salinan terpisah dan bukan langsung pada master (seperti dalam ilustrasi di bawah), salinannya harus direkonsiliasi. Sebagian besar sistem SCM menangani masalah ini dengan mengizinkan beberapa engineer memeriksa file ("sinkronisasi" atau "update") dan melakukan perubahan sesuai kebutuhan. Sistem SCM kemudian menjalankan algoritma untuk menggabungkan perubahan saat file diperiksa kembali ("kirim" atau "commit") ke repositori.

Algoritma ini sederhana (minta para teknisi untuk menyelesaikan perubahan yang berkonflik) atau tidak terlalu sederhana (tentukan cara menggabungkan perubahan yang berkonflik secara cerdas dan hanya bertanya kepada seorang insinyur apakah sistem benar-benar macet).

Pembuatan Versi

Pembuatan versi mengacu pada melacak revisi file yang memungkinkan pembuatan ulang (atau rollback ke) versi file sebelumnya. Ini dilakukan dengan membuat salinan arsip setiap file saat masuk ke dalam repositori, atau dengan menyimpan setiap perubahan yang dibuat pada sebuah file. Kami dapat menggunakan arsip atau mengubah informasi untuk membuat versi sebelumnya. Sistem pembuatan versi juga dapat membuat laporan log tentang siapa yang check in, perubahan, kapan mereka check in, dan apa perubahan tersebut.

Sinkronisasi

Dengan beberapa sistem SCM, setiap file diperiksa masuk dan keluar dari repositori. Sistem yang lebih canggih memungkinkan Anda memeriksa lebih dari satu file sekaligus. Engineer memeriksa salinan repositori mereka sendiri yang lengkap (atau bagiannya) dan mengerjakan file sesuai kebutuhan. Kemudian, mereka melakukan commit perubahan kembali ke repositori master secara berkala, dan memperbarui salinan pribadi mereka sendiri agar tetap mendapatkan perubahan terbaru yang dibuat orang lain. Proses ini disebut sinkronisasi atau pembaruan.

Subversi

Subversion (SVN) adalah sistem kontrol versi open source. Aplikasi ini memiliki semua fitur yang dijelaskan di atas.

SVN mengadopsi metodologi sederhana saat terjadi konflik. Konflik adalah ketika dua insinyur atau lebih membuat perubahan yang berbeda pada area basis kode yang sama dan kemudian mengirimkan perubahannya. SVN hanya memperingatkan para insinyur bahwa ada konflik - para insinyur untuk menyelesaikannya.

Kita akan menggunakan SVN selama kursus ini untuk membantu Anda memahami manajemen konfigurasi. Sistem semacam ini sangat umum di industri.

Langkah pertama adalah menginstal SVN di sistem Anda. Klik di sini untuk mengetahui petunjuknya. Temukan sistem operasi Anda dan download biner yang sesuai.

Beberapa Terminologi SVN

  • Revisi: Perubahan pada file atau kumpulan file. Revisi adalah satu "ringkasan" dalam proyek yang terus berubah.
  • Repositori: Salinan master tempat SVN menyimpan histori revisi lengkap project. Setiap project memiliki satu repositori.
  • Salinan Kerja: Salinan tempat engineer membuat perubahan pada project. Dapat ada banyak salinan kerja dari project tertentu yang masing-masing dimiliki oleh setiap engineer.
  • Check Out: Untuk meminta salinan yang berfungsi dari repositori. Salinan yang berfungsi sama dengan keadaan proyek ketika diperiksa.
  • Commit: Untuk mengirim perubahan dari salinan kerja Anda ke repositori pusat. Juga dikenal sebagai check-in atau kirim.
  • Update: Untuk menerapkan perubahan orang lain dari repositori ke salinan kerja Anda, atau untuk menunjukkan apakah salinan kerja Anda memiliki perubahan yang belum di-commit. Hal ini sama dengan sinkronisasi, seperti yang dijelaskan di atas. Jadi, {i>update/sync<i} membawa salinan kerja dengan salinan repositori yang sudah diperbarui.
  • Konflik: Situasi saat dua engineer mencoba melakukan perubahan pada area file yang sama. SVN menunjukkan adanya konflik, tetapi engineer harus menyelesaikannya.
  • Pesan log: Komentar yang Anda lampirkan ke revisi saat melakukan commit, yang menjelaskan perubahan Anda. Log tersebut memberikan ringkasan tentang apa yang terjadi dalam sebuah proyek.

Setelah Anda menginstal SVN, kita akan menjalankan beberapa perintah dasar. Tujuan hal pertama yang harus dilakukan adalah mengatur repositori dalam direktori tertentu. Berikut adalah perintahnya:

$ svnadmin create /usr/local/svn/newrepos
$ svn import mytree file:///usr/local/svn/newrepos/project -m "Initial import"
Adding         mytree/foo.c
Adding         mytree/bar.c
Adding         mytree/subdir
Adding         mytree/subdir/foobar.h

Committed revision 1.

Perintah import menyalin konten direktori mytree ke dalam repositori. Kita dapat melihat direktori di repositori dengan perintah list

$ svn list file:///usr/local/svn/newrepos/project
bar.c
foo.c
subdir/

Impor tidak membuat salinan yang berfungsi. Untuk melakukannya, Anda harus menggunakan svn checkout. Langkah ini akan membuat salinan hierarki direktori yang berfungsi. Mari kita lakukan sekarang:

$ svn checkout file:///usr/local/svn/newrepos/project
A    foo.c
A    bar.c
A    subdir
A    subdir/foobar.h
…
Checked out revision 215.

Setelah memiliki salinan yang berfungsi, Anda dapat membuat perubahan pada file dan direktori di sana. Salinan pekerjaan Anda sama seperti koleksi file dan direktori lainnya - Anda dapat menambahkan atau mengeditnya, memindahkannya, bahkan menghapus seluruh salinan pekerjaan. Perhatikan bahwa jika Anda menyalin dan memindahkan file dalam salinan kerja Anda, Anda harus menggunakan svn copy dan svn move, bukan perintah sistem operasi. Untuk menambahkan file baru, gunakan svn add dan untuk menghapus file menggunakan svn delete. Jika yang ingin Anda lakukan adalah mengedit, cukup buka file dengan editor Anda dan langsung edit!

Ada beberapa nama direktori standar yang sering digunakan dengan Subversion. "Trunk" direktori memegang garis utama pengembangan untuk proyek Anda. "Cabang" direktori menyimpan versi cabang apa pun yang mungkin Anda kerjakan.

$ svn list file:///usr/local/svn/repos
/trunk
/branches

Jadi, misalnya Anda telah membuat semua perubahan yang diperlukan pada salinan kerja dan ingin menyinkronkannya dengan repositori. Jika banyak insinyur lain yang bekerja di area repositori ini, penting untuk selalu memperbarui salinan kerja Anda. Anda dapat menggunakan perintah svn status untuk melihat perubahan yang telah dilakukan dilakukan.

A       subdir/new.h      # file is scheduled for addition
D       subdir/old.c        # file is scheduled for deletion
M       bar.c                  # the content in bar.c has local modifications

Perhatikan bahwa ada banyak flag pada perintah status untuk mengontrol output ini. Jika Anda ingin melihat perubahan tertentu dalam file yang diubah, gunakan svn diff.

$ svn diff bar.c
Index: bar.c
===================================================================
--- bar.c	(revision 5)
+++ bar.c	(working copy)
## -1,18 +1,19 ##
+#include
+#include

 int main(void) {
-  int temp_var;
+ int new_var;
...

Terakhir, untuk memperbarui salinan yang berfungsi dari repositori, gunakan perintah svn update.

$ svn update
U  foo.c
U  bar.c
G  subdir/foobar.h
C  subdir/new.h
Updated to revision 2.

Ini adalah salah satu tempat terjadinya konflik. Pada {i>output<i} di atas, huruf "U" menunjukkan tidak ada perubahan yang dilakukan pada versi repositori file ini dan pembaruan selesai. "G" berarti terjadi penggabungan. Versi repositori memiliki telah diubah, namun perubahan tersebut tidak bertentangan dengan perubahan Anda. "C" menunjukkan konflik. Ini berarti bahwa perubahan dari repositori tumpang tindih dengan milik Anda, dan sekarang Anda harus memilih di antaranya.

Untuk setiap file yang memiliki konflik, Subversion akan menempatkan tiga file dalam salinan kerja Anda:

  • {i>file.mine<i}: Ini adalah file Anda seperti yang ada di salinan kerja Anda sebelum memperbarui salinan pekerjaan Anda.
  • {i>file.rOLDREV<i}: Ini adalah file yang Anda periksa dari repositori sebelum membuat perubahan.
  • file.rNEWREV: File ini adalah versi terkini dalam repositori.

Anda dapat melakukan salah satu dari tiga hal berikut untuk menyelesaikan konflik:

  • Periksa file dan lakukan penggabungan secara manual.
  • Salin salah satu file sementara yang dibuat oleh SVN ke versi teks yang berfungsi.
  • Jalankan svn revert untuk menghapus semua perubahan.

Setelah konflik teratasi, Anda memberi tahu SVN dengan menjalankan svn terselesaikan. Tindakan ini akan menghapus tiga file sementara dan SVN tidak lagi melihat file dalam status konflik.

Hal terakhir yang harus dilakukan adalah meng-commit versi akhir Anda ke repositori. Ini dilakukan dengan perintah svn commit. Ketika Anda meng-commit perubahan, Anda perlu untuk memberikan pesan log, yang menjelaskan perubahan Anda. Pesan log ini terlampir pada revisi yang Anda buat.

svn commit -m "Update files to include new headers."  

Ada banyak hal yang perlu dipelajari tentang SVN, dan bagaimana SVN dapat mendukung software berukuran besar proyek teknik. Ada banyak referensi yang tersedia di web - cukup lakukan penelusuran Google pada "Subversion".

Untuk latihan, buat repositori untuk sistem Database Composer, dan impor semua file Anda. Kemudian periksa salinan yang berfungsi dan lakukan perintah yang dijelaskan di atas.

Referensi

Buku Subversi Online

Artikel Wikipedia tentang SVN

Situs subversi

Aplikasi: Sebuah Studi dalam Anatomi

Lihat eSkeletons dari The University dari Texas di Austin