Dalam C ++, berapa banyak waktu yang dihabiskan programmer untuk melakukan manajemen memori

39

Orang-orang yang terbiasa dengan sampah bahasa yang dikumpulkan sering takut dengan manajemen memori C ++. Ada alat, suka auto_ptrdan shared_ptryang akan menangani banyak tugas manajemen memori untuk Anda. Banyak pustaka C ++ mendahului alat-alat itu, dan memiliki cara mereka sendiri untuk menangani tugas manajemen memori.

Berapa banyak waktu yang Anda habiskan untuk tugas manajemen memori?

Saya menduga itu sangat tergantung pada set perpustakaan yang Anda gunakan, jadi tolong katakan yang mana jawaban Anda berlaku, dan jika mereka membuatnya lebih baik atau lebih buruk.

Sean McMillan
sumber
1
Tidak terlalu banyak, kok ... Terutama dengan C ++ 0x, referensi dan STL. Anda bahkan dapat menulis kode tanpa manajemen memori sama sekali.
Coder
9
Secara umum: Tidak sebanyak itu jika Anda berpengalaman. Banyak jika Anda pemula ke C ++ (-> biasanya berburu kebocoran memori / sumber daya).
MaR
1
Saya menemukan pertanyaan sebenarnya, hari ini, lebih banyak tentang mengejar referensi basi. Dan biasanya cukup jelas setiap kali, hanya menjengkelkan bahwa itu tidak ditangkap sebelumnya: p
Matthieu M.
Saya tahu ini sudah lama, tetapi manajemen memori IMO merupakan bagian integral dari menjadi programmer yang baik. Abstraksi seperti wadah STL bagus, tetapi ketidaktahuan akan memori sangat bertentangan dengan gagasan komputasi itu sendiri. Orang mungkin juga bertanya bagaimana seseorang dapat menghilangkan manipulasi aljabar, logika, dan perulangan dari gudang programmer.
imallett
Bagaimana dengan "berapa banyak waktu yang digunakan untuk men - debug manajemen memori yang serba salah?" Per se, manajemen memori dimungkinkan dan tidak terlalu sulit di C ++. Faktanya adalah: pengaturannya adalah kerajinan yang tepat, dan sangat rentan terhadap persetan. Ketika Anda mengacau, Anda mungkin tidak menyadarinya, dan melacak kembali kesalahan lama dengan perilaku tidak menentu yang menumpuk seiring waktu, adalah waktu yang sebenarnya Anda harus takuti. Itu sebabnya non-sampah modern mengumpulkan bahasa, (Saya sedang memikirkan karat) memindahkan banyak tanggung jawab untuk memeriksa kesalahan khas ke kompiler.
ZJR

Jawaban:

54

Modern C ++ membuat Anda tidak perlu khawatir tentang manajemen memori sampai Anda harus melakukannya, yaitu sampai Anda perlu mengatur memori Anda dengan tangan, sebagian besar untuk tujuan optimasi, atau jika konteks memaksa Anda untuk melakukannya (pikirkan perangkat keras kendala besar). Saya telah menulis seluruh permainan tanpa memanipulasi memori mentah, hanya mengkhawatirkan tentang menggunakan wadah yang merupakan alat yang tepat untuk pekerjaan itu, seperti dalam bahasa apa pun.

Jadi itu tergantung pada proyek tetapi sebagian besar waktu itu bukan manajemen memori yang harus Anda tangani tetapi hanya objek seumur hidup. Itu diselesaikan dengan menggunakan smart pointer , yaitu salah satu alat C ++ idiomatik yang dihasilkan dari RAII .

Setelah Anda memahami RAII , manajemen memori tidak akan menjadi masalah.

Maka ketika Anda perlu mengakses memori mentah, Anda akan melakukannya dalam kode yang sangat spesifik, dilokalisasi dan dapat diidentifikasi, seperti dalam implementasi kumpulan objek, bukan "di mana-mana".

Di luar kode semacam ini, Anda tidak perlu memanipulasi memori, hanya objek seumur hidup.

Bagian "sulit" adalah memahami RAII.

Klaim
sumber
10
Sepenuhnya benar. Dalam 5 tahun terakhir, saya hanya menulis "hapus" ketika bekerja dengan kode lawas.
drxzcl
3
Saya bekerja di lingkungan tertanam terbatas ukuran tumpukan. Sekeren RAII, itu tidak bekerja dengan baik jika ruang stack berada pada premium. Jadi itu kembali ke pointer manajemen mikro.
bastibe
1
@nikie Saya menggunakan smart pointer perpustakaan dalam kode yang memanipulasi API mereka, maka saya menggunakan standar atau meningkatkan pointer cerdas dalam kode khusus untuk aplikasi saya (jika saya orang yang memutuskan tentang itu). Jika Anda dapat mengisolasi kode perpustakaan dalam beberapa modul yang abstrak bagaimana mereka digunakan dalam aplikasi Anda, maka Anda menghindari polusi API dari dependensi.
Klaim
12
@Paperflyer: RAII tidak akan mengambil lebih banyak ruang tumpukan daripada deletesecara manual, kecuali jika Anda memiliki satu implementasi buruk.
DeadMG
2
@Paperflyer: Penunjuk pintar pada heap mengambil ruang yang sama; perbedaannya adalah bahwa kompiler memasukkan kode alokasi sumber daya pada semua keluar dari suatu fungsi. Dan karena ini sangat banyak digunakan, ini biasanya dioptimalkan dengan baik (misalnya melipatgandakan beberapa pintu keluar dengan cara yang tidak bisa - Anda tidak dapat memasukkan kode setelah a return)
MSalters
32

Manajemen memori digunakan untuk menakuti anak-anak, tetapi hanya satu jenis sumber daya yang harus dijaga oleh seorang programmer. Pikirkan file handle, koneksi jaringan, sumber daya lain yang Anda peroleh dari OS.

Bahasa yang mendukung pengumpulan sampah biasanya tidak hanya mengabaikan keberadaan sumber daya ini, tetapi juga mempersulit penanganannya dengan tidak menyediakan destruktor.

Jadi, singkatnya, saya sarankan tidak banyak waktu pengembang C ++ dihabiskan untuk mengkhawatirkan manajemen memori. Seperti yang ditunjukkan oleh klaim , setelah Anda menguasai RAII, sisanya hanya refleks.

Disaster
sumber
3
Saya terutama suka bagaimana kebocoran HttpWebRequest.GetResponse menangani dan mulai menerjang dalam bahasa GC. GC itu keren, sampai mulai mengisap karena sumber daya masih bocor. msdn.microsoft.com/en-us/library/… Lihat "Perhatian".
Coder
6
+1 untuk melihat memori sebagai sumber. Kode lama atau tidak, berapa kali kita perlu berteriak keras: Manajemen memori adalah keterampilan dan bukan kutukan .
aquaherd
4
@Coder Tidak yakin apakah saya mengikuti .. GC menyebalkan karena mungkin saja menyalahgunakan sumber daya ..? Saya pikir C # melakukan pekerjaan yang baik dengan menyediakan pelepasan sumber daya deterministik menggunakan IDisposable ...
Maks
8
@ Max: Karena jika sampah dikumpulkan, maka saya berharap tidak perlu khawatir tentang sumber daya bodoh melalui penggunaan dan IDisposables khusus. Sumber daya meninggalkan ruang lingkup, hanya itu, mereka harus dibersihkan. Pada kenyataannya, saya masih harus berpikir dan menebak mana yang akan bocor, dan mana yang tidak. Mengalahkan alasan untuk menggunakan bahasa GC di tempat pertama.
Coder
5
@deadalnix Mereka memang memiliki finalizekonstruk. Namun, Anda tidak tahu kapan akan dipanggil. Apakah itu sebelum Anda kehabisan soket, atau objek WebResponse? Anda akan menemukan banyak artikel yang memberi tahu Anda bahwa Anda tidak boleh mengandalkan finalize- dengan alasan yang bagus.
Disaster
13

Cukup banyak. Bahkan teknologi lama seperti COM, Anda dapat menulis pengubah kustom untuk pointer Standar yang akan mengonversinya dalam waktu yang sangat singkat. Misalnya, std::unique_ptrdapat dikonversi untuk secara unik menyimpan referensi COM dengan lima baris deleter khusus. Bahkan jika Anda harus secara manual menulis handler sumber daya Anda sendiri, prevalensi pengetahuan seperti SRP dan copy-and-swap membuatnya relatif mudah untuk menulis kelas pengelola sumber daya agar dapat digunakan selamanya lebih banyak.

Kenyataannya adalah bahwa kepemilikan bersama, unik, dan tidak kepemilikan semuanya disertakan bersama kompiler C ++ 11 Anda, dan Anda hanya perlu menulis adaptor kecil untuk membuatnya bekerja bahkan dengan kode lama.

DeadMG
sumber
1
Seberapa banyak keahlian dengan C ++ yang harus Anda miliki untuk a) menulis penghapus khusus b) tahu bahwa penghapus khusus adalah yang Anda butuhkan? Saya bertanya karena tampaknya mudah untuk mengambil bahasa GC'd baru dan mendapatkan hampir-benar-benar tanpa tahu semuanya - apakah mudah untuk mendapatkan yang benar di C ++ juga?
Sean McMillan
1
@SeanMcMillan: Penghapus khusus sepele untuk menulis dan menggunakan, COM yang saya sebutkan adalah lima baris untuk semua jenis COM, dan siapa pun yang memiliki pelatihan dasar dalam C ++ modern harus terbiasa dengan mereka. Anda tidak dapat mengambil bahasa GCed, karena kejutan - GC tidak akan mengumpulkan objek COM. Atau menangani file. Atau memori yang diperoleh dari sistem lain. Atau koneksi basis data. RAII akan melakukan semua hal itu.
DeadMG
2
Dengan "Mengambil bahasa GC," Maksud saya, saya telah melompat-lompat di antara Java / C # / Ruby / Perl / Javascript / Python, dan mereka semua memiliki gaya manajemen sumber daya yang sama - memori sebagian besar otomatis, dan yang lainnya , kamu harus mengatur. Kedengarannya bagi saya seperti Anda mengatakan alat manajemen C ++ memungkinkan Anda mengelola file menangani / koneksi db / etc dengan cara yang sama seperti memori, dan itu relatif mudah setelah Anda mempelajarinya. Bukan operasi otak. Apakah saya mengerti dengan benar?
Sean McMillan
3
@SeanMcMillan: Ya, itu benar sekali, dan itu tidak rumit.
DeadMG
11

Ketika saya masih seorang programmer C ++ (lama sekali), saya menghabiskan waktu yang lama khawatir tentang bug manajemen memori ketika mencoba untuk memperbaiki mereproduksi bug .

Dengan modem C ++, manajemen memori jauh lebih sedikit dari masalah, tetapi bisakah Anda memercayai semua orang dalam tim besar untuk memperbaikinya. Berapa biaya / waktu:

  • Pelatihan (tidak banyak programmer mengerti dengan masalah yang baik)
  • Ulasan kode untuk menemukan masalah manajemen memori
  • Debugging masalah manajemen memori
  • Selalu harus diingat bahwa bug di satu bagian aplikasi, mungkin karena masalah manajemen memori di bagian yang tidak terkait dengan aplikasi .

Jadi bukan hanya menghabiskan waktu untuk " melakukan ", ini lebih merupakan masalah pada proyek-proyek besar.

Ian
sumber
2
Saya pikir beberapa proyek C ++ telah putus asa memperbaiki beberapa kebocoran memori mereka karena kode yang ditulis dengan buruk. Kode yang buruk akan terjadi, dan ketika itu terjadi, itu bisa memakan banyak waktu orang lain juga.
Jeremy
@ Jeremy, saya menemukan ketika saya pindah dari C ++ ke C #, masih ada kode yang ditulis dengan buruk (jika tidak lebih), tetapi setidaknya itu sangat mudah untuk menemukan bagian dari program yang memiliki bug yang diberikan.
Ian
1
ya ini banyak mengapa sebagian besar toko telah pindah ke Java atau .NET. Pengumpulan sampah mengurangi kerusakan kode buruk yang tidak terhindarkan.
Jeremy
1
Anehnya, kita tidak memiliki masalah itu.
David Thornley
1
@ DavidThornley, saya pikir banyak masalah berasal dari menulis kode UI di C ++, hari ini sebagian besar kode C ++ saya lihat bukan UI
Ian
2

Saya menggunakan boost dan pustaka TR1, dan mereka membuat manajemen memori dalam arti yang ketat (baru / hapus) bukan masalah. Di sisi lain, alokasi memori dalam C ++ tidak murah, dan orang harus memperhatikan di mana pointer bersama mewah ini dibuat. Anda sering menggunakan ruang kerja, atau bekerja dengan memori berbasis tumpukan. Secara umum, saya akan mengatakan bahwa sebagian besar masalah desain, bukan masalah implementasi.

quant_dev
sumber
2

Berapa lama waktu yang dibutuhkan sebagai klien? sangat sedikit, begitu Anda terbiasa. ketika sebuah wadah mengelola referensi seumur hidup, itu sangat mudah. imo, ini jauh lebih sederhana daripada penghitungan referensi manual, dan itu praktis transparan jika Anda menganggap wadah yang Anda gunakan sebagai dokumentasi yang dengan mudahnya membuat Anda tidak melakukan transfer kepemilikan yang tidak sah dalam sistem typesafe yang dirancang dengan baik.

sebagian besar waktu yang saya habiskan (sebagai klien) dihabiskan berisi jenis-jenis dari apis lain, sehingga mereka berfungsi dengan baik dalam konteks program Anda. Contoh: ini adalah wadah ThirdPartyFont saya, dan mendukung fitur ini, dan kehancuran alat cara ini, dan referensi menghitung cara ini, dan menyalin cara ini, dan ... . Banyak dari konstruksi itu harus ada, dan seringkali merupakan tempat yang logis untuk menempatkannya. apakah Anda ingin memasukkan itu sebagai waktu atau tidak tergantung pada definisi Anda (implementasi perlu ada ketika berinteraksi dengan apis ini, toh, kan?).

setelah itu, Anda perlu mempertimbangkan dan mempertimbangkan kepemilikan. dalam sistem tingkat yang lebih rendah, itu bagus dan perlu, tetapi bisa membutuhkan waktu dan perancah untuk menerapkan bagaimana Anda harus memindahkan barang-barang. saya tidak melihatnya sebagai rasa sakit karena ini adalah persyaratan sistem tingkat yang lebih rendah. kepemilikan, kontrol, dan tanggung jawab sudah jelas.

jadi kita bisa mengubahnya menjadi apis berbasis c yang menggunakan jenis buram: wadah kami memungkinkan kami untuk abstrak semua detail implementasi kecil mengelola masa hidup dan menyalin jenis-jenis buram, yang pada akhirnya membuat pengelolaan sumber daya sangat sangat sederhana dan menghemat waktu, cacat, dan mengurangi implementasi.

itu benar-benar sangat sederhana untuk menggunakan ini - masalahnya (berasal dari GC) adalah bahwa Anda sekarang harus mempertimbangkan masa hidup sumber daya Anda. jika Anda salah, mungkin perlu banyak waktu untuk menyelesaikannya. belajar dan mengintegrasikan manajemen seumur hidup yang eksplisit dapat dipahami rumit jika dibandingkan (bukan untuk semua orang) - itulah rintangan sesungguhnya. sekali Anda nyaman mengendalikan masa hidup dan menggunakan solusi yang baik, maka sangat mudah untuk mengelola masa pakai sumber daya. itu bukan bagian penting dari hari saya (kecuali bug yang sulit telah merangkak masuk).

jika Anda tidak menggunakan wadah (auto / shared pointer), maka Anda hanya memohon rasa sakit.

saya sudah mengimplementasikan perpustakaan saya sendiri. saya perlu waktu untuk mengimplementasikan hal-hal itu, tetapi kebanyakan orang menggunakan kembali (yang biasanya merupakan ide yang baik).

justin
sumber
1

Maksud Anda seperti secara manual harus membebaskan memori, menutup file, hal-hal semacam ini? Jika demikian, saya akan mengatakan minimum dan biasanya kurang dari sebagian besar bahasa lain yang saya gunakan, terutama jika kita menggeneralisasikannya tidak hanya untuk "manajemen memori" tetapi "manajemen sumber daya." Dalam hal itu, saya benar-benar berpikir C ++ memerlukan lebih sedikit manajemen sumber daya manual daripada, katakanlah Java atau C #.

Ini terutama disebabkan oleh destruktor yang secara otomatis menghancurkan sumber daya (memori atau lainnya). Biasanya satu-satunya waktu saya harus membebaskan / menghancurkan sumber daya secara manual dalam C ++ adalah jika saya menerapkan struktur data level-vlow (sesuatu yang kebanyakan orang tidak perlu lakukan) atau menggunakan C API di mana saya hanya menghabiskan sedikit waktu membungkus sumber daya C yang perlu secara manual dibebaskan / dihancurkan / ditutup menjadi pembungkus C ++ yang sesuai dengan RAII.

Tentu saja jika pengguna meminta untuk menutup gambar dalam perangkat lunak pengedit gambar, saya harus menghapus gambar dari koleksi atau sesuatu. Tapi mudah-mudahan itu tidak dianggap sebagai manajemen "memori" atau "sumber daya" yang penting dalam konteks ini, karena itu cukup banyak diperlukan dalam bahasa apa pun jika Anda ingin membebaskan memori yang terkait dengan gambar itu pada waktu itu. Tapi sekali lagi yang harus Anda lakukan adalah menghapus gambar dari koleksi dan penghancur gambar mengurus sisanya.

Sementara itu jika saya bandingkan dengan, katakanlah, Java atau C #, Anda sering menemukan orang harus menutup file secara manual di sana, mencabut soket secara manual, mengatur referensi objek ke nol untuk memungkinkan mereka menjadi sampah yang dikumpulkan, dll. Ada jauh lebih banyak memori manual dan manajemen sumber daya dalam bahasa-bahasa itu jika Anda bertanya kepada saya. Dalam C ++ Anda bahkan sering tidak perlu unlockmutex secara manual, karena loker mutex akan melakukannya untuk Anda secara otomatis ketika mutex keluar dari ruang lingkup. Misalnya, Anda tidak perlu melakukan hal-hal seperti ini di C ++:

System.IO.StreamReader file = new System.IO.StreamReader(path);
try
{
    file.ReadBlock(buffer, index, buffer.Length);
}
catch (System.IO.IOException e)
{
    ...
}
finally
{
    if (file != null)
        file.Close();
}

Tidak perlu melakukan hal-hal seperti menutup file secara manual di C ++. Mereka akhirnya menutup diri secara otomatis begitu mereka keluar dari ruang lingkup apakah mereka keluar dari ruang lingkup sebagai akibat atau jalur eksekusi normal atau luar biasa. Hal serupa untuk sumber daya terkait memori seperti std::vector. Kode seperti di file.Close()atas akan sering disukai karena, terutama dalam konteks finallyblok, yang menunjukkan sumber daya lokal perlu dibebaskan secara manual ketika seluruh pola pikir sekitar C ++ adalah untuk mengotomatisasi itu.

Dalam hal manajemen memori manual, saya akan mengatakan C membutuhkan maksimum, Java / C # jumlah sedang, dan C ++ minimum di antaranya. Ada banyak alasan untuk sedikit malu menggunakan C ++ karena ini adalah bahasa yang sangat sulit untuk dikuasai, tetapi manajemen memori tidak boleh menjadi salah satunya. Sebaliknya saya benar-benar berpikir itu adalah salah satu bahasa yang paling mudah di luar sana dalam aspek yang satu ini.

Tentu saja C ++ tidak membiarkan Anda mulai mengalokasikan memori secara manual dan memohon operator delete/delete[]untuk membebaskan memori secara manual. Ini juga memungkinkan Anda menggunakan fungsi C seperti mallocdanfree. Tapi itu semacam praktik pengkodean gaya kuno yang menurut saya sudah usang jauh sebelum orang-orang memberi penghargaan, karena Stroustrup menganjurkan RAII sebelum dia bahkan menciptakan istilah itu sejak awal. Jadi saya bahkan tidak berpikir itu adil untuk mengatakan "C ++ modern" mengotomatisasi manajemen sumber daya, karena itu seharusnya menjadi tujuan selama ini. Anda tidak bisa mendapatkan keselamatan pengecualian jika tidak. Hanya saja, banyak pengembang yang salah kaprah selama awal 90-an mencoba menggunakan C ++ seperti halnya C dengan objek, seringkali mengabaikan penanganan pengecualian, dan tidak pernah seharusnya digunakan seperti itu. Jika Anda menggunakan C ++ dengan cara yang praktis selalu dimaksudkan untuk digunakan, maka manajemen memori benar-benar otomatis dan umumnya bukan sesuatu yang harus Anda tangani secara manual (atau harus berurusan dengan) sama sekali.


sumber
1
Java modern memiliki "coba dengan sumber daya" yang menghapus semua kode yang berantakan di blok akhirnya. Jarang diperlukan untuk memiliki blok akhirnya. Sepertinya para desainer telah menyalin konsep RAII.
kiwiron
0

Tergantung pada petunjuk teknis senior dalam tim. Di beberapa perusahaan (termasuk tambang), tidak ada konsep yang disebut smart poiner. Itu dianggap mewah. Jadi, orang cukup menghapus semua tempat dan ada drive untuk memperbaiki kebocoran memori setiap 2 bulan. Gelombang baru pernyataan penghapusan tiba di mana-mana. Jadi, tergantung pada perusahaan dan jenis orang yang bekerja di sana.

Jagannath
sumber
1
Adakah sesuatu di lingkungan Anda yang menghentikan Anda untuk menggunakan auto_ptrdan berteman?
Sean McMillan
2
Sepertinya perusahaan Anda tidak menulis kode C ++, Anda sedang menulis C.
gbjbaanb