Katakanlah saya memiliki kelas Foobar
yang menggunakan (tergantung pada) kelas Widget
. Pada hari-hari baik, Widget
wolud dinyatakan sebagai bidang Foobar
, atau mungkin sebagai penunjuk pintar jika perilaku polimorfik diperlukan, dan itu akan diinisialisasi dalam konstruktor:
class Foobar {
Widget widget;
public:
Foobar() : widget(blah blah blah) {}
// or
std::unique_ptr<Widget> widget;
public:
Foobar() : widget(std::make_unique<Widget>(blah blah blah)) {}
(…)
};
Dan kita akan siap dan selesai. Sayangnya, hari ini, anak-anak Jawa akan menertawakan kita begitu mereka melihatnya, dan memang seharusnya, karena pasangan Foobar
dan Widget
bersama - sama. Solusinya tampaknya sederhana: gunakan Injeksi Ketergantungan untuk mengambil konstruksi dependensi di luar Foobar
kelas. Tapi kemudian, C ++ memaksa kita untuk berpikir tentang kepemilikan dependensi. Tiga solusi muncul dalam pikiran:
Pointer unik
class Foobar {
std::unique_ptr<Widget> widget;
public:
Foobar(std::unique_ptr<Widget> &&w) : widget(w) {}
(…)
}
Foobar
mengklaim kepemilikan satu-satunya Widget
yang diberikan padanya. Ini memiliki keuntungan sebagai berikut:
- Dampak pada kinerja dapat diabaikan.
- Ini aman, karena
Foobar
mengontrol masa pakaiWidget
, jadi itu memastikan bahwaWidget
tidak tiba-tiba menghilang. - Aman
Widget
tidak akan bocor dan akan dihancurkan dengan benar ketika tidak lagi dibutuhkan.
Namun, ini harus dibayar:
- Ini menempatkan pembatasan pada bagaimana
Widget
instance dapat digunakan, misalnya tidak ada stack-dialokasikanWidgets
dapat digunakan, tidakWidget
dapat dibagikan.
Pointer yang dibagikan
class Foobar {
std::shared_ptr<Widget> widget;
public:
Foobar(const std::shared_ptr<Widget> &w) : widget(w) {}
(…)
}
Ini mungkin setara atau paling dekat dengan Jawa dan bahasa pengumpulan sampah lainnya. Keuntungan:
- Lebih universal, karena memungkinkan berbagi dependensi.
- Menjaga keamanan (poin 2 dan 3) dari
unique_ptr
solusi.
Kekurangan:
- Menghabiskan sumber daya saat tidak ada pembagian yang terlibat.
- Masih membutuhkan alokasi tumpukan dan melarang objek yang dialokasikan tumpukan.
Pointer pengamatan biasa
class Foobar {
Widget *widget;
public:
Foobar(Widget *w) : widget(w) {}
(…)
}
Tempatkan pointer mentah di dalam kelas dan mengalihkan beban kepemilikan kepada orang lain. Pro:
- Sesederhana itu bisa.
- Universal, menerima sembarang saja
Widget
.
Cons:
- Tidak aman lagi.
- Memperkenalkan entitas lain yang bertanggung jawab atas kepemilikan keduanya
Foobar
danWidget
.
Beberapa metaprogram template gila
Satu-satunya keuntungan yang dapat saya pikirkan adalah bahwa saya dapat membaca semua buku yang saya tidak punya waktu untuk sementara perangkat lunak saya builing;)
Saya condong ke solusi ketiga, karena paling universal, Foobars
bagaimanapun juga, ada sesuatu yang harus dikelola , jadi mengelola Widgets
adalah perubahan sederhana. Namun, menggunakan raw pointer mengganggu saya, di sisi lain solusi smart pointer merasa salah bagi saya, karena mereka membuat konsumen ketergantungan membatasi bagaimana ketergantungan itu dibuat.
Apakah saya melewatkan sesuatu? Atau apakah Injeksi Ketergantungan pada C ++ tidak sepele? Haruskah kelas memiliki dependensi atau hanya mengamati mereka?
Foobar
satu-satunya pemilik? Dalam kasus lama itu sederhana. Tetapi masalah dengan DI, seperti yang saya lihat, adalah karena ia memisahkan kelas dari konstruksi dependensinya, ia juga memisahkannya dari kepemilikan dependensi tersebut (karena kepemilikan terkait dengan konstruksi). Di lingkungan sampah yang dikumpulkan seperti Jawa, itu tidak masalah. Di C ++, ini.
std::unique_ptr
adalah cara untuk pergi. Anda dapat menggunakanstd::move()
untuk mentransfer kepemilikan sumber daya dari ruang lingkup atas ke kelas.