@SebastianRedl sudah memberikan jawaban langsung yang sederhana, tetapi beberapa penjelasan tambahan mungkin berguna.
TL; DR = ada aturan gaya untuk membuat konstruktor tetap sederhana, ada alasannya, tetapi alasan itu kebanyakan berhubungan dengan gaya pengkodean yang bersejarah (atau hanya buruk). Penanganan pengecualian dalam konstruktor didefinisikan dengan baik, dan destruktor masih akan dipanggil untuk variabel dan anggota lokal yang sepenuhnya dibangun, yang berarti tidak boleh ada masalah dalam kode C ++ idiomatik. Aturan gaya tetap ada, tetapi biasanya itu bukan masalah - tidak semua inisialisasi harus ada di konstruktor, dan khususnya tidak harus konstruktor itu.
Ini adalah aturan gaya umum bahwa konstruktor harus melakukan minimum absolut yang mereka bisa untuk mengatur keadaan valid yang ditentukan. Jika inisialisasi Anda lebih kompleks, itu harus ditangani di luar konstruktor. Jika tidak ada nilai murah untuk diinisialisasi yang bisa disiapkan oleh konstruktor Anda, Anda harus melemahkan invarants yang diberlakukan oleh kelas Anda untuk menambahkannya. Sebagai contoh jika mengalokasikan penyimpanan untuk kelas Anda untuk mengelola terlalu mahal, tambahkan status null yang belum dialokasikan, karena tentu saja memiliki status case khusus seperti null tidak pernah menyebabkan masalah pada siapa pun. Ahem.
Meskipun umum, tentu dalam bentuk ekstrem ini sangat jauh dari absolut. Secara khusus, seperti yang ditunjukkan oleh sarkasme saya, saya berada di kamp yang mengatakan bahwa pelemahan invarian hampir selalu merupakan harga yang terlalu tinggi. Namun, ada alasan di balik aturan gaya, dan ada cara untuk memiliki konstruktor minimal dan invarian yang kuat.
Alasannya berkaitan dengan pembersihan destruktor otomatis, khususnya dalam menghadapi pengecualian. Pada dasarnya, harus ada titik yang terdefinisi dengan baik ketika kompiler menjadi bertanggung jawab untuk memanggil destruktor. Saat Anda masih dalam panggilan konstruktor, objek tidak harus sepenuhnya dibangun, jadi tidak sah untuk memanggil destruktor untuk objek itu. Oleh karena itu, tanggung jawab untuk merusak objek hanya ditransfer ke kompiler ketika konstruktor berhasil menyelesaikan. Ini dikenal sebagai RAII (Alokasi Sumber Daya Adalah Inisialisasi) yang sebenarnya bukan nama terbaik.
Jika lemparan pengecualian terjadi di dalam konstruktor, bagian apa pun yang dibangun perlu dibersihkan secara eksplisit, biasanya dalam a try .. catch
.
Namun, komponen objek yang telah berhasil dibangun sudah menjadi tanggung jawab penyusun. Ini berarti bahwa dalam praktiknya, itu bukan masalah besar. misalnya
classname (args) : base1 (args), member2 (args), member3 (args)
{
}
Badan konstruktor ini kosong. Selama konstruktor untuk base1
, member2
dan member3
adalah pengecualian aman, tidak ada yang perlu khawatir tentang. Misalnya, jika konstruktor member2
melempar, konstruktor tersebut bertanggung jawab untuk membersihkan dirinya sendiri. Pangkalan base1
sudah benar-benar dibangun, sehingga destruktornya akan secara otomatis dipanggil. member3
bahkan tidak pernah sebagian dibangun, jadi tidak perlu pembersihan.
Bahkan ketika ada badan, variabel lokal yang dibangun sepenuhnya sebelum pengecualian dilemparkan akan secara otomatis dihancurkan, sama seperti fungsi lainnya. Badan konstruktor yang menyulap pointer mentah, atau "memiliki" semacam keadaan implisit (disimpan di tempat lain) - biasanya berarti panggilan fungsi begin / memperoleh harus dicocokkan dengan panggilan akhir / rilis - dapat menyebabkan pengecualian masalah keselamatan, tetapi masalah sebenarnya ada gagal mengelola sumber daya dengan benar melalui kelas. Misalnya jika Anda mengganti pointer mentah dengan unique_ptr
dalam konstruktor, destruktor untuk unique_ptr
akan dipanggil secara otomatis jika diperlukan.
Masih ada alasan lain yang orang berikan untuk memilih konstruktor do-the-minimum. Salah satunya adalah karena aturan gaya ada, banyak orang menganggap panggilan konstruktor itu murah. Salah satu cara untuk mendapatkannya, namun masih memiliki invarian yang kuat, adalah dengan memiliki kelas pabrik / pembangun terpisah yang memiliki invarian yang melemah, dan yang menetapkan nilai awal yang diperlukan menggunakan (kemungkinan banyak) panggilan fungsi anggota normal. Setelah Anda memiliki keadaan awal yang Anda butuhkan, berikan objek itu sebagai argumen kepada konstruktor untuk kelas dengan invarian yang kuat. Itu bisa "mencuri nyali" objek invarian lemah - pindahkan semantik - yang merupakan operasi yang murah (dan biasanya noexcept
).
Dan tentu saja Anda dapat membungkusnya dalam suatu make_whatever ()
fungsi, jadi penelepon dari fungsi itu tidak perlu melihat instance kelas yang dilemahkan-invarian.