Katakanlah gim saya memiliki monster yang dapat kamikaze meledak pada pemain. Mari kita pilih nama untuk monster ini secara acak: Creeper. Jadi, Creeper
kelas memiliki metode yang terlihat seperti ini:
void Creeper::kamikaze() {
EventSystem::postEvent(ENTITY_DEATH, this);
Explosion* e = new Explosion;
e->setLocation(this->location());
this->world->addEntity(e);
}
Acara tidak antri, mereka dikirim segera. Ini menyebabkan Creeper
objek terhapus di suatu tempat di dalam panggilan postEvent
. Sesuatu seperti ini:
void World::handleEvent(int type, void* context) {
if(type == ENTITY_DEATH){
Entity* ent = dynamic_cast<Entity*>(context);
removeEntity(ent);
delete ent;
}
}
Karena Creeper
objek terhapus saat kamikaze
metode ini masih berjalan, itu akan macet ketika mencoba mengakses this->location()
.
Salah satu solusinya adalah dengan mengantri acara ke buffer dan mengirimkannya nanti. Apakah itu solusi umum dalam game C ++? Rasanya seperti sedikit peretasan, tapi itu mungkin hanya karena pengalaman saya dengan bahasa lain dengan praktik manajemen memori yang berbeda.
Dalam C ++, apakah ada solusi umum yang lebih baik untuk masalah ini di mana sebuah objek secara tidak sengaja menghapus dirinya sendiri dari dalam salah satu metodenya?
sumber
autorelease
di Objective-C, di mana penghapusan ditunda hingga "hanya sedikit".Jawaban:
Jangan hapus
this
Bahkan secara implisit.
- Pernah -
Menghapus objek saat salah satu fungsi anggotanya masih ada di tumpukan meminta masalah. Setiap arsitektur kode yang mengakibatkan hal itu terjadi ("tidak sengaja" atau tidak) secara objektif buruk , berbahaya , dan harus segera di-refactored . Dalam hal ini, jika monster Anda akan diizinkan untuk memanggil 'World :: handleEvent', jangan, dalam keadaan apa pun, hapus monster di dalam fungsi itu!
(Dalam situasi khusus ini, pendekatan saya yang biasa adalah membuat monster menetapkan bendera 'mati' pada dirinya sendiri, dan memiliki objek Dunia - atau sesuatu seperti itu - menguji bendera 'mati' itu sekali per frame, menghilangkan benda-benda itu dari daftar objek di dunia, dan menghapusnya atau mengembalikannya ke kolam monster atau apa pun yang sesuai. Pada saat ini, dunia juga mengirimkan pemberitahuan tentang penghapusan, sehingga objek lain di dunia tahu bahwa monster itu memiliki berhenti ada, dan dapat menjatuhkan petunjuk apa pun ke sana yang mungkin mereka pegang. Dunia melakukan ini pada waktu yang aman, ketika tahu bahwa tidak ada objek yang sedang diproses, jadi Anda tidak perlu khawatir tentang tumpukan yang meleset ke suatu titik di mana penunjuk 'ini' menunjuk pada memori yang dibebaskan.)
sumber
Alih-alih mengantri acara di buffer, antri penghapusan di buffer. Delayed-deletion berpotensi menyederhanakan logika secara besar-besaran; Anda benar-benar dapat membebaskan memori di akhir atau awal bingkai ketika Anda tahu tidak ada hal menarik yang terjadi pada objek Anda, dan hapus dari mana saja.
sumber
NSAutoreleasePool
dari Objective-C dalam situasi ini. Mungkin harus membuatDeletionPool
dengan template C ++ atau sesuatu.Alih-alih membiarkan dunia menangani penghapusan, Anda bisa membiarkan instance kelas lain berfungsi sebagai ember untuk menyimpan semua entitas yang dihapus. Contoh khusus ini harus mendengarkan
ENTITY_DEATH
acara dan menanganinya sehingga membuatnya mengantri. TheWorld
kemudian iterate atas kasus ini dan dapat melakukan operasi pasca-kematian setelah frame telah diberikan dan 'jelas' ember ini, yang pada gilirannya akan melakukan penghapusan sebenarnya dari entitas kasus.Contoh kelas seperti itu akan seperti ini: http://ideone.com/7Upza
sumber
World
kelas.Saya sarankan menerapkan pabrik yang digunakan untuk semua alokasi objek game dalam game. Jadi, alih-alih menyebut diri Anda sendiri yang baru, Anda akan memberi tahu pabrik untuk membuat sesuatu untuk Anda.
Sebagai contoh
Setiap kali Anda ingin menghapus objek, pabrik mendorong objek dalam buffer yang dihapus frame berikutnya. Kehancuran yang tertunda sangat penting dalam sebagian besar skenario.
Juga pertimbangkan mengirim semua pesan dengan penundaan satu bingkai. Hanya ada beberapa pengecualian di mana Anda perlu mengirim segera, bahwa sebagian besar kasus bagaimanapun
sumber
Anda dapat mengimplementasikan memori terkelola di C ++ sendiri, sehingga ketika
ENTITY_DEATH
dipanggil, semua yang terjadi adalah jumlah referensi dikurangi satu.Kemudian seperti yang disarankan John pada permintaan setiap frame Anda dapat memeriksa entitas mana yang tidak berguna (yang tidak memiliki referensi) dan menghapusnya. Misalnya Anda dapat menggunakan
boost::shared_ptr<T>
( didokumentasikan di sini ) atau jika Anda menggunakan C ++ 11 (VC2010)std::tr1::shared_ptr<T>
sumber
std::shared_ptr<T>
, bukan laporan teknis! - Anda harus menentukan deleter khusus, jika tidak, itu juga akan menghapus objek segera ketika jumlah referensi mencapai nol.Gunakan pooling dan tidak benar-benar menghapus objek. Sebagai gantinya, ubah struktur data tempat mereka terdaftar. Misalnya untuk rendering objek ada objek adegan dan semua entitas entah bagaimana terdaftar untuk rendering, deteksi tabrakan dll. Alih-alih menghapus objek, lepaskan dari adegan dan masukkan ke dalam kumpulan objek mati. Metode ini tidak hanya akan mencegah masalah memori (seperti objek menghapus sendiri) tetapi juga dapat mempercepat permainan Anda jika Anda menggunakan kolam dengan benar.
sumber
Apa yang kami lakukan dalam permainan adalah menggunakan penempatan baru
eventPool hanyalah array besar memori yang diukir, dan pointer ke setiap segmen disimpan. Jadi, alokasi () akan mengembalikan alamat blok memori bebas. Dalam eventPool kami, memori diperlakukan sebagai tumpukan, jadi setelah semua acara telah dikirim, kami baru saja mengatur ulang penunjuk tumpukan kembali ke awal array.
Karena cara sistem acara kami bekerja, kami tidak perlu memanggil destruktor pada evetn. Jadi kolam hanya akan mendaftarkan blok memori sebagai bebas, dan akan mengalokasikannya.
Ini memberi kami kecepatan tinggi.
Juga ... Kami benar-benar menggunakan kumpulan memori untuk semua yang dialokasikan secara dinamis keberatan dalam pengembangan, karena itu adalah cara yang bagus untuk menemukan kebocoran memori, jika ada benda yang tersisa di kolam ketika permainan keluar (biasanya) maka kemungkinan ada kebocoran mem.
sumber