Apakah 'baru' dan 'hapus' sudah tidak digunakan lagi di C ++?

68

Saya menemukan sebuah kuis yang melibatkan deklarasi array dengan ukuran berbeda. Hal pertama yang terlintas di pikiran saya adalah bahwa saya perlu menggunakan alokasi dinamis dengan newperintah, seperti ini:

while(T--) {
   int N;
   cin >> N;
   int *array = new int[N];
   // Do something with 'array'
   delete[] array;
}

Namun, saya melihat bahwa salah satu solusi memungkinkan kasus berikut:

while(T--) {
    int N;
    cin >> N;
    int array[N];
    // Do something with 'array'
}

Setelah sedikit riset, saya membaca bahwa g ++ memungkinkan ini, tetapi membuat saya berpikir, dalam hal apa kemudian diperlukan untuk menggunakan alokasi dinamis? Atau apakah kompiler menerjemahkan ini sebagai alokasi dinamis?

Fungsi hapus disertakan. Perhatikan, bagaimanapun, bahwa pertanyaan di sini bukan tentang kebocoran memori.

learning_dude
sumber
54
Contoh kedua menggunakan array panjang variabel yang belum pernah menjadi bagian dari C ++. Untuk kasus ini, gunakan std::vector( std::vector<int> array(N);).
Beberapa programmer Bung
7
Jawaban langsung untuk pertanyaan Anda harus: tidak, itu tidak ditinggalkan. Meskipun versi modern C ++ menyediakan banyak fitur yang menyederhanakan manajemen kepemilikan memori (smart pointer), masih umum dilakukan untuk mengalokasikan objek dengan memohon new OBJsecara langsung.
pptaszni
8
Untuk orang lain yang bingung mengapa orang berbicara tentang kebocoran memori, pertanyaannya telah diedit untuk memperbaiki bug yang tidak penting bagi pertanyaan
Mike Caron
4
@Mannoj lebih suka menggunakan istilah Dynamic dan Automatic untuk menumpuk dan menumpuk. Ini jarang tetapi mungkin untuk mengimplementasikan C ++ tanpa tumpukan dan tumpukan.
user4581301
1
Tidak ada yang pernah ditinggalkan dalam C ++ dan tidak akan pernah ada. Itu bagian dari apa artinya C ++.
JoelFan

Jawaban:

114

Cuplikan yang Anda tampilkan tidak idiomatis, kode C ++ modern.

newdan delete(dan new[]dan delete[]) tidak ditinggalkan dalam C ++ dan tidak akan pernah. Mereka masih merupakan cara untuk instantiate objek yang dialokasikan secara dinamis. Namun, karena Anda harus selalu mencocokkan a newdengan delete(dan a new[]dengan delete[]), mereka paling baik disimpan dalam kelas (perpustakaan) yang memastikan ini untuk Anda. Lihat Mengapa pemrogram C ++ meminimalkan penggunaan 'baru'? .

Cuplikan pertama Anda menggunakan "telanjang" new[]dan kemudian tidak pernah delete[]menciptakan array. Itu masalah. std::vectormelakukan semua yang Anda butuhkan di sini dengan baik. Ini akan menggunakan beberapa bentuk di newbelakang layar (saya tidak akan membahas rincian implementasi), tetapi untuk semua yang Anda harus peduli, itu adalah array yang dinamis tetapi lebih baik dan lebih aman.

Cuplikan kedua Anda menggunakan "array panjang variabel" (VLA), fitur C yang juga diizinkan oleh beberapa kompiler di C ++ sebagai ekstensi. Tidak seperti new, VLA pada dasarnya dialokasikan pada stack (sumber daya yang sangat terbatas). Tetapi yang lebih penting, mereka bukan fitur C ++ standar dan harus dihindari karena mereka tidak portabel. Mereka tentu tidak menggantikan alokasi dinamis (yaitu tumpukan).

Max Langhof
sumber
3
Saya ingin menambahkan bahwa walaupun VLA tidak ada dalam standar secara resmi, VLA didukung oleh semua kompiler utama, dan dengan demikian keputusan apakah menghindarinya lebih merupakan masalah gaya / preferensi daripada kepedulian realistis terhadap portabilitas.
Stack Tracer
4
Juga ingat bahwa Anda tidak dapat mengembalikan array atau menyimpannya di tempat lain, jadi VLA tidak akan pernah hidup lebih lama dari waktu fungsi
Ruslan
16
@StackTracer Sejauh yang saya ketahui, MSVC tidak mendukung VLA. Dan MSVC jelas merupakan "kompiler utama".
Max Langhof
2
"Anda harus selalu mencocokkan yang baru dengan penghapusan" - tidak jika Anda bekerja dengan Qt, karena kelas dasarnya semuanya memiliki pemulung, jadi Anda cukup sering menggunakan newdan melupakannya, Untuk elemen GUI, ketika widget orangtua ditutup, anak-anak keluar dari ruang lingkup dan dikumpulkan secara otomatis.
vsz
6
@vsz Bahkan di Qt, masing-masing newmasih memiliki kecocokan delete; hanya saja deletes dilakukan oleh widget induk daripada di blok kode yang sama dengan news.
jjramsey
22

Baiklah, sebagai permulaan, new/delete tidak mendapatkan usang.

Dalam kasus spesifik Anda, mereka bukan satu-satunya solusi. Apa yang Anda pilih tergantung pada apa yang disembunyikan di bawah komentar "lakukan sesuatu dengan array" Anda.

Contoh ke-2 Anda menggunakan ekstensi VLA non-standar yang mencoba menyesuaikan array pada stack. Ini memiliki keterbatasan tertentu - yaitu ukuran terbatas dan ketidakmampuan untuk menggunakan memori ini setelah array keluar dari ruang lingkup. Anda tidak dapat memindahkannya, itu akan "menghilang" setelah tumpukan dibuka.

Jadi, jika satu-satunya tujuan Anda adalah melakukan perhitungan lokal dan kemudian membuang data, itu mungkin benar-benar berfungsi dengan baik. Namun, pendekatan yang lebih kuat adalah mengalokasikan memori secara dinamis, lebih disukai dengan std::vector. Dengan begitu Anda mendapatkan kemampuan untuk membuat ruang untuk elemen yang persis sama banyaknya seperti yang Anda butuhkan berdasarkan nilai runtime (yang akan kita lakukan selama ini), tetapi juga akan membersihkan dirinya dengan baik, dan Anda dapat memindahkannya ruang lingkup ini jika Anda ingin menyimpan memori digunakan untuk nanti.

Berputar kembali ke awal, mungkin vector akan menggunakan newbeberapa lapisan lebih dalam, tetapi Anda tidak perlu khawatir dengan itu, karena antarmuka yang dihadirkannya jauh lebih unggul. Dalam hal itu, menggunakan newdan deletedapat dianggap tidak dianjurkan.

Bartek Banachewicz
sumber
1
Perhatikan "... beberapa lapisan lebih dalam". Jika Anda menerapkan wadah Anda sendiri, Anda harus tetap menghindari menggunakan newdan delete, tetapi lebih suka menggunakan smart pointer seperti std::unique_pointer.
Maks
1
yang sebenarnya disebutstd::unique_ptr
user253751
2
@ Max: std::unique_ptrpanggilan destruktor default deleteatau delete[], yang berarti bahwa objek yang dimiliki harus dialokasikan oleh newatau new[], yang panggilan telah disembunyikan std::make_uniquesejak C ++ 14.
Laurent LA RIZZA
15

Contoh kedua Anda menggunakan array panjang variabel (VLA), yang sebenarnya merupakan fitur C99 ( bukan C ++!), Namun demikian didukung oleh g ++ .

Lihat juga jawaban ini .

Perhatikan bahwa array panjang variabel berbeda dari new/ deletedan jangan "mencabut" mereka dengan cara apa pun.

Perlu diketahui juga bahwa VLA bukan ISO C ++.

andreee
sumber
13

C ++ modern menyediakan cara yang lebih mudah untuk bekerja dengan alokasi dinamis. Pointer pintar dapat menangani pembersihan setelah pengecualian (yang mungkin terjadi di mana saja jika diizinkan) dan pengembalian awal, segera setelah struktur data yang direferensikan keluar dari ruang lingkup, jadi mungkin masuk akal untuk menggunakan ini sebagai gantinya:

  int size=100;

  // This construct requires the matching delete statement.
  auto buffer_old = new int[size];

  // These versions do not require `delete`:
  std::unique_ptr<int[]> buffer_new (new int[size]);
  std::shared_ptr<int[]> buffer_new (new int[size]); 
  std::vector<int> buffer_new (size);  int* raw_access = buffer_new.data();

Dari C ++ 14 Anda juga dapat menulis

auto buffer_new = std::make_unique<int[]>(size);

ini terlihat lebih bagus dan akan mencegah kebocoran memori jika alokasi gagal. Dari C ++ 20 Anda harus dapat melakukan sebanyak mungkin

auto a = std::make_shared<int[]>(size);

ini bagi saya masih belum dikompilasi pada saat penulisan dengan gcc 7.4.0. Dalam dua contoh ini kami juga menggunakan autobukan tipe deklarasi di sebelah kiri. Dalam semua kasus, gunakan array seperti biasa:

buffer_old[0] = buffer_new[0] = 17;

Memori bocor dari new dan crash dari dua kali lipat deleteadalah sesuatu C ++ telah dihancurkan selama bertahun-tahun, menjadi "titik sentral" dari argumentasi untuk beralih ke bahasa lain. Mungkin lebih baik dihindari.

Audrius Meskauskas
sumber
Anda harus menghindari unique/shared_ptrkonstruktor yang mendukung make_unique/shared, bukan saja Anda tidak harus menulis tipe yang dikonstruksikan dua kali (menggunakan auto) tetapi Anda tidak berisiko bocor memori atau sumber daya jika konstruksi gagal sebagian (jika Anda menggunakan tipe yang dapat gagal)
Simon Buchan
2
make_unique tersedia dengan array dari C ++ 14, dan make_share dari C ++ 20 saja. Ini masih jarang pengaturan default sehingga mengusulkan std :: make_share <int []> (size) mencari saya agak sebelumnya.
Audrius Meskauskas
Cukup adil! Saya tidak terlalu make_shared<int[]>banyak menggunakan , ketika Anda selalu ingin vector<int>, tapi senang mengetahui.
Simon Buchan
Pedantri berlebih, tetapi IIRC unique_ptrkonstruktornya bukanhrow, jadi bagi Tyang memiliki konstruktor nothrow, jadi tidak ada risiko kebocoran dengan unique_ptr(new int[size])dan shared_ptrmemiliki yang berikut: "Jika pengecualian dilemparkan, hapus p dipanggil ketika T bukan tipe array, hapus [ ] p sebaliknya. ", jadi Anda memiliki efek yang sama - risikonya adalah untuk unique/shared_ptr(new MyPossiblyAllocatingType[size]).
Simon Buchan
3

baru dan hapus tidak ditinggalkan.

Objek yang dibuat oleh operator baru dapat dilewatkan dengan referensi. Objek dapat dihapus menggunakan delete.

baru dan hapus adalah aspek dasar bahasa. Kegigihan suatu objek dapat dikelola menggunakan yang baru dan menghapus. Ini pasti tidak akan ditinggalkan.

Statement - int array [N] adalah cara mendefinisikan sebuah array. Array dapat digunakan dalam lingkup blok kode terlampir. Itu tidak bisa dilewati seperti bagaimana suatu objek dilewatkan ke fungsi lain.

Gopinath
sumber
2

Contoh pertama membutuhkan delete[]di akhir, atau Anda akan memiliki kebocoran memori.

Contoh kedua menggunakan panjang array variabel yang tidak didukung oleh C ++; itu hanya memungkinkan konstan ekspresi untuk panjang array .

Dalam hal ini berguna untuk digunakan std::vector<>sebagai solusi; yang membungkus semua tindakan yang dapat Anda lakukan pada array ke dalam kelas templat.

zig silet
sumber
3
Apa yang Anda maksud dengan "sampai C ++ 11"? Saya cukup yakin, VLA tidak pernah menjadi bagian dari standar.
Churill
lihat standar c ++ 14 [standar c ++ 14] ( isocpp.org/files/papers/N3690.pdf ) di halaman 184 paragraf 8.3.4
zig razor
4
Thats tidak yang . Standar, tetapi hanya konsep dan bagian tentang “array runtime terikat" tidak membuatnya menjadi standar sejauh yang saya tahu cppreference tidak menyebutkan Vlas ada.
churill
1
@zigrazor cppreference.com memiliki daftar tautan ke draft terdekat sebelum / setelah publikasi masing-masing standar. Standar yang diterbitkan tidak tersedia secara bebas, tetapi konsep ini harus sangat dekat. Seperti yang dapat Anda lihat dari nomor dokumen, draf Anda yang ditautkan adalah beberapa draf yang lebih lama untuk C ++ 14.
kenari
2
@learning_dude Hal ini tidak didukung oleh standar. Jawabannya adalah (sekarang) benar (walaupun singkat). Ini hanya berfungsi untuk Anda karena GCC memungkinkannya sebagai ekstensi non-standar .
walnut
-4

Sintaksnya terlihat seperti C ++, tetapi idiomnya mirip dengan Algol60 tua biasa. Itu umum untuk memiliki blok kode seperti ini:

read n;
begin
    integer array x[1:n];
    ... 
end;

Contohnya dapat ditulis sebagai:

while(T--) {
    int N;
    cin >> N;
    {
        int array[N];
        // Do something with 'array'
    }
}

Saya terkadang melewatkan ini dalam bahasa saat ini;)

kdo
sumber