Misalkan saya memiliki kode berikut:
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
Apakah ini aman? Atau harus ptr
dilemparkan ke char*
sebelum dihapus?
c++
memory-management
casting
void-pointers
An̲̳̳drew
sumber
sumber
Jawaban:
Itu tergantung pada "aman." Ini biasanya akan berfungsi karena informasi disimpan bersama dengan penunjuk tentang alokasi itu sendiri, sehingga deallocator dapat mengembalikannya ke tempat yang tepat. Dalam pengertian ini "aman" selama pengalokasi Anda menggunakan tag batas internal. (Banyak yang melakukannya.)
Namun, seperti yang disebutkan dalam jawaban lain, menghapus void pointer tidak akan memanggil destruktor, yang bisa menjadi masalah. Dalam pengertian itu, ini tidak "aman."
Tidak ada alasan yang baik untuk melakukan apa yang Anda lakukan dengan cara Anda melakukannya. Jika Anda ingin menulis fungsi deallokasi Anda sendiri, Anda dapat menggunakan template fungsi untuk menghasilkan fungsi dengan jenis yang benar. Alasan yang baik untuk melakukannya adalah untuk menghasilkan pengalokasi kumpulan, yang bisa sangat efisien untuk tipe tertentu.
Seperti yang disebutkan dalam jawaban lain, ini adalah perilaku tidak terdefinisi di C ++. Secara umum adalah baik untuk menghindari perilaku yang tidak terdefinisi, meskipun topiknya sendiri rumit dan penuh dengan pendapat yang saling bertentangan.
sumber
sizeof(T*) == sizeof(U*)
untuk semuaT,U
menyarankan bahwa itu harus mungkin untuk memiliki 1 non template,void *
implementasi pengumpul sampah berbasis. Tapi kemudian, ketika gc benar-benar harus menghapus / membebaskan pointer, pertanyaan ini muncul. Untuk membuatnya bekerja, Anda memerlukan lambda function destructor wrappers (urgh) atau Anda akan membutuhkan semacam "tipe sebagai data" yang dinamis yang memungkinkan bolak-balik antara suatu tipe dan sesuatu yang dapat disimpan.Menghapus melalui penunjuk kosong tidak ditentukan oleh Standar C ++ - lihat bagian 5.3.5 / 3:
Dan catatan kakinya:
.
sumber
NULL
membuat perbedaan untuk manajemen memori aplikasi?Ini bukan ide yang bagus dan bukan sesuatu yang akan Anda lakukan di C ++. Anda kehilangan info tipe Anda tanpa alasan.
Destruktor Anda tidak akan dipanggil pada objek dalam larik yang Anda hapus saat Anda memanggilnya untuk tipe non primitif.
Anda sebaiknya mengganti baru / hapus.
Menghapus void * mungkin akan membebaskan memori Anda dengan benar secara kebetulan, tetapi itu salah karena hasilnya tidak ditentukan.
Jika karena alasan tertentu tidak saya ketahui Anda perlu menyimpan pointer Anda dalam void * lalu membebaskannya, Anda harus menggunakan malloc dan gratis.
sumber
Menghapus penunjuk kosong berbahaya karena penghancur tidak akan dipanggil pada nilai yang sebenarnya ditunjukkannya. Hal ini dapat mengakibatkan kebocoran memori / sumber daya dalam aplikasi Anda.
sumber
Pertanyaan itu tidak masuk akal. Kebingungan Anda mungkin sebagian karena bahasa ceroboh yang sering digunakan orang dengan
delete
:Anda gunakan
delete
untuk menghancurkan objek yang dialokasikan secara dinamis. Lakukan itu, Anda membentuk ekspresi hapus dengan penunjuk ke objek itu . Anda tidak pernah "menghapus penunjuk". Apa yang sebenarnya Anda lakukan adalah "menghapus sebuah objek yang dikenali dari alamatnya".Sekarang kita melihat mengapa pertanyaan itu tidak masuk akal: Void pointer bukanlah "alamat sebuah objek". Itu hanya sebuah alamat, tanpa semantik apapun. Ini mungkin berasal dari alamat objek sebenarnya, tetapi informasi itu hilang, karena dikodekan dalam tipe penunjuk asli. Satu-satunya cara untuk memulihkan penunjuk objek adalah dengan mengembalikan penunjuk kosong ke penunjuk objek (yang mengharuskan penulis untuk mengetahui apa arti penunjuk).
void
itu sendiri adalah tipe yang tidak lengkap dan karenanya tidak pernah menjadi tipe objek, dan penunjuk kosong tidak pernah dapat digunakan untuk mengidentifikasi objek. (Objek diidentifikasi secara bersama-sama berdasarkan jenis dan alamatnya.)sumber
delete
mungkin berupa nilai penunjuk nol, penunjuk ke objek non-larik yang dibuat oleh ekspresi baru sebelumnya , atau penunjuk ke subobjek yang mewakili kelas dasar dari objek semacam itu. Jika tidak, perilakunya tidak ditentukan. " Jadi jika kompiler menerima kode Anda tanpa diagnostik, itu hanyalah bug di kompiler ...delete void_pointer
. Ini perilaku yang tidak terdefinisi. Pemrogram tidak boleh meminta perilaku tidak terdefinisi, bahkan jika responsnya tampak melakukan apa yang diinginkan oleh pemrogram.Jika Anda benar-benar harus melakukan ini, mengapa tidak memotong tengah-tengah manusia (yang
new
dandelete
operator) dan panggilan globaloperator new
danoperator delete
langsung? (Tentu saja, jika Anda mencoba melengkapi operatornew
dandelete
, Anda sebenarnya harus menerapkan ulangoperator new
danoperator delete
.)Perhatikan bahwa tidak seperti
malloc()
,operator new
melemparstd::bad_alloc
pada kegagalan (atau memanggilnew_handler
jika ada yang terdaftar).sumber
Karena char tidak memiliki logika destruktor khusus. INI tidak akan berhasil.
D'ctor tidak akan dipanggil.
sumber
Jika Anda ingin menggunakan void *, mengapa Anda tidak menggunakan malloc / gratis saja? baru / hapus lebih dari sekedar pengelolaan memori. Pada dasarnya, new / delete memanggil konstruktor / destruktor dan ada lebih banyak hal yang terjadi. Jika Anda hanya menggunakan tipe bawaan (seperti char *) dan menghapusnya melalui void *, ini akan berhasil tetapi tetap tidak disarankan. Intinya adalah gunakan malloc / gratis jika Anda ingin menggunakan void *. Jika tidak, Anda dapat menggunakan fungsi template untuk kenyamanan Anda.
sumber
Banyak orang telah berkomentar mengatakan tidak, tidak aman untuk menghapus penunjuk kosong. Saya setuju dengan itu, tetapi saya juga ingin menambahkan bahwa jika Anda bekerja dengan void pointer untuk mengalokasikan array yang berdekatan atau yang serupa, Anda dapat melakukan ini
new
sehingga Anda dapat menggunakandelete
dengan aman (dengan, ahem , sedikit kerja ekstra). Ini dilakukan dengan mengalokasikan penunjuk kosong ke wilayah memori (disebut 'arena') dan kemudian memasok penunjuk ke arena ke yang baru. Lihat bagian ini di FAQ C ++ . Ini adalah pendekatan umum untuk mengimplementasikan kumpulan memori di C ++.sumber
Hampir tidak ada alasan untuk melakukan ini.
Pertama-tama, jika Anda tidak tahu jenis datanya, dan yang Anda tahu hanyalah itu
void*
, maka Anda seharusnya memperlakukan data itu sebagai gumpalan data biner (unsigned char*
) tanpa tipe , dan gunakanmalloc
/free
untuk menghadapinya . Ini kadang-kadang diperlukan untuk hal-hal seperti data bentuk gelombang dan sejenisnya, di mana Anda perlu meneruskanvoid*
petunjuk ke C apis. Tidak apa-apa.Jika Anda melakukan mengetahui jenis data (yaitu memiliki ctor / dtor), tapi untuk beberapa alasan Anda berakhir dengan
void*
pointer (karena alasan apapun Anda memiliki) maka Anda benar-benar harus melemparkan kembali ke jenis yang Anda tahu itu untuk jadilah , dan panggildelete
itu.sumber
Saya telah menggunakan void *, (alias tipe yang tidak diketahui) dalam kerangka saya untuk sementara dalam refleksi kode dan prestasi ambiguitas lainnya, dan sejauh ini, saya tidak memiliki masalah (kebocoran memori, pelanggaran akses, dll.) Dari kompiler mana pun. Hanya peringatan karena operasinya tidak standar.
Sangat masuk akal untuk menghapus yang tidak diketahui (void *). Pastikan saja penunjuk mengikuti pedoman ini, atau mungkin berhenti masuk akal:
1) Penunjuk yang tidak diketahui tidak boleh menunjuk ke tipe yang memiliki dekonstruktor sepele, dan ketika dicor sebagai penunjuk yang tidak diketahui, itu TIDAK PERNAH DIHAPUS. Hapus hanya penunjuk yang tidak dikenal SETELAH mentransmisikannya kembali ke tipe ASLI.
2) Apakah instance direferensikan sebagai pointer yang tidak dikenal dalam memori terikat tumpukan atau tumpukan? Jika pointer yang tidak diketahui mereferensikan sebuah instance pada stack, maka itu TIDAK PERNAH DIHAPUS!
3) Apakah Anda 100% positif pointer yang tidak diketahui adalah wilayah memori yang valid? Tidak, maka itu TIDAK PERNAH DITUNDA!
Secara keseluruhan, sangat sedikit pekerjaan langsung yang dapat dilakukan dengan menggunakan tipe penunjuk yang tidak diketahui (void *). Namun, secara tidak langsung, void * adalah aset yang bagus untuk diandalkan oleh developer C ++ saat ambiguitas data diperlukan.
sumber
Jika Anda hanya ingin buffer, gunakan malloc / free. Jika Anda harus menggunakan new / delete, pertimbangkan kelas pembungkus sepele:
sumber
Untuk kasus khusus char.
char adalah tipe intrinsik yang tidak memiliki destruktor khusus. Jadi argumen kebocoran itu bisa diperdebatkan.
sizeof (char) biasanya satu jadi tidak ada argumen penyelarasan juga. Dalam kasus platform langka di mana sizeof (char) bukan satu, mereka mengalokasikan memori yang cukup untuk karakter mereka. Jadi argumen penyelarasan juga bisa diperdebatkan.
malloc / free akan lebih cepat dalam kasus ini. Tetapi Anda kehilangan std :: bad_alloc dan harus memeriksa hasil malloc. Memanggil operator baru dan hapus global mungkin lebih baik karena melewati perantara.
sumber
new
sebenarnya adalah melempar. Ini tidak benar. Ini tergantung pada kompiler dan kompilator. Lihat misalnya/GX[-] enable C++ EH (same as /EHsc)
sakelar MSVC2019 . Juga pada sistem tertanam banyak yang memilih untuk tidak membayar pajak kinerja untuk pengecualian C ++. Jadi kalimat yang diawali dengan "But you forfeit std :: bad_alloc ..." patut dipertanyakan.