Mengapa sintaks delete[] ada di C++?

Setiap kali seseorang mengajukan pertanyaan tentang delete[] di sini, selalu ada pertanyaan yang cukup umum, begitulah cara C++ melakukannya, gunakan jenis respons delete[]. Berasal dari latar belakang vanilla C, apa yang saya tidak mengerti adalah mengapa perlu ada pemanggilan yang berbeda sama sekali.

Dengan malloc()/free() pilihan Anda adalah mendapatkan penunjuk ke blok memori yang berdekatan dan mengosongkan blok memori yang berdekatan. Sesuatu di bidang implementasi datang dan mengetahui ukuran blok yang Anda alokasikan berdasarkan alamat dasar, ketika Anda harus mengosongkannya.

Tidak ada fungsi free_array(). Saya telah melihat beberapa teori gila tentang pertanyaan lain yang terkait dengan hal ini, seperti memanggil delete ptr hanya akan membebaskan bagian atas array, bukan seluruh array. Atau lebih tepatnya, tidak ditentukan oleh implementasinya. Dan tentu saja... jika ini adalah versi pertama C++ dan Anda membuat pilihan desain yang aneh, itu masuk akal. Tapi kenapa dengan standar C++ $PRESENT_YEAR belum kelebihan beban???

Tampaknya ini adalah satu-satunya bit tambahan yang ditambahkan C++ melalui array dan memanggil destruktor, dan saya pikir mungkin ini adalah inti dari itu, dan secara harfiah menggunakan fungsi terpisah untuk menyelamatkan kita dari pencarian panjang runtime tunggal, atau nullptr di akhir daftar dengan imbalan menyiksa setiap pemrogram C++ baru atau pemrogram yang mengalami hari yang tidak jelas dan lupa bahwa ada kata cadangan yang berbeda.

Bisakah seseorang mengklarifikasi sekali dan untuk selamanya jika ada alasan selain itu yang dikatakan standar dan tidak ada yang mempertanyakannya?


person awiebe    schedule 17.05.2021    source sumber
comment
Jika Anda ingin menguji alokasi dan kebebasan memori Anda untuk melihat apakah teori-teori gila itu benar atau tidak, Anda dapat menggunakan Valgrind untuk melihat apa yang sebenarnya terjadi. Saya menduga penghapusan yang berlebihan memiliki lebih banyak masalah daripada yang dijelaskan sejauh ini dalam jawaban, tetapi saya tidak memiliki keahlian.   -  person ttbek    schedule 18.05.2021
comment
Pertanyaan terkait: Bagaimana delete[] mengetahui bahwa itu adalah array?, dan khususnya mencatat jawaban ini.   -  person Fred Larson    schedule 18.05.2021


Jawaban (7)


Objek di C++ sering kali memiliki destruktor yang harus dijalankan di akhir masa pakainya. delete[] memastikan destruktor setiap elemen array dipanggil. Namun melakukan hal ini memiliki overhead yang tidak ditentukan, sementara delete tidak. Satu untuk array, yang membayar biaya overhead dan satu lagi untuk objek tunggal yang tidak membayar.

Agar hanya memiliki satu versi, implementasi memerlukan mekanisme untuk melacak informasi tambahan tentang setiap pointer. Namun salah satu prinsip dasar C++ adalah pengguna tidak boleh dipaksa membayar biaya yang sebenarnya tidak harus mereka bayarkan.

Selalu delete apa yang Anda new dan selalu delete[] apa yang Anda new[]. Namun di C++ modern, new dan new[] umumnya tidak digunakan lagi. Gunakan std::make_unique, std::make_shared, std::vector atau alternatif lain yang lebih ekspresif dan lebih aman.

person François Andrieux    schedule 17.05.2021
comment
Wow, itu respon yang cepat, terima kasih atas petunjuk tentang fungsi alokasi. Mengejutkan betapa seringnya jawaban di C++ adalah jangan gunakan kata kunci itu, gunakan std::someWeirdFunctionIntroductedInC++›=11() - person awiebe; 18.05.2021
comment
@awiebe C++ memberi Anda alat untuk bekerja sedekat mungkin dengan perangkat keras. Namun alat-alat tersebut biasanya kuat dan tumpul, sehingga berbahaya dan sulit digunakan secara efektif. Jadi ia juga menyediakan alat melalui perpustakaan standar yang sedikit lebih jauh dari perangkat keras, namun sangat aman dan mudah. Inilah sebabnya mengapa Anda mempelajari begitu banyak fitur, tetapi diberitahu untuk tidak menggunakannya. Karena kecuali Anda melakukan sesuatu yang sangat unik atau aneh, alat tingkat rendah tersebut tidak berguna. Fitur-fitur yang lebih nyaman umumnya baik-baik saja. - person François Andrieux; 18.05.2021
comment
@awiebe Anda benar bahwa sebagian besar waktu, jika ada fitur perpustakaan standar yang rapi untuk menggantikan mekanisme bawaan, itu berasal dari C++11 atau lebih baru. C++11 pada dasarnya merevolusi bahasa tersebut, memungkinkan fitur perpustakaan standar yang sebelumnya tidak mungkin diimplementasikan. Perbedaan antara C++11 dan versi sebelumnya sangat signifikan sehingga pada dasarnya dapat dianggap sebagai dua bahasa berbeda. Berhati-hatilah saat mempelajari C++, untuk membedakan antara materi pendidikan yang menargetkan C++03 dan versi sebelumnya dengan materi yang menargetkan C++11 dan versi lebih baru. - person François Andrieux; 18.05.2021
comment
@awiebe, perhatikan juga bahwa keberadaan mekanisme tingkat yang lebih rendah seperti new memungkinkan sebagian besar perpustakaan standar (dan perpustakaan lainnya) ditulis dalam C++ murni (beberapa bagian mungkin memerlukan dukungan kompiler). Jadi sarannya juga bisa menggunakan ini hanya untuk membangun abstraksi tingkat yang lebih tinggi. - person Carsten S; 18.05.2021
comment
Jawaban ini sepertinya tidak benar. Baik delete dan delete[] memanggil destruktor. - person Barmar; 18.05.2021
comment
@Barmar Saya menulis ulang untuk memperjelas bahwa delete[] berpotensi memiliki overhead tambahan terkait dengan penghancuran setiap elemen array. - person François Andrieux; 18.05.2021
comment
Menghindari perulangan sepele dalam kasus satu elemen sepertinya merupakan optimasi mikro yang aneh, tapi mungkin masuk akal di masa-masa awal, ketika komputer lebih lambat dan semangat C++ lebih mirip dengan C. - person Barmar; 18.05.2021
comment
@Barmar Saya pikir kekhawatirannya lebih pada overhead memori daripada kecepatan. Masalahnya menjadi lebih jelas dengan penempatan baru, dimana penempatan portabel baru dari sebuah array saat ini tidak mungkin dilakukan karena potensi overhead ini. Lihat Dapatkah penempatan baru untuk array digunakan dalam a cara portabel?. Anda mungkin perlu memberikan lebih banyak penyimpanan daripada yang dibutuhkan elemen, namun tidak mungkin menanyakan implementasi berapa banyak penyimpanan tambahan yang diperlukan. - person François Andrieux; 18.05.2021
comment
@FrançoisAndrieux: Pilihan kata-nitpick ...alat-alat itu biasanya kuat dan tumpul...: Saya sebenarnya melihatnya sebagai alat bedah yang sangat tajam: Anda bisa mendapatkan apa yang Anda inginkan, sesuai keinginan Anda. Namun menjahit atau membersihkan prosedur pembedahan membutuhkan keterampilan dan bahan yang setara, plester tidak akan cukup. - person Willem van Rumpt; 18.05.2021
comment
@WillemvanRumpt Anda mungkin benar, analogi operasi mungkin lebih baik. Saya memilih alat tumpul karena Anda dapat melakukan pekerjaan dengan alat yang tumpul, namun Anda mungkin perlu melakukan lebih banyak usaha untuk mengerjakannya. - person François Andrieux; 18.05.2021
comment
Ini tidak hanya diperlukan untuk destruktor, tetapi juga untuk mengosongkan memori pada sistem di mana ukuran blok memori tidak diketahui oleh pengalokasi dan harus ditentukan saat mengosongkan. Misalnya, di AmigaOS diperbolehkan melakukan char *p = AllocMem(64); FreeMem(p + 16, 32);, sehingga Anda memiliki dua alokasi masing-masing 16 byte, di p dan p+48. Di AmigaOS, delete o; memanggil FreeMem(o, sizeof *o);, jadi menggunakan penghapusan non-array pada array hanya akan membebaskan elemen pertama. - person Simon Richter; 18.05.2021
comment
Fakta bahwa delete pointer dan delete [] pointer menggunakan fungsi deallokasi yang berbeda tidak ada hubungannya dengan yang pertama harus berurusan dengan kemungkinan penghapusan polimorfik atau yang kedua harus memulihkan jumlah elemen yang akan dihapus. Pada saat Anda masuk ke fungsi deallokasi, semuanya sudah selesai. Saya tidak mengetahui alasan rasional mengapa mereka menggunakan serangkaian fungsi alokasi dan deallokasi yang terpisah. - person Deduplicator; 18.05.2021
comment
@Deduplicator Saya tidak* cukup yakin tentang mengapa ada fungsi deallokasi yang berbeda untuk delete dan delete[], tidak jelas bagi saya apakah tidak mungkin memiliki satu versi yang dapat menangani kedua kasus tanpa overhead. Saya akan mengambil kembali bagian pertanyaan itu dari jawaban saya. Bagian tentang destruktor seharusnya cukup untuk membenarkan perbedaan antara delete dan delete[]. Sunting : jangan lupa* - person François Andrieux; 18.05.2021
comment
@Barmar: Anda benar tentang "optimasi mikro yang aneh". AFAICT, akan menjadi standar yang benar-benar valid bagi kompiler C++ untuk mengimplementasikan new X sama dengan new X[1], dan delete sama dengan delete[], sehingga memaafkan penggunaan operator penghapusan yang salah secara tidak sengaja. Tapi itu berarti "membuang" memori untuk menyimpan panjang array. - person dan04; 18.05.2021
comment
@dan04 Standar ini mengharuskan ekspresi baru non-array yang membuat T (new T;) diperlukan untuk meminta tepat sizeof(T) byte dari fungsi alokasi. Karena aturan ini, Anda hanya dapat mengimplementasikan new T; sebagai new T[1]; jika ekspresi baru dengan tipe array juga tidak memiliki overhead alokasi array pada platform tersebut. Lihat : Argumen tersebut tidak boleh kurang dari ukurannya dari objek yang dibuat; mungkin lebih besar dari ukuran objek yang dibuat hanya jika objek tersebut berupa array. - person François Andrieux; 18.05.2021
comment
@Deduplicator Setelah dipikir-pikir, ada kendala lain. Karena operator new dapat diganti secara terpisah dari operator new[] meskipun itu mungkin tidak dapat dilakukan. - person François Andrieux; 18.05.2021
comment
@FrançoisAndrieux Ya. Memiliki dua fungsi terpisah sangat merepotkan di sana. - person Deduplicator; 18.05.2021
comment
Ya, begitu mereka membuat keputusan desain asli, ada konsekuensi yang mempengaruhi spesifikasi. - person Barmar; 19.05.2021
comment
Perhatikan bahwa terkadang Anda harus memasangkan new T() dengan delete[]. baru yang tampak skalar sangat mampu melakukan alokasi array, jika tipe sebenarnya adalah array. - person Ben Voigt; 19.05.2021
comment
Jawabannya sering kali menggunakan fungsi yang aneh, mungkin dari C++11, @awiebe, karena sebagian besar perpustakaan standar didedikasikan untuk menyediakan pembungkus di sekitar fitur bahasa inti agar kompiler melakukan pekerjaan berat untuk Anda kapan pun Anda tidak perlu mengoptimalkan sesuatu hingga batas absolut. Ini menukar potensi sedikit penurunan kecepatan dan/atau peningkatan ukuran yang dapat dieksekusi dengan kode yang dapat dibaca, pengurangan pembukuan di pihak Anda, dan jaminan keamanan yang lebih baik. Misalnya, std::vector pada dasarnya hanyalah pembungkus cantik yang mengelola new[] dan delete[] untuk Anda, dengan tambahan. - person Justin Time - Reinstate Monica; 19.05.2021
comment
@FrançoisAndrieux: C++ memberi Anda alat untuk bekerja sedekat mungkin dengan perangkat keras. - kecuali jika panitia memutuskan untuk tidak melakukannya. Perhatikan bahwa C++ new/delete tidak memiliki mekanisme C realloc apa pun (atau try_realloc untuk non-trivially-copyable) yang dapat memperluas alokasi yang ada untuk menghindari penyalinan jika memungkinkan. Kebanyakan OS nyata dapat melakukan itu, dan jika bukan implementasi yang sepele, cukup baru/salin/hapus, atau kembalikan false untuk try_realloc. Hal ini membuat std::vector besar membuang banyak waktu, bandwidth memori, dan kesalahan halaman, menyalin berbagai hal seiring pertumbuhannya, jika Anda tidak .memesan terlebih dahulu. - person Peter Cordes; 19.05.2021
comment
Bjarne dahulu kala mengatakan [] adalah sebuah kesalahan. - person philipxy; 19.05.2021
comment
@BenVoigt Apakah ada T-shirt yang membuat C++ tetap aneh? - person Peter - Reinstate Monica; 20.05.2021
comment
@philipxy: Diperlukan kutipan. - person Ben Voigt; 20.05.2021

Pada dasarnya, malloc dan free mengalokasikan memori, dan new dan delete membuat dan menghancurkan objek. Jadi kalian harus tahu apa saja objeknya.

Untuk menguraikan overhead yang tidak ditentukan yang disebutkan oleh jawaban François Andrieux, Anda dapat melihat jawaban saya pada pertanyaan ini di mana saya memeriksa apa yang dimaksud dengan a implementasi spesifik yang dilakukan (Visual C++ 2013, 32-bit). Implementasi lain mungkin melakukan hal serupa atau tidak.

Jika new[] digunakan dengan array objek dengan destruktor non-sepele, yang dilakukannya adalah mengalokasikan 4 byte lebih banyak, dan mengembalikan penunjuk yang digeser 4 byte ke depan, jadi ketika delete[] ingin mengetahui berapa banyak objek yang ada di sana, itu mengambil penunjuk, menggesernya 4 byte sebelumnya, dan mengambil nomor di alamat itu dan memperlakukannya sebagai jumlah objek yang disimpan di sana. Ia kemudian memanggil destruktor pada setiap objek (ukuran objek diketahui dari jenis pointer yang diteruskan). Kemudian, untuk melepaskan alamat yang tepat, ia meneruskan alamat yang 4 byte sebelum alamat yang diteruskan.

Pada implementasi ini, meneruskan array yang dialokasikan dengan new[] ke delete biasa akan menyebabkan pemanggilan satu destruktor, dari elemen pertama, diikuti dengan meneruskan alamat yang salah ke fungsi deallokasi, sehingga merusak heap. Jangan lakukan itu!

person milleniumbug    schedule 18.05.2021

Sesuatu yang tidak disebutkan dalam jawaban lain (semuanya bagus) adalah bahwa akar permasalahannya adalah bahwa array - yang diwarisi dari C - tidak pernah menjadi hal kelas satu di C++.

Mereka memiliki semantik C primitif dan tidak memiliki semantik C++, dan oleh karena itu kompiler C++ dan dukungan runtime, yang memungkinkan Anda atau sistem runtime kompiler melakukan hal-hal berguna dengan petunjuk ke sana.

Faktanya, mereka sangat tidak didukung oleh C++ sehingga penunjuk ke suatu array terlihat seperti penunjuk ke satu hal. Hal ini, khususnya, tidak akan terjadi jika array adalah bagian bahasa yang tepat - bahkan sebagai bagian dari perpustakaan, seperti string atau vektor.

Kutil pada bahasa C++ ini terjadi karena warisan dari C. Dan ini tetap menjadi bagian dari bahasa tersebut - meskipun kita sekarang memiliki std::array untuk array dengan panjang tetap dan (selalu memiliki) std::vector untuk array dengan panjang variabel - sebagian besar untuk tujuan kompatibilitas: Mampu memanggil dari C++ ke API sistem operasi dan ke perpustakaan yang ditulis dalam bahasa lain menggunakan interop bahasa C.

Dan ... karena ada banyak sekali buku, situs web, dan ruang kelas di luar sana yang mengajarkan susunan sangat awal dalam pedagogi C++ mereka, karena a) mampu menulis contoh yang berguna/menarik sejak dini yang ternyata memang benar memanggil OS API, dan tentu saja karena kekuatan luar biasa dari b) itulah cara kami selalu melakukannya.

person davidbak    schedule 18.05.2021
comment
Jawaban ini membuat sejumlah klaim yang salah, tampaknya didasarkan pada ketidaktahuan bahwa C dan C++ mendukung tipe pointer-to-array. Ini bukan kurangnya kemampuan untuk mengekspresikan pointer ke array, melainkan tidak digunakannya kemampuan tersebut dalam praktik. - person Ben Voigt; 19.05.2021
comment
pointer-to-array langsung meluruh menjadi pointer-to-elemen, dan begitulah cara penggunaannya., bukan? Berapa banyak tanda tangan fungsi/metode C++ (atau C) yang menggunakan tipe pointer-to-array? Tidak seorang pun, kecuali seorang pun, yang mengajarkan hal itu, dan juga tidak demikian cara penggunaannya. Apakah Anda tidak setuju? Misalnya, tunjukkan kepada saya di mana di API Unix/Linux pointer-ke-array digunakan dalam tanda tangan fungsi di atas pointer telanjang yang diasumsikan oleh dokumentasi sebagai array? @BenVoigt - person davidbak; 19.05.2021
comment
@BenVoigt - Baik Bahasa Pemrograman C (Kernighan, Ritchie, 1978) maupun C: A Referrence Manual - edisi ke-3 (Harbison, Steele, 1991) (termasuk ANSI dan C tradisional) membahas pointer-to-array - mereka hanya membahas bagaimana array T dan pointer ke T hampir setara. Ketika pengidentifikasi array pertama kali muncul dalam ekspresi, jenis pengidentifikasi diubah dari array T menjadi penunjuk ke T dan nilai pengidentifikasi diubah menjadi penunjuk ke elemen pertama array. (Harbison, hal 111). Dan masih banyak referensi serupa lainnya. - person davidbak; 19.05.2021
comment
Desain dan Evolusi C++ (Stroustrup, 1994) tidak hanya tidak menyebutkan pointer-to-array, tetapi juga menggemakan kata-kata Harbison/Steele. - person davidbak; 19.05.2021
comment
Baik C++ Efektif - edisi ke-3 (Meyers, 2008) maupun C++ Lebih Efektif (Meyers, 1996) tidak menyebutkan tipe pointer-to-array. Aku bisa melanjutkan dengan buku-buku dari perpustakaanku, tapi... Aku tidak begitu peduli. Persoalannya bukanlah apakah pada suatu waktu - bahkan pada awalnya - bahasa-bahasa tersebut, secara teknis, memiliki kemampuan ini. Intinya adalah tidak ada yang pernah menggunakannya. Pernah. Fakta bahwa saya tidak menyebutkannya dalam jawaban saya bukan berarti saya tidak mengetahuinya. Hanya saja saya tahu itu adalah sisa-sisa pengetahuan penulis kompiler yang tidak berguna. Tidak pernah digunakan, tidak pernah diajarkan. - person davidbak; 19.05.2021
comment
Masalah inti di sini adalah tipe pointer-to-array dan reference-to-array sangat sulit dibaca, sehingga orang-orang menjadi terbiasa untuk tidak menggunakannya, yang menyebabkan pengetahuan menjadi terpinggirkan. . Cara termudah untuk menggunakannya adalah dengan templat atau decltype, dan menggunakannya biasanya dengan cepat berubah menjadi hampir berantakan. create() di sini cukup buruk (dalam berbagai cara), bayangkan saja sebuah fungsi yang mengambil pointer ke dua array dan mengembalikan pointer ke jenis array yang berbeda. - person Justin Time - Reinstate Monica; 19.05.2021
comment
Karena penggunaan umum new[] adalah mengalokasikan array dengan ukuran yang tidak diketahui pada waktu kompilasi, pointer-to-array C tidak terlalu membantu. - person Jack Aidley; 19.05.2021
comment
@JackAidley: Ini berlaku untuk array 2D, karena C99 juga mendukung VLA. Cara lain untuk melakukan malloc array 2D? menunjukkan bahwa int (*p)[2] = malloc(3 * sizeof *p); adalah cara C untuk mengalokasikan objek int [3][2] array-of-arrays secara dinamis, dengan penduduk setempat menunjuk ke sana. 2 dan 3 keduanya bisa menjadi variabel runtime di C, tidak seperti C++. - person Peter Cordes; 19.05.2021
comment
@davidbak: Memang, ini adalah pilihan antara harus menggunakan p[INDEX2D(i,j)] untuk meniru pengindeksan 2D dalam array datar, atau menggunakan p[i][j] dan memiliki kompiler skala i dengan dimensi array lain untuk Anda, berdasarkan tipe pointer-to-VLA. Salah satunya boleh saja, selama Anda menghindari int **p dengan alokasi terpisah untuk setiap baris. (nvm, sepertinya Anda menghapus komentar yang saya balas. Saya juga bisa menghapus yang ini.) - person Peter Cordes; 19.05.2021
comment
@PeterCordes - wrt pertanyaan khusus ini, VLA hanya untuk C dan hanya distandarisasi di C99 - jauh setelah pembuatan C++. Wikipedia tentang VLA sayangnya kurang dalam hal kapan GCC pertama kali mendukungnya - tetapi diragukan Stroustrup akan menggunakannya sebagai model, karena tidak standar, karena implementasi C++ pertamanya, cfront, adalah penerjemah ke C, dan dia mungkin bahkan punya ide untuk tidak mengikatnya ke kompiler C tertentu. - person davidbak; 19.05.2021
comment
@PeterCordes - Saya menghapus komentar saya karena ... maksud Anda adalah tanggapan yang valid terhadap komentar yang ada ... meskipun saya sendiri ragu tentang keterbacaan konstruksinya. - person davidbak; 19.05.2021
comment
Ya, Jack membalas @JustinTime, yang menunjukkan bahwa sintaks pointer-to-array cukup sulit dibaca. Tidak ada argumen dari saya, dan biasanya tidak layak untuk diganggu. Kurangnya dukungan ISO C++ untuk VLA berarti sering kali tidak berguna menggunakan pointer-to-array di C++ portabel; std::array sama kuat dan bersihnya. (GNU C++ mendukung VLA di C++.) Tapi C++ pastinya bisa memiliki pointer ke array, jadi saya setuju dengan komentar awal Ben. Satu-satunya jenis array yang didukungnya memiliki ukuran konstan waktu kompilasi, tetapi itu dapat diarahkan. - person Peter Cordes; 19.05.2021
comment
@petercordes C99 muncul agak terlambat untuk C++ - person Jack Aidley; 19.05.2021
comment
@JackAidley: Benar, desain awal C++ dibatasi oleh keinginan untuk mengkompilasi ke C portabel pada saat itu, tanpa perlu bergantung pada fitur VLA yang didukung oleh beberapa tetapi tidak semua kompiler C pada saat itu. Itu saja tidak menjelaskan mengapa C++ modern masih kekurangannya (alasannya mungkin terkait dengan komplikasi destruktor). Saya pikir kita baru saja membicarakan mengapa sintaks pointer-to-array tidak banyak digunakan (dalam C atau C++), dan saya menunjukkan bahwa ada kasus penggunaan C untuk itu. Saya tidak mencoba untuk berdebat secara langsung tentang desain C++ (kecuali bahwa ia tidak mendukung pointer-to-array) - person Peter Cordes; 19.05.2021
comment
@PeterCordes - Saya kira alasan C++ modern masih kekurangannya adalah karena tidak sepadan dengan upaya untuk melakukan operasi apa pun pada bahasa tersebut - besar atau kecil - pada saat ini untuk membuat array gaya C berperilaku lebih baik. Jauh lebih menguntungkan jika kita hanya memberi kita peringkat operator[] yang sewenang-wenang, lalu siapa pun dapat menggunakan fasilitas kelas dan templat bawaan C++ yang telah terbukti untuk membuat jenis array apa pun yang mereka inginkan. Kemudian, operator new dan delete - baik versi skalar maupun array - hanya akan terlihat terenkapsulasi dan disembunyikan dalam satu atau dua metode kelas tingkat rendah yang memerlukan heap. - person davidbak; 19.05.2021
comment
Sintaks VLA jauh lebih bersih daripada alloca (yang juga merupakan IIRC non-standar) ketika Anda menginginkan array sementara lokal kecil dalam penyimpanan otomatis, dengan ukuran variabel runtime yang Anda tahu cukup kecil. (yaitu di tumpukan dalam implementasi C++ normal). Untuk alokasi dinamis, tentu Anda bisa menggabungkan new[] di kelas dengan operator[] yang kelebihan beban. Rasanya seperti banyak pekerjaan yang sia-sia untuk menulis pembungkus seperti array untuk alloca ketika GNU C++ mendukung alignas(32) float foo[n]; untuk sejumlah kecil ruang awal. - person Peter Cordes; 19.05.2021
comment
@PeterCordes - kasus penggunaan yang menarik, saya pasti pernah menggunakan alloca() di masa lalu - tapi saya ingin tahu apakah itu akan menjadi hal yang baik - atau bahkan hal yang dapat diterima - ke depannya karena kita memiliki coroutines . .. (termasuk VLA di dalamnya) - person davidbak; 19.05.2021
comment
Dari en.cppreference.com/w/cpp/lingual/coroutines, itu sepertinya desainnya kompatibel untuk dipanggil dari fungsi yang menggunakan VLA/alloca. Ruang penyimpanan untuk status coroutine dialokasikan dalam tumpukan dengan new, atau dapat dioptimalkan (menggunakan ruang tumpukan) jika masa pakainya memungkinkan, dan jika ukuran bingkai coroutine diketahui di lokasi panggilan. Kompiler mungkin melarang alokasi / VLA di dalam fungsi coroutine (mereka mungkin memerlukan ukuran bingkai tumpukan yang diperbaiki selama durasi coroutine), tetapi standar menyediakan cara pasti untuk mengetahui kapan suatu fungsi adalah coroutine. - person Peter Cordes; 19.05.2021
comment
Ada alasan mengapa C++ tidak mengadopsinya, tetapi alasan tersebut tidak relevan dengan pilihan delete[] vs. delete. Itu sudah dilakukan sejak lama. Betapa berharganya Bjarne mengakuinya sebagai sebuah kesalahan, IIRC. - person Jack Aidley; 19.05.2021
comment
@davidbak Jelas Anda tidak mengetahuinya, karena Anda secara keliru percaya bahwa pointer-to-array meluruh menjadi pointer-to-the-first-elemen. Itu tidak. Array meluruh menjadi penunjuk ke elemen awal, penunjuk ke array dapat diubah dengan penunjuk ke elemen pertama tetapi tidak meluruh. Dan sangat buruk bagi buku teks mana pun untuk tidak mencakup pointer-to-array, karena bertentangan dengan buku teks Anda yang belum pernah digunakan, ini sebenarnya cukup umum. Array dua dimensi meluruh menjadi pointer-to-array. Pengindeksan ke dalam array dua dimensi melakukan aritmatika pointer pada pointer-ke-array. - person Ben Voigt; 20.05.2021

Umumnya, kompiler C++ dan runtime terkaitnya dibangun di atas runtime C platform. Khususnya dalam hal ini manajer memori C.

Manajer memori C memungkinkan Anda mengosongkan satu blok memori tanpa mengetahui ukurannya, namun tidak ada cara standar untuk mendapatkan ukuran blok dari runtime dan tidak ada jaminan bahwa blok yang sebenarnya dialokasikan persis dengan ukuran Anda. diminta. Ini mungkin lebih besar.

Oleh karena itu, ukuran blok yang disimpan oleh manajer memori C tidak dapat digunakan secara berguna untuk mengaktifkan fungsionalitas tingkat yang lebih tinggi. Jika fungsionalitas tingkat yang lebih tinggi memerlukan informasi mengenai ukuran alokasi maka fungsionalitas tersebut harus menyimpannya sendiri. (Dan C++ delete[] memerlukan ini untuk tipe dengan destruktor, untuk menjalankannya pada setiap elemen.)

C++ juga memiliki sikap bahwa Anda hanya membayar untuk apa yang Anda gunakan, menyimpan bidang panjang ekstra untuk setiap alokasi (terpisah dari pembukuan pengalokasi yang mendasarinya) tidak akan cocok dengan sikap ini.

Karena cara normal untuk merepresentasikan array dengan ukuran yang tidak diketahui (pada waktu kompilasi) di C dan C++ adalah dengan pointer ke elemen pertamanya, kompiler tidak dapat membedakan antara alokasi objek tunggal dan alokasi array berdasarkan tipenya. sistem. Jadi terserah pada programmer untuk membedakannya.

person plugwash    schedule 19.05.2021

Cerita sampulnya adalah delete diperlukan karena hubungan C++ dengan C.

Operator new dapat membuat objek yang dialokasikan secara dinamis dari hampir semua jenis objek.

Namun, karena warisan C, penunjuk ke suatu tipe objek menjadi ambigu antara dua abstraksi:

  • menjadi lokasi suatu objek, dan
  • menjadi dasar dari array dinamis.

Situasi delete versus delete[] mengikuti dari situ.

Namun, hal tersebut tidak benar, karena, meskipun pengamatan di atas benar, satu operator delete dapat digunakan. Tidak logis bahwa diperlukan dua operator.

Ini bukti informal. Pemanggilan operator new T (kasus objek tunggal) secara implisit dapat berperilaku seolah-olah new T[1]. Artinya, setiap new selalu dapat mengalokasikan sebuah array. Jika tidak ada sintaks array yang disebutkan, bisa jadi secara implisit array [1] akan dialokasikan. Maka, hanya ada satu delete yang berperilaku seperti delete[] saat ini.

Mengapa desain itu tidak diikuti?

Saya pikir intinya adalah seperti biasa: itu adalah seekor kambing yang dikorbankan untuk dewa efisiensi. Saat Anda mengalokasikan array dengan new [], penyimpanan ekstra dialokasikan untuk metadata guna melacak jumlah elemen, sehingga delete [] dapat mengetahui berapa banyak elemen yang perlu diiterasi untuk dimusnahkan. Saat Anda mengalokasikan satu objek dengan new, metadata seperti itu tidak diperlukan. Objek dapat dibuat langsung di memori yang berasal dari pengalokasi yang mendasarinya tanpa header tambahan.

Ini adalah bagian dari jangan membayar untuk apa yang tidak Anda gunakan dalam hal biaya run-time. Jika Anda mengalokasikan objek tunggal, Anda tidak perlu membayar overhead representasi apa pun pada objek tersebut untuk menghadapi kemungkinan bahwa objek dinamis apa pun yang direferensikan oleh penunjuk mungkin berupa array. Namun, Anda dibebani dengan tanggung jawab untuk menyandikan informasi tersebut dengan cara Anda mengalokasikan objek dengan array new dan kemudian menghapusnya.

person Kaz    schedule 20.05.2021

Sebuah contoh mungkin bisa membantu. Saat Anda mengalokasikan array objek bergaya C, objek tersebut mungkin memiliki destruktornya sendiri yang perlu dipanggil. Operator delete tidak melakukan itu. Ini berfungsi pada objek kontainer, tetapi tidak pada array gaya C. Anda membutuhkan delete[] untuk mereka.

Berikut ini contohnya:

#include <iostream>
#include <stdlib.h>
#include <string>

using std::cerr;
using std::cout;
using std::endl;

class silly_string : private std::string {
  public:
    silly_string(const char* const s) :
      std::string(s) {}
    ~silly_string() {
      cout.flush();
      cerr << "Deleting \"" << *this << "\"."
           << endl;
      // The destructor of the base class is now implicitly invoked.
    }

  friend std::ostream& operator<< ( std::ostream&, const silly_string& );
};

std::ostream& operator<< ( std::ostream& out, const silly_string& s )
{
  return out << static_cast<const std::string>(s);
}

int main()
{
  constexpr size_t nwords = 2;
  silly_string *const words = new silly_string[nwords]{
    "hello,",
    "world!" };

  cout << words[0] << ' '
       << words[1] << '\n';

  delete[] words;

  return EXIT_SUCCESS;
}

Program pengujian tersebut secara eksplisit menginstrumentasikan panggilan destruktor. Ini jelas merupakan contoh yang dibuat-buat. Salah satu alasannya adalah suatu program tidak perlu segera mengosongkan memori sebelum program tersebut dihentikan dan melepaskan semua sumber dayanya. Namun hal ini menunjukkan apa yang terjadi dan bagaimana urutannya.

Beberapa kompiler, seperti clang++, cukup pintar untuk memperingatkan Anda jika Anda mengabaikan [] di delete[] words;, namun jika Anda memaksanya untuk mengkompilasi kode buggy, Anda akan mendapatkan kerusakan tumpukan.

person Davislor    schedule 19.05.2021

Hapus adalah operator yang menghancurkan objek array dan non-array (pointer) yang dihasilkan oleh ekspresi baru.

Ini dapat digunakan dengan menggunakan operator Hapus atau operator Hapus [ ]. Operator baru digunakan untuk alokasi memori dinamis yang menempatkan variabel pada memori heap. Ini berarti operator Hapus membatalkan alokasi memori dari heap. Penunjuk ke objek tidak dimusnahkan, nilai atau blok memori yang ditunjuk oleh penunjuk akan dimusnahkan. Operator hapus memiliki tipe pengembalian batal yang tidak mengembalikan nilai.

person Aditya-ai    schedule 14.06.2021