Langkah Berikutnya

Pengantar Pemrograman dan C++

Tutorial online ini dilanjutkan dengan konsep lanjutan lainnya - harap 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 perlu generator angka acak untuk menentukan jarak musuh bulat. 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 selama pelaksanaan program. Berikut ini 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. Sewaktu program dijalankan, semua variabel disimpan dalam memori, masing-masing di alamat atau lokasinya sendiri. Pointer adalah jenis variabel khusus yang berisi alamat memori daripada daripada 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 "point" ke lokasi penyimpanan ("yang ditunjuk"). Jadi dalam contoh di atas, {i>intptr<i} menunjuk ke {i>pointee<i} 5.

    Perhatikan penggunaan kata "baru" untuk mengalokasikan memori ke bilangan bulat penerima tugas. Ini adalah sesuatu yang harus kita lakukan sebelum mencoba mengakses penerima tugas.

    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 {i>dereference<i} di C. Salah satu kesalahan paling umum Pemrogram C/C++ saat bekerja dengan pointer lupa untuk menginisialisasi orang yang ditunjuk. Terkadang hal ini dapat menyebabkan error runtime karena kita sedang mengakses lokasi dalam memori yang berisi data yang tidak diketahui. Jika kita mencoba dan memodifikasi ini data, kita dapat menyebabkan kerusakan memori yang halus sehingga {i>bug<i} yang sulit dilacak.

  3. Penunjukan pointer di antara dua pointer membuatnya menunjuk ke pointer yang sama. Jadi penugasannya y = x; menunjukkan y titik ke titik yang sama dengan x. Penetapan kursor tidak menyentuh orang yang dituju. Perintah ini hanya mengubah satu pointer untuk memiliki lokasi yang sama sebagai 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 ini:

1. Alokasikan dua pointer x dan y. Mengalokasikan pointer akan tidak mengalokasikan titik akses apa pun.
2. Alokasikan ujung tugas dan tetapkan x agar mengarah ke titik 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 mengarah ke orang yang sama -- mereka "berbagi".
6. Cobalah untuk membatalkan referensi y untuk menyimpan 13 pada titik aksesnya. Kali ini berhasil, karena tugas sebelumnya memberi Anda poin.

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

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. 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 heap dan runtime) dalam pelajaran lain. Untuk saat ini, cukup menganggap heap sebagai penyimpanan bebas 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;

Dalam contoh terakhir ini, kami menunjukkan bagaimana pointer digunakan untuk meneruskan nilai melalui referensi tentang suatu 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). Tetapi C++ menyediakan cara yang lebih sederhana untuk meneruskan nilai ke fungsi dengan walaupun huruf "C" lama masih ada yang berhasil.

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 bernama Accelerator() yang mengambil input kecepatan kendaraan, dan jumlah. Fungsi ini menambahkan jumlah pada 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 Sains: Ilmu Komputer

Latihan

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

Ingat bahwa Program yang Bagus...

... diurai secara logis menjadi fungsi di mana salah satu 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 "keajaiban" apa pun dalam program ini.

... memiliki antarmuka pengguna yang mudah digunakan.

Latihan Pemanasan

  • Latihan 1

    Bilangan bulat 36 memiliki sifat khusus: bilangan bulat sempurna, dan juga jumlah bilangan bulat dari 1 sampai 8. Angka berikutnya adalah 1225 yang adalah 352, dan jumlah semua bilangan bulat dari 1 sampai 49. Temukan nomor berikutnya itu adalah kuadrat sempurna dan juga jumlah dari deret 1...n. Nomor berikutnya ini mungkin lebih besar dari 32767. Anda dapat menggunakan fungsi perpustakaan yang Anda ketahui, (atau rumus matematika) untuk membuat program Anda 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: tergantung pada komputer dan program Anda, butuh 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 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-cara untuk menangani banyak data yang masuk ke dalam suatu program (tetap disesuaikan!), cukup proses satu buku dalam satu waktu dan tampilkan layar {i>output<i} 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 membangun desain perangkat lunak: Salah satu caranya adalah membuatnya sangat sederhana sehingga jelas kekurangan, dan cara lainnya adalah membuatnya sangat rumit sehingga tidak ada kekurangan yang jelas. Metode pertama jauh lebih sulit." - K.A.R. Hoare

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

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 mungkin mengubah status objek, atau mungkin memanggil tindakan objek lain. Premis dasarnya adalah bahwa sebuah objek "mengetahui" bagaimana untuk melakukan sesuatu sendiri.

Dalam desain OO, kita mendefinisikan objek fisik dalam hal class dan objek; atribut dan perilaku mereka. Umumnya ada banyak objek dalam program OO. Namun, banyak dari 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 objeknya mendefinisikan 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. Menentukan tindakan setiap class dan cara melakukan beberapa tindakan dari satu class diimplementasikan menggunakan tindakan class terkait lainnya.

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

Untuk sistem database komposer, kita memerlukan class Composer yang mengenkapsulasi semua data yang ingin kita simpan pada satu komposer. 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 objek satu per satu dengan memanggil tindakan tampilan objek Composer.

Terakhir, kita membutuhkan semacam antarmuka pengguna untuk menyediakan operasi interaktif pada {i>database<i}. 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 {i>placeholder<i} yang yang dapat kita isi nanti.

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

Untuk database komposer, kita menentukan class Composer yang berisi elemen data yang ingin kita simpan di setiap komposer. {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. Tapi apakah ada pertanyaan tentang bagaimana kelas berhubungan dan berinteraksi, cara terbaik adalah menggambarnya terlebih dahulu, dan mempelajari detailnya sebelum memulai untuk menulis kode.

Setelah kami memiliki gambaran yang jelas tentang desain dan mengevaluasinya (lebih lanjut tentang segera), kita akan menentukan antarmuka untuk setiap class. Kami tidak perlu memikirkan penerapan detail pada tahap ini - apa yang dimaksud dengan atribut dan tindakan, dan bagian apa suatu kelas' status dan tindakan 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 (metode “get”) dan mutator (metode “set”), 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 simpel.

// 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 merangkum data spesifik komposer dengan cermat dalam . 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 Composer dan Database , akan jauh lebih mudah jika Anda 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 istilah "obyektif yang berlebihan" dengan kita mencoba membuat semuanya menjadi kelas, atau kita memiliki lebih banyak kelas daripada yang dibutuhkan. Dibutuhkan latihan untuk menemukan keseimbangan yang tepat, dan Anda akan menemukan bahwa setiap {i>programmer<i} akan berbeda pendapat.

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 lakukan triknya:

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 kelas, atau sebagai program prosedural. Bukan semua yang ada dalam program C++ harus berupa class. Bahkan, jika pemrosesan berurutan atau berorientasi pada tugas, seperti dalam program menu ini, tidak masalah untuk menerapkannya secara prosedural. 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 ingin program main() yang mengambil input, mengisi komposer, 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. Di pelajaran selanjutnya, kami akan memperkenalkan program tes pemeriksaan 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 menerapkan antarmuka pengguna. Membuat metode Anda anti-error. Misalnya, peringkat harus selalu berada dalam rentang 1-10. Jangan biarkan siapa pun menambahkan 101 komposer, kecuali jika Anda berencana untuk mengubah struktur data di kelas 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 dalam MyExcitationFunction(). Konstanta dimulai dengan "k" dan gunakan huruf besar untuk menandai kata, seperti pada kDaysInWeek.
  • Indentasi adalah kelipatan dua. Tingkat pertama adalah dua spasi; jika lebih jauh indentasi 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 perangkat lunak industri, di mana banyak insinyur sering bekerja pada satu sistem besar. Alat-alat ini membantu mengkoordinasikan dan mengontrol perubahan pada {i>code base<i}, dan menyediakan cara yang efisien untuk dan menghubungkan sistem dari banyak file program dan {i>header<i}.

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, fungsi yang disebut 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. Materi ini akan juga hanya 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 hal yang dapat 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 satu salinan panduan teknis. Ketika Susan menyimpan perubahan, dia menimpa lagu yang dibuat Bobi.

Inilah jenis situasi yang dapat dikendalikan oleh sistem CM. Dengan CM berbeda, baik Bob maupun Susan "check out" salinan dokumen teknis manual dan mengerjakannya. Ketika Bob memeriksa kembali perubahannya, sistem akan tahu bahwa Susan telah memeriksa 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 sejumlah besar file kode sumber individu dalam program organisasi dan banyak sekali insinyur yang harus membuat perubahan pada mereka, sudah jelas bahwa sistem SCM sangatlah penting.

Manajemen Konfigurasi Perangkat Lunak

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

Semua sistem SCM menyediakan fitur-fitur penting berikut:

  • Pengelolaan Konkurensi
  • Pembuatan Versi
  • Sinkronisasi

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

Pengelolaan Konkurensi

Konkurensi adalah pengeditan file secara bersamaan oleh lebih dari satu orang. Dengan repositori yang besar, kami ingin orang dapat melakukan ini, tetapi hal itu dapat mengarah terhadap beberapa masalah.

Bayangkan satu contoh sederhana di domain teknik: 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 untuk 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. Paling sering Sistem SCM menangani masalah ini dengan mengizinkan beberapa insinyur untuk memeriksa file ("sinkronisasi" atau "pembaruan") dan buat perubahan yang diperlukan. SCM sistem kemudian menjalankan algoritma untuk menggabungkan perubahan saat file diperiksa kembali ("submit" 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 pelacakan revisi file yang memungkinkan untuk membuat ulang (atau melakukan roll back 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 {i>check in<i}, perubahan, kapan mereka {i>check in<i}, dan apa perubahan tersebut.

Sinkronisasi

Pada beberapa sistem SCM, masing-masing file masuk dan keluar dari repositori. Sistem yang lebih canggih memungkinkan Anda memeriksa lebih dari satu file sekaligus. Insinyur memeriksa sendiri, menyelesaikan, salinan repositori (atau bagiannya) dan pada file sesuai kebutuhan. Kemudian, mereka meng-commit perubahan kembali ke repositori master secara berkala, dan memperbarui salinan pribadi mereka untuk tetap mengikuti perubahan dilakukan oleh orang lain. Proses ini disebut sinkronisasi atau pembaruan.

Subversi

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

SVN menggunakan 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 seperti itu sangat umum di industri.

Langkah pertama adalah menginstal SVN di sistem Anda. Klik di sini untuk petunjuk. 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.
  • {i>Working Copy<i}: Salinan di mana seorang insinyur membuat perubahan pada proyek. Ada bisa banyak salinan kerja dari proyek tertentu yang masing-masing dimiliki oleh seorang insinyur individu.
  • 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.
  • Pembaruan: Untuk mengajak orang lain perubahan dari repositori ke salinan kerja Anda, atau untuk menunjukkan apakah salinan kerja Anda memiliki perubahan yang tidak di-commit. Ini adalah sama dengan sinkronisasi, seperti dijelaskan di atas. Jadi, {i>update/sync<i} membawa salinan kerja dengan salinan repositori yang sudah diperbarui.
  • Konflik: Situasi ketika dua insinyur mencoba untuk melakukan perubahan pada area dari sebuah file. 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 berikut:

$ 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 akan 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, katakanlah Anda telah membuat semua perubahan yang diperlukan pada salinan kerja dan Anda 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 berkonflik, {i>Subversion<i} menempatkan tiga file dalam salin:

  • {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 refund untuk menghapus semua perubahan.

Setelah konflik diatasi, Anda harus memberi tahu SVN dengan menjalankan svn terselesaikan. Tindakan ini akan menghapus tiga file sementara dan SVN tidak lagi melihat file tersebut 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 dilampirkan 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 sumber daya yang tersedia di web - hanya saja lakukan penelusuran Google tentang "Subversion".

Untuk latihan, buat repositori untuk sistem Database Composer Anda, lalu 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