Saya sedang mengerjakan mesin gim saya sendiri, dan saya sedang merancang manajer saya. Saya telah membaca bahwa untuk manajemen memori, menggunakan Init()
dan CleanUp()
fungsinya lebih baik daripada menggunakan konstruktor dan destruktor.
Saya telah mencari contoh kode C ++, untuk melihat bagaimana fungsi-fungsi itu bekerja dan bagaimana saya bisa mengimplementasikannya ke dalam mesin saya. Bagaimana Init()
dan CleanUp()
bekerja, dan bagaimana saya bisa menerapkannya ke dalam mesin saya?
Jawaban:
Sebenarnya cukup sederhana:
Alih-alih memiliki Konstruktor yang melakukan pengaturan Anda,
... minta konstruktor Anda melakukan sedikit atau tidak sama sekali, dan tulis metode yang disebut
.init
atau.initialize
, yang akan melakukan apa yang biasanya dilakukan konstruktor Anda.Jadi sekarang, bukannya hanya seperti:
Anda bisa pergi:
Keuntungannya adalah sekarang Anda dapat menggunakan dependensi-injeksi / inversi-kontrol lebih mudah di sistem Anda.
Alih-alih mengatakan
Anda dapat membangun tentara, memberinya metode melengkapi, di mana Anda menyerahkan dia senjata, dan KEMUDIAN memanggil semua sisa fungsi konstruktor.
Jadi sekarang, alih-alih musuh subkelas di mana satu tentara memiliki pistol dan yang lain memiliki senapan dan yang lain memiliki senapan, dan itulah satu-satunya perbedaan, Anda bisa mengatakan:
Kesepakatan yang sama dengan kehancuran. Jika Anda memiliki kebutuhan khusus (menghapus pendengar acara, menghapus instance dari array / struktur apa pun yang bekerja dengan Anda, dll.), Anda kemudian akan memanggil mereka secara manual, sehingga Anda tahu persis kapan dan di mana dalam program yang sedang terjadi.
EDIT
Seperti yang ditunjukkan oleh Kryotan, di bawah ini, ini menjawab "Bagaimana" posting asli , tetapi tidak benar-benar melakukan pekerjaan yang baik dari "Mengapa".
Seperti yang mungkin Anda lihat dalam jawaban di atas, mungkin tidak ada banyak perbedaan antara:
dan menulis
sementara hanya memiliki fungsi konstruktor yang lebih besar.
Ada argumen yang harus dibuat untuk objek yang memiliki 15 atau 20 pra-kondisi, yang akan membuat konstruktor sangat, sangat sulit untuk dikerjakan, dan itu akan membuat segalanya lebih mudah untuk dilihat dan diingat, dengan menarik hal-hal itu ke antarmuka. , sehingga Anda dapat melihat cara instantiation bekerja, satu tingkat lebih tinggi.
Konfigurasi opsional objek adalah ekstensi alami untuk ini; secara opsional mengatur nilai pada antarmuka, sebelum membuat objek berjalan.
JS memiliki beberapa cara pintas yang bagus untuk ide ini, yang sepertinya tidak sesuai dengan bahasa c-type yang lebih kuat.
Yang mengatakan, kemungkinannya adalah, jika Anda berurusan dengan daftar argumen yang lama di konstruktor Anda, bahwa objek Anda terlalu besar dan melakukan terlalu banyak, sebagaimana adanya. Sekali lagi, ini adalah hal preferensi pribadi, dan ada pengecualian jauh dan luas, tetapi jika Anda memasukkan 20 hal ke dalam objek, kemungkinan besar Anda bisa menemukan cara untuk membuat objek itu melakukan lebih sedikit, dengan membuat objek yang lebih kecil .
Alasan yang lebih relevan, dan yang dapat diterapkan secara luas adalah bahwa inisialisasi objek bergantung pada data asinkron, yang saat ini tidak Anda miliki.
Anda tahu bahwa Anda memerlukan objek, jadi Anda tetap akan membuatnya, tetapi untuk membuatnya berfungsi dengan baik, ia membutuhkan data dari server, atau dari file lain yang sekarang perlu dimuat.
Sekali lagi, apakah Anda mengirimkan data yang diperlukan ke init raksasa, atau membangun antarmuka tidak terlalu penting untuk konsep, sebanyak itu penting untuk antarmuka objek Anda, dan desain sistem Anda ...
Tetapi dalam hal membangun objek, Anda mungkin melakukan sesuatu seperti ini:
async_loader
mungkin melewati nama file, atau nama sumber daya atau apa pun, memuat sumber daya itu - mungkin memuat file suara, atau data gambar, atau mungkin memuat statistik karakter yang disimpan ...... dan kemudian akan memasukkan data itu kembali ke
obj_w_async_dependencies.init(result);
.Jenis dinamis ini sering ditemukan di aplikasi web.
Tidak harus dalam konstruksi objek, untuk aplikasi tingkat yang lebih tinggi: misalnya, galeri mungkin memuat dan menginisialisasi segera, dan kemudian menampilkan foto saat mereka streaming - itu tidak benar-benar inisialisasi async, tetapi di mana itu terlihat lebih sering akan menjadi di perpustakaan JavaScript.
Satu modul mungkin tergantung pada yang lain, dan inisialisasi modul itu mungkin ditunda sampai pemuatan tanggungan selesai.
Dalam hal contoh khusus game ini, pertimbangkan
Game
kelas yang sebenarnya .Mengapa kita tidak bisa memanggil
.start
atau.run
di konstruktor?Sumber daya perlu dimuat - sisanya semuanya telah cukup banyak didefinisikan dan baik untuk dijalankan, tetapi jika kita mencoba menjalankan game tanpa koneksi database, atau tanpa tekstur atau model atau suara atau level, itu tidak akan menjadi permainan yang sangat menarik ...
... jadi apa, lalu perbedaan antara apa yang kita lihat dari tipikal
Game
, kecuali bahwa kita memberikan metode "maju" nama yang lebih menarik daripada.init
(atau sebaliknya, pisahkan inisialisasi lebih jauh terpisah, untuk memisahkan pemuatan, mengatur hal-hal yang telah dimuat, dan menjalankan program ketika semuanya telah diatur).sumber
.init
, mungkin tidak, tetapi kemungkinan. Ergo, kasus yang valid.Apa pun yang Anda baca yang mengatakan Init dan CleanUp lebih baik, seharusnya juga memberi tahu Anda alasannya. Artikel yang tidak membenarkan klaim mereka tidak layak dibaca.
Memiliki fungsi inisialisasi dan shutdown yang terpisah dapat membuatnya lebih mudah untuk mengatur dan menghancurkan sistem karena Anda dapat memilih urutan untuk memanggilnya, sedangkan konstruktor dipanggil secara tepat ketika objek dibuat dan destruktor disebut ketika objek dihancurkan. Ketika Anda memiliki dependensi kompleks antara 2 objek, Anda sering membutuhkan keduanya untuk eksis sebelum mereka mengatur sendiri - tetapi seringkali ini adalah tanda desain yang buruk di tempat lain.
Beberapa bahasa tidak memiliki destruktor yang dapat Anda andalkan, karena penghitungan referensi dan pengumpulan sampah membuat lebih sulit untuk mengetahui kapan objek akan dihancurkan. Dalam bahasa-bahasa ini Anda hampir selalu membutuhkan metode shutdown / pembersihan, dan beberapa suka menambahkan metode init untuk simetri.
sumber
Saya pikir alasan terbaik adalah: untuk memungkinkan pengumpulan.
jika Anda memiliki Init dan CleanUp, Anda dapat, ketika sebuah objek terbunuh, cukup panggil CleanUp, dan dorong objek tersebut ke tumpukan objek dengan tipe yang sama: a 'pool'.
Kemudian, kapan pun Anda membutuhkan objek baru, Anda dapat memunculkan satu objek dari pool ATAU jika pool kosong -terlalu buruk- Anda harus membuat yang baru. Kemudian Anda memanggil Init pada objek ini.
Strategi yang baik adalah mengisi terlebih dahulu kumpulan sebelum permainan dimulai dengan jumlah objek yang 'bagus', sehingga Anda tidak perlu membuat objek yang dikumpulkan selama permainan.
Jika, di sisi lain, Anda menggunakan 'baru', dan hanya berhenti merujuk objek ketika tidak berguna bagi Anda, Anda membuat sampah yang harus diingat kembali pada suatu waktu. Ingatan ini terutama merupakan hal yang buruk untuk bahasa single-threaded seperti Javascript, di mana pengumpul sampah menghentikan semua kode ketika mengevaluasi perlu mengingat kembali memori objek yang tidak lagi digunakan. Gim ini hang selama beberapa milidetik, dan pengalaman bermainnya manja.
- Anda sudah mengerti -: jika Anda mengumpulkan semua objek Anda, tidak ada ingatan yang terjadi, maka tidak ada lagi perlambatan acak.
Ini juga jauh lebih cepat untuk memanggil init pada objek yang datang dari kolam daripada mengalokasikan memori + init objek baru.
Tetapi peningkatan kecepatan kurang penting, karena sering kali penciptaan objek bukan hambatan kinerja ... Dengan beberapa pengecualian, seperti game panik, mesin partikel, atau mesin fisik menggunakan vektor 2D / 3d intensif untuk perhitungan mereka. Di sini kecepatan dan pembuatan sampah sangat ditingkatkan dengan menggunakan kolam.
Rq: Anda mungkin tidak perlu memiliki metode CleanUp untuk objek yang dikumpulkan Anda jika Init () me-reset semuanya.
Sunting: membalas posting ini memotivasi saya untuk menyelesaikan sedikit artikel yang saya buat tentang penyatuan dalam Javascript .
Anda dapat menemukannya di sini jika Anda tertarik:
http://gamealchemist.wordpress.com/
sumber
Pertanyaan Anda dibalik ... Secara historis, pertanyaan yang lebih relevan adalah:
Mengapa adalah konstruksi + intialisation digabungkan , yaitu mengapa tidak kita lakukan langkah-langkah ini secara terpisah? Tentunya ini bertentangan dengan SoC ?
Untuk C ++, maksud RAII adalah bahwa akuisisi dan pelepasan sumber daya terkait langsung dengan objek seumur hidup, dengan harapan hal ini akan menjamin pelepasan sumber daya. Melakukannya? Sebagian. Ini dipenuhi 100% dalam konteks variabel berbasis-stack / otomatis, di mana meninggalkan ruang lingkup yang terkait secara otomatis memanggil destruktor / melepaskan variabel-variabel ini (maka dari itu kualifikasi
automatic
). Namun untuk variabel tumpukan, pola yang sangat berguna ini rusak, karena Anda masih dipaksa untuk memanggil secara eksplisitdelete
untuk menjalankan destruktor, dan jika Anda lupa melakukannya, Anda masih akan tergigit oleh apa yang coba dipecahkan oleh RAII; dalam konteks heap-dialokasikan variabel, maka, C ++ memberikan manfaat terbatas atas C (delete
vsfree()
) saat menggabungkan konstruksi dengan inisialisasi, yang berdampak negatif dalam hal berikut:Membangun sistem objek untuk permainan / simulasi dalam C sangat disarankan karena akan memberikan banyak cahaya pada keterbatasan RAII dan pola OO-sentris lainnya melalui pemahaman yang lebih dalam tentang asumsi yang dibuat oleh C ++ dan kemudian bahasa OO klasik. (ingat bahwa C ++ dimulai sebagai sistem OO yang dibangun di C).
sumber