Object yang bagus biasanya lahir sudah terisi: sebuah pemesanan datang dengan nomor dan nama pelanggannya, sebuah titik datang dengan koordinatnya. Memaksa pemanggil membuat object kosong lalu mengisinya lewat beberapa fungsi terpisah membuka celah lupa-isi. Constructor berparameter (parameterized constructor) menyelesaikan masalah ini dengan menerima data langsung saat object dibuat. Chapter ini juga merapatkan dua topik yang berdekatan: bagaimana compiler memilih salah satu dari beberapa constructor berparameter, dan dua sintaks pembuatan object yang tersedia di C++.
Mengirim Data Saat Object Dibuat #
Constructor berparameter ditulis seperti constructor lain, tetapi dengan daftar parameter di dalam kurungnya. Saat kita menulis Pemesanan p(101, "Siti");, compiler menjalankan constructor yang cocok dan memakai argumen tersebut untuk mengisi object. Inilah bentuk paling langsung dari inisialisasi yang aman: tidak ada momen ketika object hidup tanpa data, dan tidak ada urutan pemanggilan fungsi yang bisa tertukar.
Parameternya bebas — boleh tipe fundamental, std::string, pointer, atau reference. Konvensi yang sudah kita praktikkan sejak Chapter Constructor tetap berlaku: parameter bertipe class seperti std::string sebaiknya dilewatkan sebagai const std::string& agar tidak disalin ulang. Di dalam badan constructor, kita tinggal menugaskan nilai parameter ke data member, dan boleh pula menambahkan validasi — misalnya menolak jumlah barang negatif.
Overloading Constructor #
Seperti fungsi biasa, constructor boleh di-overload: beberapa constructor dengan daftar parameter berbeda hidup berdampingan dalam satu class. Compiler memilih berdasarkan jumlah dan tipe argumen yang dikirim, memakai aturan resolusi overload yang sama dengan pemanggilan fungsi biasa. Bila tidak ada yang cocok — misalnya kita mengirim dua argumen tetapi hanya ada overload satu dan tiga parameter — kompilasi gagal dengan pesan no matching function, bukan error saat program berjalan.
Perlu diingat dua hal yang berlaku khusus untuk constructor. Pertama, jika ada constructor berparameter dan kita ingin object tanpa argumen tetap bisa dibuat, default constructor harus tetap ada — topik yang baru kita bahas pada Chapter Default Constructor. Kedua, resolusi overload constructor mengikuti aturan konversi implisit: satu argumen 42 akan dengan senang hati masuk ke constructor yang meminta double. Untuk class yang ketat, aturan tambahan seperti explicit bisa mempersempitnya — itu akan kita sentuh saat membahas operator konversi di bagian lanjutan Section ini.
Kurung atau Kurawal: Dua Sintaks Pembuatan Object #
C++ memberi dua sintaks untuk membuat object dengan argumen: direct initialization memakai kurung — Pemesanan p(101, "Siti"); — dan list initialization memakai kurawal — Pemesanan p{101, "Siti"};. Untuk constructor biasa, keduanya memanggil constructor yang sama; perbedaan yang paling terasa adalah kurawal menolak narrowing conversion (misalnya double ke int yang menghilangkan bagian desimal), sehingga kesalahan penyusutan tipe tertangkap saat kompilasi.
Kurawal punya satu gotcha yang perlu diwaspadai: bila class punya constructor menerima std::initializer_list, bentuk kurawal akan memilihnya terlebih dahulu — topik yang muncul nanti saat membahas container dan vector. Untuk materi Section ini, keduanya aman dipakai. Dalam kode industri, kurung lebih lazim untuk constructor biasa, sedangkan kurawal sering dipilih untuk memberi penekanan bahwa yang terjadi adalah inisialisasi.
Contoh: Tiga Overload untuk Tiga Tingkat Kelengkapan #
Program berikut memakai satu class dengan tiga constructor berparameter, lalu membuat tiga object dengan sintaks berbeda:
#include <iostream>
#include <string>
class Pemesanan {
public:
int id;
std::string pelanggan;
int jumlahBarang;
double total;
// Constructor dengan satu parameter
Pemesanan(int i) {
id = i;
pelanggan = "(tanpa nama)";
jumlahBarang = 0;
total = 0.0;
}
// Overload: dua parameter
Pemesanan(int i, const std::string& nama) {
id = i;
pelanggan = nama;
jumlahBarang = 0;
total = 0.0;
}
// Overload: empat parameter
Pemesanan(int i, const std::string& nama, int jumlah, double bayar) {
id = i;
pelanggan = nama;
jumlahBarang = jumlah;
total = bayar;
}
void cetak() {
std::cout << "#" << id << " " << pelanggan << " | "
<< jumlahBarang << " item | Rp" << total << "\n";
}
};
int main() {
Pemesanan cepat(101); // memilih overload 1 parameter
Pemesanan standar(102, "Siti"); // memilih overload 2 parameter
Pemesanan lengkap{103, "Dewi", 3, 450000}; // kurung kurawal, 4 parameter
cepat.cetak();
standar.cetak();
lengkap.cetak();
return 0;
}Output yang Dihasilkan:
#101 (tanpa nama) | 0 item | Rp0
#102 Siti | 0 item | Rp0
#103 Dewi | 3 item | Rp450000Bedah baris demi baris:
- Baris 9-16 (
Pemesanan(int i) { ... }): overload paling ringkas. Field yang tidak dikirim pemanggil diisi nilai netral —"(tanpa nama)",0,0.0— sehingga object tetap konsisten walau datanya minim. - Baris 19-26 dan 29-36: dua overload berikutnya menambah parameter. Setiap overload mengulang penugasan yang sama untuk field yang sudah diisi oleh overload lain — kita lihat akibatnya sebentar lagi.
- Baris 48 (
Pemesanan cepat(101);): satu argumenintcocok dengan overload pertama. Compiler tidak melirik dua overload lainnya. - Baris 49 (
Pemesanan standar(102, "Siti");): dua argumen —intdan string literal — cocok dengan overload kedua; string literal dikonversi otomatis menjadistd::stringmelalui parameterconst std::string&. - Baris 50 (
Pemesanan lengkap{103, "Dewi", 3, 450000};): list initialization dengan kurawal memilih overload empat parameter, sama seperti kurung. Tiga baris output terakhir memperlihatkan ketiga object dengan state masing-masing.
Duplikasi yang Menyisakan Pekerjaan Rumah #
Sekali lagi perhatikan badan ketiga constructor: penugasan id = i; pelanggan = nama; ditulis dua kali, tiga kali untuk sebagian field. Pola ini bekerja, tetapi menumpulkan kode dan membuka celah: bila nanti ada field baru, kita harus mengingat menambah penugasannya di setiap overload. Ada dua perbaikan di depan kita — member initializer list yang menggantikan penugasan di badan dengan inisialisasi sejati (Chapter berikutnya), dan delegating constructor yang membuat satu constructor menyerahkan pekerjaannya ke constructor lain (Chapter setelahnya).
Kita sudah bisa membuat object dengan data lengkap sejak awal. Sekarang saatnya merapikan cara pengisiannya — dari penugasan di badan constructor menuju inisialisasi yang lebih efisien dan satu-satunya jalur yang sah untuk member const dan reference.
Rujukan resmi: Constructors (cppreference) , Initialization (cppreference) .