Apakah aman untuk menghapus pointer NULL?

299

Apakah aman untuk menghapus pointer NULL?

Dan apakah itu gaya pengkodean yang baik?

qiuxiafei
sumber
21
Praktik yang baik adalah menulis program C ++ tanpa satu panggilan pun delete. Gunakan RAII sebagai gantinya. Yaitu, gunakan std::vector<T> v(100);alih-alih T* p = new T[100];, gunakan pointer pintar seperti unique_ptr<T>dan shared_ptr<T>yang mengurus penghapusan bukan pointer mentah dll.
fredoverflow
8
berkat make_shared(c ++ 11) dan make_unique(c ++ 14) Program Anda harus berisi nol dari newdandelete
sp2danny
2
Mungkin masih ada beberapa kasus langka yang memerlukan baru / hapus, misalnya atom <T *>: atom <unique_ptr <T>> tidak diizinkan dan atom <shared_ptr <T>> memiliki overhead yang mungkin tidak dapat diterima dalam beberapa kasus.
atb
2
Untuk mendeklarasikan kelas dengan manajemen sumber daya menggunakan RAII, Anda perlu memanggil yang baru dan menghapus bukan ?, atau Anda mengatakan ada beberapa kelas templat untuk menyembunyikan ini bahkan ini.
VinGarcia
2
@VinGarcia Intinya adalah bahwa sebagian besar pengguna / klien (yaitu: non-perpustakaan) kode tidak boleh menulis newatau delete. Kelas yang dirancang untuk mengelola sumber daya, di mana komponen Standar tidak dapat melakukan pekerjaan, tentu saja dapat melakukan apa yang harus mereka lakukan, tetapi intinya adalah mereka melakukan hal-hal buruk dengan memori yang mereka kelola, bukan kode pengguna akhir. Jadi, buat perpustakaan / kelas pembantu Anda sendiri untuk melakukan new/ delete, dan gunakan kelas itu sebagai ganti mereka.
underscore_d

Jawaban:

265

deletetetap melakukan pemeriksaan, jadi memeriksa di sisi Anda menambahkan overhead dan terlihat lebih buruk. Sebuah sangat praktek yang baik adalah pengaturan pointer ke NULL setelah delete(membantu menghindari ganda penghapusan dan masalah korupsi memori lain yang serupa).

Saya juga suka jika deletesecara default menyetel parameter ke NULL seperti pada

#define my_delete(x) {delete x; x = NULL;}

(Saya tahu tentang nilai R dan L, tapi bukankah itu menyenangkan?)

ruslik
sumber
72
Perhatikan bahwa masih ada beberapa petunjuk lain yang menunjuk ke objek yang sama bahkan jika Anda menetapkan satu ke NULL pada penghapusan.
sth
13
Dalam kebanyakan kasus dalam kode saya, pointer keluar dari ruang lingkup setelah dihapus. Jauh lebih aman daripada hanya mengaturnya ke NULL.
Jalf
142
Sebuah sangat praktek dewa yang tidak menetapkan pointer ke NULL setelah penghapusan. Mengatur pointer ke NULL setelah menghapusnya menyamar kesalahan alokasi memori, yang merupakan hal yang sangat buruk. Program yang benar tidak menghapus pointer dua kali, dan program yang menghapus pointer dua kali seharusnya macet.
Damon
15
@Alice: Tidak relevan dengan apa yang dikatakan standar dalam hal itu. Standar yang ditetapkan menghapus pointer nol yang valid untuk beberapa alasan absurd 30 tahun yang lalu, jadi itu legal (kemungkinan besar warisan C). Tetapi menghapus pointer yang sama dua kali (bahkan setelah mengubah pola bitnya) masih merupakan kesalahan program yang serius. Bukan oleh kata-kata standar, tetapi oleh logika dan kepemilikan program. Seperti menghapus pointer nol, karena pointer nol sesuai dengan tidak ada objek , jadi tidak ada yang bisa dihapus. Suatu program harus benar - benar tahu apakah suatu benda valid dan siapa yang memilikinya, dan kapan benda itu bisa dihapus.
Damon
27
@Damon Namun, meskipun pencabutan aturan kepemilikan kejam ini, struktur bebas kunci terbukti lebih kuat daripada yang berbasis kunci. Dan ya, rekan kerja saya benar-benar mencintai saya karena profil eksekusi yang ditingkatkan yang diberikan struktur ini dan keselamatan benang ketat yang mereka pertahankan, yang memungkinkan alasan yang lebih mudah tentang kode (bagus untuk pemeliharaan). Namun, tidak satu pun dari hal ini maupun serangan pribadi Anda yang tersirat tidak ada hubungannya dengan definisi apa pun tentang kebenaran, validitas, atau kepemilikan. Apa yang Anda usulkan adalah aturan praktis yang baik, tetapi itu bukan hukum universal juga tidak diabadikan dalam standar.
Alice
77

Dari draft Standar C ++ 0x.

$ 5.3.5 / 2 - "[...] Di kedua alternatif tersebut, nilai operan delete mungkin merupakan nilai null pointer. [... '"

Tentu saja, tidak ada yang akan melakukan 'hapus' pointer dengan nilai NULL, tetapi aman untuk dilakukan. Idealnya seseorang tidak boleh memiliki kode yang menghapus pointer NULL. Tetapi kadang-kadang berguna ketika penghapusan pointer (misalnya dalam wadah) terjadi dalam satu lingkaran. Karena menghapus nilai pointer NULL aman, orang dapat benar-benar menulis logika penghapusan tanpa memeriksa eksplisit untuk menghapus operan NULL.

Sebagai tambahan, C Standar $ 7.20.3.2 juga mengatakan bahwa 'gratis' pada pointer NULL tidak melakukan tindakan.

Fungsi bebas menyebabkan ruang yang ditunjuk oleh ptr untuk dialokasikan, yaitu, tersedia untuk alokasi lebih lanjut. Jika ptr adalah pointer nol, tidak ada tindakan yang terjadi.

Chubsdad
sumber
2
Saya benar-benar ingin jawaban ini untuk kutipannya jika tidak sengaja memperkenalkan inefisiensi dalam kode yang tidak dioptimalkan. Seperti yang dinyatakan oleh jawaban yang diterima, penghapusan pointer nol adalah no-op. Oleh karena itu, memeriksa apakah sebuah pointer nol sebelum menghapusnya benar-benar asing.
codetaku
47

Ya itu aman.

Tidak ada salahnya menghapus null pointer; sering mengurangi jumlah tes di ujung fungsi jika pointer yang tidak terisi diinisialisasi ke nol dan kemudian dihapus.


Karena kalimat sebelumnya telah menyebabkan kebingungan, contoh - yang bukan pengecualian aman - dari apa yang dijelaskan:

void somefunc(void)
{
    SomeType *pst = 0;
    AnotherType *pat = 0;

    
    pst = new SomeType;
    
    if (…)
    {
        pat = new AnotherType[10];
        
    }
    if (…)
    {
        code using pat sometimes
    }

    delete[] pat;
    delete pst;
}

Ada semua jenis telur kutu yang dapat diambil dengan kode sampel, tetapi konsepnya (saya harap) jelas. Variabel pointer diinisialisasi ke nol sehingga deleteoperasi di akhir fungsi tidak perlu menguji apakah mereka non-null dalam kode sumber; kode perpustakaan tetap melakukan pemeriksaan itu.

Jonathan Leffler
sumber
Saya harus membaca itu beberapa kali untuk membuatnya masuk akal. Anda harus mengartikannya menjadi nol di bagian atas metode ini, atau selama itu, bukan di bagian ekor, tentunya? Kalau tidak, Anda hanya akan menghapus zeroing dan delete.
Marquis of Lorne
1
@EJP: Sebuah garis tidak sepenuhnya masuk akal dari suatu fungsi mungkin: void func(void) { X *x1 = 0; Y *y1 = 0; … x1 = new[10] X; … y1 = new[10] Y; … delete[] y1; delete[] x1; }. Saya tidak menunjukkan struktur blok atau lompatan, tetapi delete[]operasi pada akhirnya aman karena inisialisasi di awal. Jika sesuatu melompat ke akhir setelah x1dialokasikan dan sebelum y1dialokasikan dan tidak ada inisialisasi y1, maka akan ada perilaku yang tidak terdefinisi - dan sementara kode dapat menguji nullness (dari x1dan y1) sebelum penghapusan, tidak perlu dilakukan begitu.
Jonathan Leffler
22

Menghapus pointer nol tidak berpengaruh. Ini bukan gaya pengkodean yang baik karena itu tidak diperlukan, tetapi juga tidak buruk.

Jika Anda mencari praktik pengkodean yang baik pertimbangkan untuk menggunakan smart pointer, maka Anda tidak perlu melakukannya deletesama sekali.

Brian R. Bondy
sumber
20
saat orang ingin menghapus pointer NULL adalah ketika mereka tidak yakin apakah itu berisi NULL ... jika mereka tahu itu NULL maka mereka tidak akan mempertimbangkan untuk menghapus dan karenanya bertanya ;-).
Tony Delroy
@Tony: Maksud saya hanya itu tidak akan berpengaruh, dan keberadaan kode seperti itu yang menghapus pointer yang terkadang berisi NULL tidak selalu buruk.
Brian R. Bondy
3
Pemeriksaan redundan IMO tentu saja buruk, untuk kinerja, keterbacaan, dan pemeliharaan.
paulm
@paulm OP tentu tidak berbicara tentang hal buruk seperti itu, lebih dari Seg Fault / UB jenis buruk.
Musim dingin
8

Untuk melengkapi jawaban ruslik , dalam C ++ 14 Anda dapat menggunakan konstruksi ini:

delete std::exchange(heapObject, nullptr);
ashrasmun
sumber
3

Aman kecuali Anda membebani operator hapus. jika Anda membebani operator hapus dan tidak menangani kondisi nol maka itu tidak aman sama sekali.


sumber
Bisakah Anda menambahkan penjelasan untuk jawaban Anda?
Marcin Nabiałek
-2

Saya pernah mengalami bahwa tidak aman (VS2010) untuk menghapus [] NULL (yaitu sintaksis array). Saya tidak yakin apakah ini sesuai dengan standar C ++.

Ini adalah aman untuk NULL delete (skalar sintaks).

pheest
sumber
6
Ini ilegal, dan saya tidak percaya.
Konrad Rudolph
5
Anda harus dapat menghapus pointer nol. Jadi jika itu melanggar untuk Anda, maka Anda mungkin memiliki bug dalam kode yang menunjukkannya.
Mysticial
3
§5.3.2 Dalam alternatif kedua (hapus array), nilai operan penghapusan dapat berupa nilai penunjuk nol atau nilai penunjuk yang dihasilkan dari ekspresi baru array sebelumnya.
sp2danny
1
@ Opux Perilaku VS2010 yang diduga dalam jawabannya. Seperti yang dijelaskan dalam komentar lain, aman untuk itu delete[] NULL.
Konrad Rudolph
1
@ Opux Itu sebabnya saya menulis "Saya tidak percaya itu" daripada "itu salah". Tapi saya masih belum, dan itu akan menjadi pelanggaran standar yang sangat keterlaluan dan bodoh. VC ++ secara umum cukup baik dalam mengikuti batasan standar, dan tempat-tempat yang dilanggar itu masuk akal secara historis.
Konrad Rudolph