Saya memiliki kelas CPP yang konstruktornya melakukan beberapa operasi. Beberapa operasi ini mungkin gagal. Saya tahu bahwa konstruktor tidak mengembalikan apa pun.
Pertanyaan saya adalah,
Apakah diizinkan melakukan beberapa operasi selain yang menginisialisasi anggota dalam konstruktor?
Apakah mungkin untuk memberi tahu fungsi panggilan bahwa beberapa operasi dalam konstruktor telah gagal?
Bisakah saya membuat
new ClassName()
pengembalian NULL jika beberapa kesalahan terjadi di konstruktor?
object-oriented
c++
MayurK
sumber
sumber
Square
, dengan konstruktor yang mengambil satu parameter, panjang sisi, Anda ingin memeriksa apakah nilai itu lebih besar dari 0.Jawaban:
Ya, meskipun beberapa standar pengkodean mungkin melarangnya.
Iya nih. Cara yang disarankan adalah dengan melemparkan pengecualian. Atau, Anda dapat menyimpan informasi kesalahan di dalam objek dan memberikan metode untuk mengakses informasi ini.
Tidak.
sumber
Anda bisa membuat metode statis yang melakukan perhitungan dan mengembalikan objek jika sukses atau tidak.
Bergantung pada bagaimana konstruksi objek ini dilakukan, mungkin lebih baik untuk membuat objek lain yang memungkinkan konstruksi objek dalam metode yang tidak statis.
Memanggil konstruktor secara tidak langsung sering disebut sebagai "pabrik".
Ini juga akan memungkinkan Anda untuk mengembalikan objek-nol, yang mungkin merupakan solusi yang lebih baik daripada mengembalikan null.
sumber
NULL
. Misalnya,int foo() { return NULL
Anda benar-benar akan mengembalikan0
(nol), objek integer. Jikastd::string foo() { return NULL; }
Anda tidak sengaja meneleponstd::string::string((const char*)NULL)
yang merupakan Perilaku Tidak Terdefinisi (NULL tidak mengarah ke string yang diakhiri \ 0).int
. Misalnyastd::allocator<int>
adalah pabrik yang sangat waras.@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
Badan konstruktor ini kosong. Selama konstruktor untuk
base1
,member2
danmember3
adalah pengecualian aman, tidak ada yang perlu khawatir tentang. Misalnya, jika konstruktormember2
melempar, konstruktor tersebut bertanggung jawab untuk membersihkan dirinya sendiri. Pangkalanbase1
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 untukunique_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.sumber
main
fungsi atau variabel statis / global. Objek yang dialokasikan menggunakannew
, tidak dimiliki hingga Anda menetapkan tanggung jawab itu, tetapi pointer pintar memiliki objek yang dialokasikan tumpukan yang dirujuknya, dan wadah memiliki struktur datanya. Pemilik dapat memilih untuk menghapus lebih awal, pemilik destructor pada akhirnya bertanggung jawab.