Saya telah menggunakan C ++ untuk sementara waktu, dan saya bertanya-tanya tentang kata kunci baru . Cukup, haruskah saya menggunakannya, atau tidak?
1) Dengan kata kunci baru ...
MyClass* myClass = new MyClass();
myClass->MyField = "Hello world!";
2) Tanpa kata kunci baru ...
MyClass myClass;
myClass.MyField = "Hello world!";
Dari perspektif implementasi, mereka sepertinya tidak berbeda (tapi saya yakin begitu) ... Namun, bahasa utama saya adalah C #, dan tentu saja metode 1 adalah yang biasa saya gunakan.
Kesulitannya tampaknya bahwa metode 1 lebih sulit untuk digunakan dengan kelas C ++ Cd.
Metode mana yang harus saya gunakan?
Pembaruan 1:
Baru- baru ini saya menggunakan kata kunci baru untuk memori tumpukan (atau toko gratis ) untuk array besar yang keluar dari ruang lingkup (yaitu dikembalikan dari suatu fungsi). Di mana sebelumnya saya menggunakan tumpukan, yang menyebabkan setengah dari elemen menjadi korup di luar ruang lingkup, beralih ke tumpukan penggunaan memastikan bahwa elemen-elemen itu dalam kebijaksanaan. Yay!
Pembaruan 2:
Seorang teman saya baru-baru ini memberi tahu saya ada aturan sederhana untuk menggunakan new
kata kunci; setiap kali Anda mengetik new
, ketik delete
.
Foobar *foobar = new Foobar();
delete foobar; // TODO: Move this to the right place.
Ini membantu mencegah kebocoran memori, karena Anda selalu harus menghapusnya di suatu tempat (yaitu ketika Anda memotong dan menempelnya ke destruktor atau sebaliknya).
std::vector
danstd::shared_ptr
. Ini membungkus panggilan kenew
dandelete
untuk Anda, sehingga Anda bahkan cenderung bocor memori. Tanyakan pada diri Anda, misalnya: apakah Anda selalu ingat untuk menempatkan korespondensi didelete
mana - mana pengecualian dapat dilemparkan? Memasukkandelete
s dengan tangan lebih sulit dari yang Anda kira.Jawaban:
Metode 1 (menggunakan
new
)delete
objek Anda nanti. (Jika Anda tidak menghapusnya, Anda dapat membuat kebocoran memori)delete
memilikinya. (yaitu Anda dapatreturn
menggunakan objek yang Anda buatnew
)delete
d; dan itu harus selalu dihapus , terlepas dari jalur kontrol mana yang diambil, atau jika ada pengecualian.Metode 2 (tidak menggunakan
new
)delete
nanti.return
mengarahkan pointer ke objek di stack)Sejauh mana yang digunakan; Anda memilih metode yang paling sesuai untuk Anda, mengingat kendala di atas.
Beberapa kasus mudah:
delete
, (dan potensi menyebabkan kebocoran memori ) Anda tidak boleh menggunakannyanew
.new
sumber
Ada perbedaan penting antara keduanya.
Segala sesuatu yang tidak dialokasikan dengan
new
berperilaku seperti tipe nilai dalam C # (dan orang sering mengatakan bahwa objek-objek tersebut dialokasikan pada stack, yang mungkin merupakan kasus paling umum / jelas, tetapi tidak selalu benar. Lebih tepatnya, objek yang dialokasikan tanpa menggunakannew
memiliki penyimpanan otomatis Durasi Segala sesuatu yang dialokasikan dengannew
dialokasikan pada heap, dan pointer ke sana dikembalikan, persis seperti tipe referensi di C #.Apa pun yang dialokasikan pada tumpukan harus memiliki ukuran konstan, ditentukan pada waktu kompilasi (kompiler harus mengatur penunjuk tumpukan dengan benar, atau jika objek adalah anggota kelas lain, ia harus menyesuaikan ukuran kelas lain itu) . Itu sebabnya array dalam C # adalah tipe referensi. Mereka harus, karena dengan tipe referensi, kita dapat memutuskan pada saat runtime berapa banyak memori yang diminta. Dan hal yang sama berlaku di sini. Hanya array dengan ukuran konstan (ukuran yang dapat ditentukan pada waktu kompilasi) yang dapat dialokasikan dengan durasi penyimpanan otomatis (pada stack). Array berukuran dinamis harus dialokasikan di heap, dengan memanggil
new
.(Dan di situlah kesamaan dengan C # berhenti)
Sekarang, apa pun yang dialokasikan pada tumpukan memiliki durasi penyimpanan "otomatis" (Anda sebenarnya dapat mendeklarasikan variabel sebagai
auto
, tetapi ini adalah default jika tidak ada jenis penyimpanan lain yang ditentukan sehingga kata kunci tidak benar-benar digunakan dalam praktiknya, tetapi di sinilah sebenarnya datang dari)Durasi penyimpanan otomatis artinya persis seperti apa, durasi variabel ditangani secara otomatis. Sebaliknya, apa pun yang dialokasikan pada heap harus dihapus secara manual oleh Anda. Ini sebuah contoh:
Fungsi ini menciptakan tiga nilai yang layak dipertimbangkan:
Pada baris 1, ia mendeklarasikan variabel
b
tipebar
pada stack (durasi otomatis).Pada baris 2, ia menyatakan
bar
pointerb2
pada stack (durasi otomatis), dan memanggil yang baru, mengalokasikanbar
objek pada heap. (durasi dinamis)Ketika fungsi kembali, hal berikut akan terjadi: Pertama,
b2
keluar dari ruang lingkup (urutan kehancuran selalu berlawanan dengan urutan konstruksi). Tapib2
itu hanya sebuah pointer, jadi tidak ada yang terjadi, memori yang ditempatinya cukup dibebaskan. Dan yang terpenting, memori yang ditunjuknya (bar
instance pada heap) TIDAK tersentuh. Hanya pointer yang dibebaskan, karena hanya pointer yang memiliki durasi otomatis. Kedua,b
keluar dari ruang lingkup, jadi karena memiliki durasi otomatis, penghancurnya disebut, dan memori dibebaskan.Dan
bar
contoh di heap? Mungkin masih di sana. Tidak ada yang mau repot menghapusnya, jadi udah bocor memori.Dari contoh ini, kita dapat melihat bahwa apa pun dengan durasi otomatis dijamin memiliki destruktor yang dipanggil ketika keluar dari ruang lingkup. Itu berguna. Tetapi apa pun yang dialokasikan pada heap berlangsung selama kita membutuhkannya, dan dapat berukuran secara dinamis, seperti dalam kasus array. Itu juga berguna. Kita dapat menggunakannya untuk mengelola alokasi memori kita. Bagaimana jika kelas Foo mengalokasikan sebagian memori pada tumpukan di konstruktornya, dan menghapus memori itu di destruktornya. Kemudian kita bisa mendapatkan yang terbaik dari kedua dunia, alokasi memori yang aman yang dijamin akan dibebaskan lagi, tetapi tanpa batasan memaksa segalanya untuk berada di tumpukan.
Dan itu persis bagaimana sebagian besar kode C ++ bekerja. Lihatlah perpustakaan standar
std::vector
misalnya. Itu biasanya dialokasikan pada stack, tetapi dapat secara dinamis berukuran dan diubah ukurannya. Dan ia melakukan ini dengan mengalokasikan memori pada tumpukan secara internal sesuai kebutuhan. Pengguna kelas tidak pernah melihat ini, jadi tidak ada kemungkinan kebocoran memori, atau lupa untuk membersihkan apa yang Anda alokasikan.Prinsip ini disebut RAII (Akuisisi Sumber Daya adalah Inisialisasi), dan dapat diperluas ke sumber daya apa pun yang harus diperoleh dan dirilis. (soket jaringan, file, koneksi basis data, kunci sinkronisasi). Semuanya dapat diperoleh di konstruktor, dan dirilis di destruktor, sehingga Anda dijamin bahwa semua sumber daya yang Anda peroleh akan dibebaskan lagi.
Sebagai aturan umum, jangan pernah gunakan baru / hapus langsung dari kode tingkat tinggi Anda. Selalu bungkus dalam kelas yang dapat mengelola memori untuk Anda, dan yang akan memastikan itu dibebaskan lagi. (Ya, mungkin ada pengecualian untuk aturan ini. Khususnya, smart pointer mengharuskan Anda untuk memanggil
new
langsung, dan meneruskan pointer ke konstruktornya, yang kemudian mengambil alih dan memastikandelete
dipanggil dengan benar. Tetapi ini masih merupakan aturan praktis yang sangat penting. )sumber
Ini hampir tidak pernah ditentukan oleh preferensi pengetikan Anda tetapi oleh konteksnya. Jika Anda perlu menyimpan objek di beberapa tumpukan atau jika terlalu berat untuk tumpukan Anda mengalokasikannya di toko gratis. Selain itu, karena Anda mengalokasikan objek, Anda juga bertanggung jawab untuk melepaskan memori. Cari
delete
operator.Untuk meringankan beban menggunakan manajemen toko bebas orang-orang telah menemukan hal-hal seperti
auto_ptr
danunique_ptr
. Saya sangat menyarankan Anda melihat ini. Mereka bahkan mungkin bisa membantu masalah mengetik Anda ;-)sumber
Jika Anda menulis dalam C ++ Anda mungkin menulis untuk kinerja. Menggunakan baru dan toko gratis jauh lebih lambat daripada menggunakan tumpukan (terutama ketika menggunakan utas) jadi hanya gunakan saat Anda membutuhkannya.
Seperti yang orang lain katakan, Anda perlu baru ketika objek Anda harus hidup di luar fungsi atau ruang lingkup objek, objek sangat besar atau ketika Anda tidak tahu ukuran array pada waktu kompilasi.
Juga, cobalah untuk tidak pernah menggunakan delete. Bungkus baru Anda menjadi pointer pintar sebagai gantinya. Biarkan panggilan pointer pintar hapus untuk Anda.
Ada beberapa kasus di mana pointer pintar tidak pintar. Jangan pernah menyimpan std :: auto_ptr <> di dalam wadah STL. Ini akan menghapus pointer terlalu cepat karena operasi penyalinan di dalam wadah. Kasus lain adalah ketika Anda memiliki wadah STL yang sangat besar dari pointer ke objek. boost :: shared_ptr <> akan memiliki satu ton overhead kecepatan saat menabrak jumlah referensi naik dan turun. Cara yang lebih baik untuk pergi dalam kasus itu adalah dengan menempatkan wadah STL ke objek lain dan memberikan objek itu destruktor yang akan memanggil delete pada setiap pointer dalam wadah.
sumber
Jawaban singkatnya adalah: jika Anda seorang pemula di C ++, Anda seharusnya tidak pernah menggunakan
new
ataudelete
diri Anda sendiri.Sebagai gantinya, Anda harus menggunakan pointer pintar seperti
std::unique_ptr
danstd::make_unique
(atau lebih jarang,std::shared_ptr
danstd::make_shared
). Dengan begitu, Anda tidak perlu khawatir tentang kebocoran memori. Dan bahkan jika Anda lebih maju, praktik terbaik biasanya adalah merangkum cara kustom yang Anda gunakannew
dandelete
ke dalam kelas kecil (seperti penunjuk cerdas khusus) yang didedikasikan hanya untuk mengatasi masalah siklus hidup.Tentu saja, di belakang layar, pointer pintar ini masih melakukan alokasi dan deallokasi dinamis, sehingga kode yang menggunakannya masih memiliki overhead runtime terkait. Jawaban lain di sini telah membahas masalah ini, dan bagaimana membuat keputusan desain tentang kapan harus menggunakan smart pointer versus hanya membuat objek pada stack atau menggabungkannya sebagai anggota langsung dari suatu objek, cukup baik sehingga saya tidak akan mengulanginya. Tetapi ringkasan eksekutif saya adalah: jangan gunakan pointer pintar atau alokasi dinamis sampai sesuatu memaksa Anda melakukannya.
sumber
Tanpa
new
kata kunci Anda menyimpannya di tumpukan panggilan . Menyimpan variabel yang terlalu besar pada stack akan menyebabkan stack overflow .sumber
Jawaban sederhananya adalah ya - new () membuat objek di heap (dengan efek samping yang tidak menguntungkan yang harus Anda kelola seumur hidup (dengan secara eksplisit memanggil delete di atasnya), sedangkan form kedua membuat objek di tumpukan saat ini lingkup dan objek itu akan hancur ketika keluar dari ruang lingkup.
sumber
Jika variabel Anda hanya digunakan dalam konteks fungsi tunggal, Anda lebih baik menggunakan variabel stack, yaitu, Opsi 2. Seperti yang orang lain katakan, Anda tidak perlu mengelola masa hidup variabel stack - mereka dibangun dan dihancurkan secara otomatis. Juga, mengalokasikan / membatalkan alokasi variabel pada heap lambat dengan perbandingan. Jika fungsi Anda dipanggil cukup sering, Anda akan melihat peningkatan kinerja yang luar biasa jika menggunakan variabel tumpukan versus variabel tumpukan.
Yang mengatakan, ada beberapa contoh yang jelas di mana variabel stack tidak cukup.
Jika variabel stack memiliki jejak memori yang besar, maka Anda berisiko meluap. Secara default, ukuran tumpukan setiap utas adalah 1 MB di Windows. Kecil kemungkinan bahwa Anda akan membuat variabel tumpukan yang berukuran 1 MB, tetapi Anda harus ingat bahwa pemanfaatan tumpukan bersifat kumulatif. Jika fungsi Anda memanggil fungsi yang memanggil fungsi lain yang memanggil fungsi lain yang ..., variabel stack di semua fungsi ini mengambil ruang di tumpukan yang sama. Fungsi rekursif dapat mengalami masalah ini dengan cepat, tergantung pada seberapa dalam rekursi tersebut. Jika ini merupakan masalah, Anda dapat menambah ukuran tumpukan (tidak disarankan) atau mengalokasikan variabel di tumpukan menggunakan operator baru (disarankan).
Yang lain, kondisi yang lebih mungkin adalah bahwa variabel Anda perlu "hidup" di luar ruang lingkup fungsi Anda. Dalam hal ini, Anda akan mengalokasikan variabel pada heap sehingga dapat dicapai di luar ruang lingkup fungsi yang diberikan.
sumber
Apakah Anda mengeluarkan myClass dari suatu fungsi, atau mengharapkannya ada di luar fungsi itu? Seperti beberapa orang lain katakan, ini semua tentang ruang lingkup ketika Anda tidak mengalokasikan pada heap. Ketika Anda meninggalkan fungsi, itu hilang (akhirnya). Salah satu kesalahan klasik yang dibuat oleh pemula adalah upaya untuk membuat objek lokal beberapa kelas dalam suatu fungsi dan mengembalikannya tanpa mengalokasikannya di heap. Saya dapat mengingat debugging hal ini kembali di hari-hari awal saya melakukan c ++.
sumber
Metode kedua membuat instance pada stack, bersama dengan hal-hal seperti sesuatu yang dideklarasikan
int
dan daftar parameter yang dilewatkan ke fungsi.Metode pertama memberi ruang bagi penunjuk pada tumpukan, yang telah Anda tetapkan ke lokasi di memori tempat yang baru
MyClass
dialokasikan di heap - atau toko gratis.Metode pertama juga mengharuskan
delete
Anda membuat apa yang Anda buatnew
, sedangkan dalam metode kedua, kelas secara otomatis dihancurkan dan dibebaskan ketika jatuh keluar dari ruang lingkup (biasanya kurung tutup berikutnya).sumber
Jawaban singkatnya adalah ya kata kunci "baru" itu sangat penting karena ketika Anda menggunakannya, data objek disimpan di tumpukan sebagai lawan dari tumpukan, yang paling penting!
sumber