C ++ 'da hafıza yönetimi için ne kadar zamanlayıcı harcanır?


39

Toplanan dilleri çöpe atmak için kullanılan insanlar genellikle C ++ 'ın hafıza yönetiminden korkarlar. Gibi auto_ptrve shared_ptrsizin için birçok bellek yönetimi görevini yerine getirebilecek araçlar var. Pek çok C ++ kütüphanesi bu araçları seçer ve bellek yönetimi görevlerini yerine getirmek için kendi yöntemlerine sahiptir.

Bellek yönetimi görevlerine ne kadar zaman harcıyorsunuz?

Kullandığınız kütüphane kümesine oldukça bağlı olduğundan şüpheliyim, bu nedenle lütfen cevabınızın hangilerine uygulandığını ve daha iyi ya da daha kötü duruma getirdiklerini söyleyin.


1
Çok değil, gerçekten ... Özellikle C ++ 0x ile, referanslar ve STL. Hiç bellek yönetimi olmadan kod bile yazabilirsiniz.
Coder

9
Genel olarak: Eğer deneyimli değilseniz o kadar da değil. C ++ 'ya yeni başlayanlar için çok - (-> genellikle av belleği / kaynak sızıntıları).
MaR

1
Asıl soru, bugünlerde bayat referansları kovalamakla ilgili. Ve genellikle her seferinde oldukça belirgindir, daha önce yakalanmamış olması can sıkıcıdır: p
Matthieu M.

Bunun eski olduğunu biliyorum, ancak IMO bellek yönetimi iyi bir programcı olmanın ayrılmaz bir parçası. STL kapları gibi soyutlamalar güzeldir, ancak hafızanın cehaleti, hesaplamanın kendisinin fikrine karşıdır. Biri cebirsel manipülasyonu, mantığı ve döngüleyiciyi programcının cephaneliğinden nasıl uzaklaştıracağını da sorabilir.
imallett

“ Hafıza yönetiminin ters gittiğinde hata ayıklamak için ne kadar zaman kullanılıyor ?” Kendi başına, C + 'da bellek yönetimi mümkün ve zor değil. Gerçek şu ki: kurarken kesin bir zanaat ve sikikleri çok eğilimli. Batırdığın zaman, farkına bile varmayabilirsin ve zamanla biriken düzensiz davranışlarla eski hataları tekrar takip etmek, korkman gereken gerçek zaman havuzudur. Bu nedenle modern olmayan çöp toplanan dilleri (paslanmayı düşünüyorum) derleyiciye tipik hataları kontrol etmek için büyük bir sorumluluk taşıyordu.
ZJR

Yanıtlar:


54

Modern C ++, gerekmedikçe bellek yönetimi konusunda endişelenmenize gerek kalmaz; bu, belleğinizi el ile, çoğunlukla optimizasyon amacıyla veya içerik sizi yapmaya zorlarsa (büyük kısıtlamaları donanım olarak düşünün) düzenlemeniz gerekene kadar sizi endişelendirmez. Tüm oyunları ham belleği değiştirmeden yazdım, sadece herhangi bir dilde olduğu gibi iş için doğru araç olan kapları kullanmaktan endişe duydum.

Bu yüzden projeye bağlı fakat çoğu zaman kullanmanız gereken hafıza yönetimi değil, yalnızca yaşam boyu itiraz ediyor. Bu akıllı işaretçiler kullanılarak çözülür , yani RAII kaynaklı deyimsel C ++ aracından biridir .

RAII'yi bir kez anladıysanız , hafıza yönetimi bir sorun olmayacaktır.

Ardından, ham belleğe erişmeniz gerektiğinde, bunu "her yerde" değil, havuz nesne uygulamalarında olduğu gibi çok özel, yerel ve tanımlanabilir bir kodda yaparsınız.

Bu tür bir kodun dışında, hafızayı değiştirmek zorunda kalmazsınız, sadece ömür boyu nesneler.

"Zor" kısım RAII'yi anlamaktır.


10
Kesinlikle doğru. Son 5 yılda, eski kodla çalışırken yalnızca "delete" lar yazdım.
drxzcl

3
Yığın boyutunda kısıtlı bir gömülü ortamda çalışıyorum. RAII kadar soğuk olursa, yığın alanı premium ise, iyi çalışmaz. İşaretçi mikroişlemine geri döndü.
bastibe

1
@nikie Kütüphaneleri kendi API'lerini yöneten koddaki akıllı işaretçileri kullanıyorum, daha sonra standart uygulamamı kullanıyorum ve uygulamama özel koddaki akıllı işaretçileri artırıyorum (buna karar veren kişi benim ise). Kütüphane kodunu uygulamanızda nasıl kullanıldığını özetleyen bazı modüllerde izole edebilirseniz, bağımlılıklardan API kirlenmesini önlersiniz.
Klaim

12
@Paperflyer: RAII, deletetek bir boktan uygulama yapmadığınız sürece, el ile olduğundan daha fazla yığın alanı kullanmaz .
DeadMG

2
@Paperflyer: Yığındaki akıllı işaretçi aynı boşluğu alır; Aradaki fark, derleyicinin bir fonksiyondan tüm çıkışlara kaynak ayırma kodunu eklemesidir. Ve bu çok yaygın bir şekilde kullanıldığından, bu genellikle iyi bir şekilde optimize edilmiştir (örneğin, birden fazla çıkışın return
katlanamayacağı

32

Hafıza yönetimi çocukları korkutmak için kullanılır, ancak bir programcının bakması gereken tek tür kaynaktır. Dosya tanıtıcılarını, ağ bağlantılarını ve işletim sisteminden edindiğiniz diğer kaynakları düşünün.

Çöp toplanmayı destekleyen diller genellikle bu kaynakların varlığını göz ardı etmekle kalmaz, aynı zamanda bir yıkıcı sağlayarak bunları düzgün bir şekilde ele almayı zorlaştırır.

Bu yüzden, kısacası, C ++ geliştiricisinin zamanının çoğunun bellek yönetimi konusunda endişelenerek geçirilmediğini öneririm. Klaim'in cevabının belirttiği gibi, RAII'yi ele geçirdiğinizde geri kalan sadece refleks olur.


3
Ben özellikle HttpWebRequest.GetResponse sızıntısının nasıl işlediğini ve GC dillerinde çökmeye başladığını çok seviyorum. GC, kaynaklar hala sızıntı olduğu için emmeye başlayana kadar serindir. msdn.microsoft.com/en-us/library/… "Dikkat" bölümüne bakın.
Coder

6
Kaynak olarak belleği görüntülemek için +1. Eski kod ya da değil, kaç kere yüksek sesle bağırmamız gerekiyor: Hafıza yönetimi bir lanet değil bir beceridir .
aquaherd

4
@Coder Takip edip etmediğimden emin değilim .. GC zaten kaynakları kötüye kullanmak mümkün olduğu için GC berbat ..? C #, IDSposable'ı kullanarak deterministic kaynak salımı sağlayan iyi bir iş çıkarıyor ...
Max

8
@Max: Çöp toplanırsa, o zaman aptal kaynaklar hakkında endişelenmemeyi ve özel IDisposables kullanmamayı umuyorum. Kaynaklar kapsamı terk etti, hepsi bu, temizlenmesi gerekiyor. Gerçekte gerçekte hangilerinin sızacağını, hangilerinin olmayacağını düşünmem ve tahmin etmem gerekiyor. GC dilini ilk etapta kullanmak için herhangi bir neden atıyor.
Coder

5
@deadalnix Yapıları var finalize. Ancak ne zaman aranacağını bilmiyorsunuz. Prizleri veya WebResponse nesnelerini bitmeden önce mi olacak? Güvenmeniz gerektiğini söyleyen makaleleri bolca bulacaksınız finalize- iyi bir sebeple.
Dysaster

13

Neredeyse hiçbiri. COM gibi eski teknolojiler bile, çok kısa sürede dönüştürecek olan Standart işaretçilere özel siliciler yazabilirsiniz. Örneğin, std::unique_ptrözel bir silginin beş satırıyla birlikte bir COM referansını benzersiz şekilde tutmak için dönüştürülebilir. Kendi kaynak işleyicinizi elle yazmak zorunda olsanız bile, SRP ve kopyala ve değiştir gibi bilgilerin yaygınlığı, sonsuza dek kullanmak üzere bir kaynak yönetimi sınıfı yazmayı nispeten kolaylaştırır.

Gerçek şu ki, paylaşılan, benzersiz ve sahip olmamak tüm C ++ 11 derleyicinizle birlikte gelir ve eski kodlarla bile çalışabilmesi için küçük adaptörleri yazmanız gerekir.


1
C ++ ile ne kadar beceriye sahip olmalısınız, a) özel bir silgi yazmak b) özel bir sileceğin ihtiyacınız olanı olduğunu biliyor musunuz? Soruyorum, çünkü yeni bir GC'd dili seçmek ve her şeyi bilmeden doğruya yaklaşmak kolay gibi görünüyor - C ++ 'da tam anlamıyla kolay anlaşılıyor mu?
Sean McMillan

1
@SeanMcMillan: Özel siliciler yazmak ve dağıtmak için çok önemlidir, bahsettiğim COM tüm COM türleri için beş satırdır ve modern C ++ 'da temel bir eğitim alan herkes bu konuya aşina olmalıdır. GCed dilini seçemezsiniz, çünkü sürpriz - GC, COM nesnelerini toplamıyor. Veya dosya tanıtıcısı. Veya diğer sistemlerden elde edilen hafıza. Veya veritabanı bağlantıları. RAII bütün bunları yapacak.
DeadMG

2
"Bir GC'd dili seçin" derken, Java / C # / Ruby / Perl / Javascript / Python arasında atladığımı kastettim ve hepsinin aynı kaynak yönetimi stiline sahip olduğunu söyledim - bellek çoğunlukla otomatik ve her şey , yönetmek zorundasın. Bana C ++ 'ın yönetim araçlarının, dosya tanıtıcılarını / db bağlantılarını / etc'leri hafıza ile aynı şekilde yönetmenize izin verdiğini ve öğrendikten sonra göreceli olarak basit olduğunu söylediğiniz gibi geliyor. Beyin ameliyatı değil. Doğru anlıyor muyum
Sean McMillan,

3
@SeanMcMillan: Evet, kesinlikle doğru ve karmaşık değil.
DeadMG

11

Bir C ++ programcısıyken (uzun zaman önce), hataları çoğaltmak zorlaşmaya çalışırken hafıza yönetimi konusunda endişelerim vardı .

Modem C ++ ile bellek yönetimi daha az sorun çıkarsa da, büyük bir ekipteki herkese doğru olanı yapabilir misiniz? Maliyeti / zamanı nedir:

  • Eğitim (pek çok programcı meseleleri iyi anlayamıyor)
  • Bellek yönetimi sorunlarını bulmak için kod incelemeleri
  • Bellek yönetimi sorunlarının hata ayıklaması
  • Uygulamanın bir kısmındaki bir hatanın, uygulamanın ilgisiz bir kısmındaki hafıza yönetimi sorunlarından kaynaklanabileceğini her zaman aklınızda bulundurmanız gerekir .

Sadece zaman “harcama yapmıyor Yani yapıyor ”, bu daha büyük projelerde bir sorun olduğunu.


2
Bazı C ++ projelerinin kötü yazılmış kodlar nedeniyle bazı bellek sızıntılarını gidermeyi umutsuz kıldığını düşünüyorum. Kötü kod olacak ve bu olduğunda diğer insanların zamanını da alabilir.
Jeremy

@ Jeremy, C ++ 'dan C #' ya taşındığımda, hala çok kötü yazılmış kodların (daha fazla değilse) olduğunu gördüm, ama en azından programın belirli bir hata yapmış olduğu kısmı bulmak çok kolaydı.
Ian

1
evet bu çoğu mağazanın Java veya .NET'e taşınmasının nedenlerinden biri. Çöp toplama kötü kodun kaçınılmaz zararını azaltır.
Jeremy

1
İşin garibi, o problemlerimiz yok.
David Thornley

1
@DavidThornley, sorunun birçoğunun C ++ 'da UI kodu yazmaktan kaynaklandığını düşünüyorum, bugünlerde en çok gördüğüm C ++ kodu UI değil
Ian

2

Boost ve TR1 kitaplıklarını çok kullanıyorum ve bellek yönetimini kesin anlamda (yeni / silme) bir sorun oluşturuyorlar. Öte yandan, C ++ 'da hafıza tahsisi ucuz değildir ve bu ortak paylaşılan işaretçilerin nerede yaratıldığına dikkat etmek gerekir. Çalışma alanlarını çok kullanarak ya da yığın tabanlı bellekle çalışarak bitiyorsunuz. Genelde bunun çoğunlukla bir tasarım sorunu olduğunu, uygulama sorunu olmadığını söyleyebilirim.


2

bir müşteri olarak ne kadar zaman alır? çok az, bir kez asmak olsun. Bir konteyner ömrünü ve referansları yönettiğinde, gerçekten çok kolaydır. imo, manuel referans saymaktan çok daha basittir ve kullandığınız kabı, derleyicinin iyi tasarlanmış bir yazı tipi sisteminde geçersiz sahiplik transferleri yapmanıza elverişli şekilde engellediği bir belge olarak kabul ederseniz pratik olarak şeffaftır.

harcadığım zamanın çoğunu (bir müşteri olarak) başka apilerden türler içerek geçirdiğim için harcıyorsunuz, bu yüzden programlarınız bağlamında iyi çalışıyorlar. örnek: ... Bu benim ThirdPartyFont konteyner olduğunu ve bu özellikleri destekler ve uygular imha bu şekilde ve referans bu şekilde sayma ve bu şekilde kopyalama ve . Bu yapıların birçoğunun yerinde olması gerekir ve genellikle bunları koymak için mantıklı bir yerdir. Bunu zamana dahil etmek veya eklememek isteyip istemediğinizin tanımınıza bağlı olup olmadığı (bu apislerle ne zaman olursa olsun uygulamanın var olması gerekir, değil mi?).

Bundan sonra, hafızayı ve mülkiyeti dikkate almanız gerekecektir. Daha düşük seviyeli bir sistemde, bu iyi ve gerekli, ancak bazı şeyleri nasıl hareket ettirmeniz gerektiğini uygulamak için biraz zaman ve iskele gerekebilir. Bunu bir acı olarak görmüyorum, çünkü bu daha düşük seviyeli bir sistemin bir gereğidir. mülkiyet, kontrol ve sorumluluk açıktır.

Böylece, opak tip kullanan c-temelli apilere dönüştürebiliriz: konteynerlerimiz, ömrünü yönetmek ve sonunda kaynak yönetimini çok basit hale getiren ve zamandan, kusurlardan tasarruf eden bu opak tiplerin kopyalanmasının tüm küçük uygulama detaylarını soyutlamamıza izin veriyor. ve uygulamaları azaltır.

Bunları kullanmak gerçekten çok basit - sorun (GC'den gelen) şimdi kaynaklarınızın ömrünü düşünmek zorunda olmanızdır. Eğer yanlış yaparsan, çözmesi çok zaman alabilir. Açık yaşam boyu yönetimi öğrenmek ve entegre etmek karşılaştırmalı olarak anlaşılır şekilde karmaşıktır (tüm insanlar için değil) - asıl engel budur. yaşamları kontrol etme ve iyi çözümler kullanma konusunda rahat olduğunuzda, kaynak yaşamını yönetmek gerçekten çok kolaydır. günümün önemli bir parçası değil (zor bir böceğin çarpmadığı sürece).

kaplar (otomatik / paylaşılan işaretçi) kullanmıyorsanız, acı çekmek için yalvarırsınız.

kendi kütüphanelerimi uyguladım. Tek gereken bana o şeyleri uygulamak için zaman, ama insanların çoğu (genellikle iyi bir fikirdir) yeniden kullanın.


1

Manuel olarak boş hafıza açmak, dosyaları kapatmak, bu tür şeyler mi demek istiyorsunuz? Öyleyse, özellikle sadece "bellek yönetimi" için değil, "kaynak yönetimi" için de genellersek, kullandığım asgari ve tipik olarak diğer dillerden daha az olduğunu söyleyebilirim. Bu anlamda, aslında C ++ 'ın Java ya da C #' dan daha az manuel kaynak yönetimi gerektirdiğini düşünüyorum.

Bu esas olarak kaynağı yok etmeyi otomatikleştiren yıkıcılar (bellek veya başka bir şey) nedeniyle. Genellikle, bir kaynağı C ++ ile manuel olarak serbest bırakmak / imha etmek zorunda olduğum tek zaman, düşük seviyeli bir veri yapısı (çoğu insanın yapması gerekmeyen bir şey) uyguluyor olsam veya biraz zaman harcadığım bir C API kullanıyorsamdır. manuel olarak serbest bırakılması / tahrip edilmesi / kapatılması gereken C kaynağının bir RAII uyumlu C ++ ambalajına sarılması.

Tabii ki, bir kullanıcı bir görüntü düzenleme yazılımındaki bir görüntüyü kapatmak isterse, görüntüyü bir koleksiyondan veya başka bir şeyden kaldırmam gerekir. Ancak umarım bu bağlamda önemli olan bir türün "bellek" veya "kaynak" yönetimi olarak sayılmaz, çünkü o görüntüyle ilgili belleği serbest bırakmak istiyorsanız, herhangi bir dilde bu çok gerekli. Fakat yine de tek yapmanız gereken, görüntüyü koleksiyondan kaldırmak ve görüntü yıkıcısı gerisini halleder.

Bu arada, örneğin Java veya C # ile karşılaştırırsam, genellikle dosyaları el ile kapatmaları, manüel olarak soketleri çıkarmaları, nesnelerin toplanmasına izin vermek için nesne referanslarını null değerine getirmeleri vb. Bana sorarsanız, bu dillerde kaynak yönetimi. C ++ 'da, genellikle unlockbir muteks bile gerekmez , çünkü muteks dolabı, muteks kapsam dışında kaldığında sizin için otomatik olarak yapar. Örneğin, C ++ 'ta asla böyle şeyler yapmamalısınız:

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();
}

C ++ 'da dosyaları elle kapatma gibi şeyler yapmaya gerek yoktur. Sonuç olarak kapsam dışı kaldıklarında mı yoksa normal veya istisnai uygulama yollarında mı, kapsam dışı kaldıklarında kendilerini otomatik olarak kapatırlar. Bellek ile ilgili kaynaklar için benzer bir şey std::vector. file.Close()Yukarıdaki gibi bir kod , çoğunlukla, özellikle bir finallyblok bağlamında, C ++ etrafındaki bütün zihniyetin bunu otomatikleştirmesi gerektiğinde, yerel kaynağın manuel olarak serbest bırakılması gerektiğini öne sürdüğü için kaşlarını çattıracaktı .

El ile bellek yönetimi açısından, C'nin en çok, Java / C # a miktarını ve C ++ 'ın bunlardan en azını gerektirdiğini söyleyebilirim. C ++ kullanmak konusunda biraz utangaç olmanın birçok nedeni vardır, çünkü ustalaşması çok zor bir dildir, ancak bellek yönetimi bunlardan biri olmamalıdır. Aksine, aslında bunun bir yönüyle en kolay dillerden biri olduğunu düşünüyorum.

Tabii ki C ++ hafızayı elle ayırmaya ve operator delete/delete[]elle boş hafızaya çağırmaya başlamanıza izin verir . Aynı zamanda mallocve gibi C işlevlerini kullanmanıza izin verir.free. Fakat bu, Stroustrup, terimi daha erken bir dönem baştan sona koymadan önce, RAII'yi savunan biri olduğundan, insanlar kredi vermeden çok eski hale geldiğini düşündüğüm eski tip kodlama uygulamaları. Bu yüzden "modern C ++" 'nın kaynak yönetimini otomatikleştirdiğini söylemenin adil olacağını sanmıyorum, çünkü bunun amaçtan geldiği sanılıyordu. Aksi takdirde pratikte istisna güvenlik elde edemezsiniz. 90'lı yılların başlarında pek çok yanlış yönlendirilmiş geliştiricinin, C ++ 'ı nesnelerle C gibi kullanmaya çalıştıkları, genellikle istisna işlemeyi tamamen görmezden geldikleri ve asla bu şekilde kullanılmaması gerekiyordu. C ++ 'ı pratikte her zaman kullanılması gerektiği gibi kullanırsanız, bellek yönetimi tamamen otomatiktir ve genellikle el ile uğraşmanız gereken (veya başa çıkmanız gereken) bir şey değildir.


1
Modern Java, tüm bu karışık kodları nihayet blokta kaldıran "kaynakları deneyin". Sonunda bir blok olması nadiren gerekli değildir. Tasarımcılar RAII konseptini kopyalamış gibi görünüyor.
kiviron

0

Takımdaki kıdemli teknik liderlere bağlı. Bazı şirketlerde (benimki dahil) smart poiner adı verilen bir kavram yoktur. Süslü olarak kabul edilir. Böylece insanlar sadece her yere silerler ve her 2 ayda bir bellek sızıntısı tespiti için bir sürücü vardır. Her yerde yeni silme ifadeleri dalgası geliyor. Yani, şirkete ve orada çalışan insanların türüne bağlı.


1
Çevrenizde sizi auto_ptrve arkadaşlarınızı kullanmanızı engelleyen bir şey var mı ?
Sean McMillan

2
şirketiniz C ++ kodu yazmıyor gibi geliyor, siz C yazıyorsunuz
gbjbaanb
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.