Linus Torvalds tarafından bu ünlü rant okuduktan sonra, aslında C++ programcılar için tüm tuzaklar olduğunu merak ettim. Açıkça bu soru ve cevapları olarak ele alınan yazım hataları veya kötü program akışından bahsetmiyorum, ancak derleyici tarafından algılanmayan ve açık hatalarla sonuçlanmayan daha yüksek düzey hatalara ilk çalıştırma, tam tasarım hataları, C'de mümkün olmayan ancak kodlarının tam anlamını anlamayan yeni başlayanlar tarafından C++ 'da yapılması muhtemel şeyler.
Ayrıca, genellikle beklenmedik yerlerde büyük bir performans düşüşüne işaret eden cevapları da memnuniyetle karşılıyoruz. Profesörlerimden birinin bana bir kez yazdığım bir LR (1) ayrıştırıcı jeneratörü hakkında ne söylediğine bir örnek:
Gereksiz miras ve sanallığın çok fazla örneğini kullandınız. Kalıtım bir tasarımı çok daha karmaşık hale getirir (ve RTTI (çalışma zamanı tipi çıkarım) alt sistemi nedeniyle verimsizdir) ve bu nedenle yalnızca mantıklı olduğu yerlerde kullanılmalıdır, örn. ayrıştırma tablosundaki eylemler için. Şablonları yoğun bir şekilde kullandığınız için, pratik olarak mirasa ihtiyacınız yok. "
Torvalds burada kıçından konuşuyor.
Tamam, neden kıçından konuşuyor:
Her şeyden önce, onun rant gerçekten rant hiçbir şey değildir. Burada çok az gerçek içerik var. Gerçekten ünlü veya hatta hafif saygı görmesinin tek nedeni, Linux Tanrısı tarafından yapılmış olmasıdır. Ana argümanı C++ bok ve C++ insanları kızdırmak seviyor. Elbette buna cevap vermek için hiçbir sebep yok ve bunu makul bir argüman olarak gören herkes zaten konuşmanın ötesinde.
En objektif noktaları olarak neyin ışıltılabileceğine gelince:
Temel olarak, Torvalds kıçından konuşuyor. Hiçbir şey hakkında anlaşılır bir tartışma yapılmaz. Böyle saçmalıkların ciddi bir çürümesini beklemek sadece aptalca. Söylediğim yerde genişlemem beklenen bir şeyin çürütülmesine "genişlemem" söylendi. Gerçekten, dürüstçe Torvalds'ın aslında hiçbir şey söylemediğini gördüğünü söylediğinize bakın.
Tanrı'nın bunun mantıklı olduğu anlamına gelmediği veya bazı rastgele bozoların söylediklerinden daha ciddiye alınması gerektiği anlamına gelmediğinden. Gerçeği söylemek gerekirse, Tanrı sadece başka bir rastgele bozo.
Asıl soruya cevap:
Muhtemelen en kötü ve en yaygın, kötü C++ uygulaması C gibi davranmaktır. Printf, gets (C'de kötü olarak da kabul edilir), strtok gibi C API işlevlerinin sürekli kullanımı ... sadece sağlanan güçten yararlanamıyor daha sıkı tip sistem tarafından, "gerçek" C++ kodu ile etkileşim kurmaya çalışırken kaçınılmaz olarak daha fazla komplikasyona yol açarlar. Temel olarak, Torvalds'ın önerdiklerinin tam tersini yapın.
Daha fazla derleme zamanı tespiti elde etmek ve hayatınızı diğer genel yollarla kolaylaştırmak için STL ve Boost'tan yararlanmayı öğrenin (örneğin, güçlendirici belirteç hem tip güvenli hem de daha iyi bir arayüzdür). İlk başta göz korkutucu olan şablon hatalarının nasıl okunacağını öğrenmeniz gerektiği doğrudur, ancak (yine de benim deneyimime göre), çalışma zamanı sırasında tanımlanmamış davranış üreten bir şeyi hata ayıklamaya çalışmaktan çok daha kolaydır. yapmak oldukça kolay.
C'nin o kadar iyi olmadığını söylememek. Elbette C++ gibi daha iyi. C gibi C programcıları daha iyi. Oyunda takaslar ve öznel beğeniler var. Ayrıca etrafında çok fazla yanlış bilgi ve FUD var. C++ hakkında daha fazla FUD ve yanlış bilgi olduğunu söyleyebilirim, ancak bu konuda önyargılıyım. Örneğin, "şişkinlik" ve "performans" sorunları C++ 'ın sözde çoğu zaman büyük sorunlar değildir ve kesinlikle gerçekliğin oranlarından dışarı atılır.
Profesörünüzün bahsettiği konulara gelince, bunlar C++ 'a özgü değildir. OOP (ve genel programlamada) kompozisyonu kalıtım yerine tercih etmek istersiniz. Kalıtım, tüm OO dillerinde var olan olası en güçlü bağlantı ilişkisidir. C++ ekler daha güçlü, dostluk, soyutlama ve "is-a" ilişkilerini temsil etmek için polimorfik kalıtım kullanılmalı, asla yeniden kullanım için kullanılmamalıdır.Bu, C++ 'da yapabileceğiniz ikinci en büyük hatadır ve oldukça büyük bir hatadır , ancak dile özgü olmaktan çok uzaktır. C # veya Java da çok karmaşık miras ilişkileri oluşturabilirsiniz ve tam olarak aynı sorunlara sahip olacaklar).
C++ 'ın tehlikelerinin her zaman tecrübesiz, C with Classes programcıları tarafından abartılı olduğunu düşündüm.
Evet, C++ 'ı Java gibi bir şeyden almak daha zordur, ancak modern teknikleri kullanarak program yaparsanız, sağlam programlar yazmak oldukça kolaydır. Dürüst olmak gerekirse C++ 'da Java gibi dillerde yaptığımdan çok daha zor bir programım yok ve sık sık kendimi bazı C++ eksik buluyorum Diğer dillerde tasarım yaparken şablonlar ve RAII gibi soyutlamalar.
Bununla birlikte, C++ 'da yıllarca programlama yaptıktan sonra bile, her seferinde daha üst düzey bir dilde mümkün olmayacak gerçekten aptalca bir hata yapacağım. C++ 'daki yaygın bir tuzak, nesne ömrünü yok saymaktır: Java ve C #' da genellikle nesne ömrü ile ilgilenmek zorunda değilsiniz *, çünkü tüm nesneler yığın üzerinde bulunur ve sizin için yönetilir sihirli bir çöp toplayıcı tarafından.
Şimdi, modern C++ 'da, genellikle nesnenin yaşam süresi hakkında da fazla ilgilenmeniz gerekmez. Sizin için nesnelerin ömrünü yöneten yıkıcılar ve akıllı işaretçiler var. Zamanın% 99'u, bu harika çalışıyor. Ama her zaman ve sonra, sarkan bir işaretçi (veya referans) ile vidalanacaksınız. Örneğin, son zamanlarda başka bir nesneye dahili bir referans değişkeni içeren bir nesne (diyelim
Foo) diyelim
Bar). Bir noktada,
Bar'ın
Foo'den önce kapsam dışına çıkması için aptalca şeyler ayarladım, ancak
Foo 's destructor
Bar işlevini çağırdı. Söylemeye gerek yok, işler iyi sonuç vermedi.
Şimdi, bunun için gerçekten C++ suçlayamam. Bu benim kendi kötü tasarımımdı, ama asıl mesele, bu tür bir şeyin daha üst düzey, yönetilen bir dilde gerçekleşmeyeceği. Akıllı işaretçiler ve benzerlerinde bile, bazen nesnenin yaşam süresi hakkında bir farkındalığa sahip olmanız gerekir.
* Eğer yönetilen kaynak hafıza ise, yani.
try/catch Blokun aşırı kullanımı.
File file("some.txt"); try { /**/ file.close(); } catch(std::exception const& e) { file.close(); }
Bu genellikle Java gibi dillerden kaynaklanır ve insanlar C++ 'nın
finalize yantümcesinden yoksun olduğunu iddia edecektir.
Ancak bu kod iki sorun gösterir:
try/catchÖncesinde
fileoluşturulması gerekir, çünkü
closeiçinde bulunmayan bir dosyayı
catchyapamazsınız. Bu, kapatıldıktan sonra
filegörünür olduğu için bir "kapsam sızıntısına" yol açar. Bir blok ekleyebilirsiniz, ancak ...: /
returnkapsamının ortasına bir
tryeklerse, dosya kapatılmaz (bu yüzden insanlar
finalizeyan tümcesinin eksikliği hakkında orospu)
Ancak, C++ ile, bu sorunu ele almanın çok daha etkili yollarına sahibiz:
finalize
using
defer
Gerçekten ilginç mülkleri en iyi
SBRM (Kapsamlı Sınırlı Kaynaklar Yönetimi) olarak özetlenen RAII'miz var.
Sınıfı, yıkıcısının sahip olduğu kaynakları temizleyecek şekilde hazırlayarak, kaynağı her bir kullanıcı için yönetme sorumluluğunu üstlenmeyiz!
Bu the başka bir dilde özledim ve muhtemelen en unutulmuş olan özelliktir.
Gerçek şu ki, nadiren giriş yapmadan sonlandırmayı önlemek için en üst düzeyde C++ 'da bir
try/catch Bloğu yazma ihtiyacı vardır.
Koddaki fark genellikle programcı ile dilden daha fazla ilgilidir. Özellikle, iyi bir C++ programcısı ve bir C programcısı benzer şekilde iyi (farklı olsalar bile) çözümlere gelecektir. Şimdi, C daha basit bir dildir (dil olarak) ve bu, kodun gerçekte ne yaptığına dair daha az soyutlama ve daha fazla görünürlük olduğu anlamına gelir.
Onun rantının bir kısmı (C++ 'a karşı rantlarıyla tanınır), daha fazla insanın C++' a girmesine ve bazı soyutlamaların neyi gizlediğini ve yanlış varsayımlar yaptığını anlamadan kod yazmasına dayanır.
Ölçütlerinize uyan yaygın bir hata, sınıfınızdaki ayrılan bellekle uğraşırken kopya oluşturucuların nasıl çalıştığını anlamak değildir. Bir 'çaylak' nesnelerini bir haritaya veya vektöre koyduğu ve kopya oluşturucuları ve yıkıcıları düzgün bir şekilde yazmadığı için çökmeleri veya bellek sızıntılarını düzeltmek için harcadığım süreyi kaybettim.
Ne yazık ki C++ böyle 'gizli' gotchas ile dolu. Ama şikayet etmek, Fransa'ya gittiğinizden ve insanların ne söylediğini anlayamadığınızdan şikayet etmek gibidir. Oraya gidecekseniz dili öğrenin.
C++ çok çeşitli özellikler ve programlama stilleri sağlar, ancak bu aslında C++ 'nin kullanılması için iyi yollar olduğu anlamına gelmez. Ve aslında, C++ 'ı yanlış kullanmak inanılmaz derecede kolaydır.
düzgün bir şekilde öğrenilmeli ve anlaşılmalıdır , sadece yaparak (veya başka bir dili kullanır gibi kullanmak), verimsiz ve hataya açık bir koda yol açacaktır.
Peki ... Yeni başlayanlar için C++ FAQ Lite
Daha sonra, birkaç kişi C++'ın incelikleri hakkında kitaplar yazarak kariyer yaptı:
Torvalds'ın rant eksik maddesine gelince ... cidden insanlara gel, cidden: Orada başka hiçbir dilde, dilin nüansları ile uğraşmak için çok fazla mürekkep dökülmedi. Python & Ruby & Java kitaplarınızın tümü uygulama yazmaya odaklanıyor ... C++ kitaplarınız aptal dil özelliklerine odaklanıyor)/ipuçları/tuzaklar.
Çok ağır şablonlama ilk başta hatalara yol açmayabilir. Zaman geçtikçe, insanların bu kodu değiştirmesi gerekecek ve büyük bir şablonu anlamakta zorlanacaklar. İşte o zaman hatalar girer - yanlış anlama "derler ve çalışır" yorumlarına yol açar, bu da çoğu zaman neredeyse doğru olmayan bir koda yol açar.
Genellikle kendimi üç seviyeli bir genel şablon yaptığını görürsem, durup nasıl azaltılacağını düşünüyorum. Genellikle problem fonksiyonlar veya sınıflar çıkartılarak çözülür.
Uyarı: Bu, cevabında "kullanıcı bilinmeyeninin" bağlandığı konuşmanın eleştirisi kadar bir cevap değildir.
İlk ana noktası (varsayılan) "sürekli değişen standart" tır. Gerçekte, verdiği tüm örnekler C++ önce bir standart vardı. 1998'den beri (ilk C++ standardı sonlandırıldığında) dilde değişiklikler oldukça azdır - aslında, çoğu gerçek sorunun daha fazla değişiklik yapılması gerektiğini savunur. Orijinal C++ standardıyla uyumlu tüm kodların hala geçerli standarda uygun olduğundan eminim. biraz daha az kesin olsa da, bir şey hızlı bir şekilde değişmedikçe (ve oldukça beklenmedik bir şekilde) aynı yaklaşan C++ standardında da oldukça doğru olacaktır (teorik olarak, kullanılan tüm kod
export kırılacak, ama neredeyse hiçbiri mevcut değil; pratik açıdan bu bir sorun değil). Bu tür herhangi bir iddiada bulunabilecek birkaç başka dil, işletim sistemi (veya bilgisayarla ilgili başka bir şey) düşünebilirim.
Daha sonra "sürekli değişen stiller" e girer. Yine, noktalarının çoğu saçmalıklara oldukça yakın.
for (int i=0; i<n;i++)'i "eski ve bozuk" ve
for (int i(0); i!=n;++i) "yeni sıcaklık" olarak karakterize etmeye çalışır. Gerçek şu ki, bu tür değişikliklerin mantıklı olabileceği tipler olsa da,
int için fark etmez - ve bir şey kazanabildiğinizde bile, iyi veya doğru kod yazmak için nadiren gereklidir. En iyi durumda bile, bir köstebek tepesinden bir dağ yapıyor.
Bir sonraki iddiası, C++ 'nın "yanlış yönde optimizasyon" olduğu yönündedir - özellikle, iyi kütüphaneler kullanmanın kolay olduğunu kabul ederken, C++' ın iyi kütüphaneler yazmayı neredeyse imkansız hale getirdiğini iddia etmektedir. Burada en temel hatalarından biri olduğuna inanıyorum. Gerçekte, neredeyse herhangi bir dil için iyi kütüphaneler yazmak son derece zordur. En azından, iyi bir kütüphane yazmak, bazı sorunlu etki alanlarının o kadar iyi anlaşılmasını gerektirir ki, kodunuz o etki alanındaki (veya ilişkili) çok sayıda olası uygulama için çalışır. C++ gerçekten ne yaptığı çoğu "çubuğu yükseltmek" - bir kütüphane ne kadar iyi gördükten sonra olabilir olmak, insanlar nadiren geri dönmek için istekli aksi takdirde sahip olacakları bir tür hileyi yazmak. Ayrıca birkaç gerçekten iyi kodlayıcıların "geri kalanımız" tarafından daha sonra (kolayca kabul ettiği gibi) kullanabileceği birkaç kütüphane yazması gerçeğini de göz ardı etmektedir. Bu gerçekten "bu bir hata değil, bir özellik."
Sırayla her noktayı vurmaya çalışmayacağım (bu sayfalar alacaktı), ancak doğrudan kapanış noktasına atlayın. Bjarne'den şöyle diyor: "tüm program optimizasyonu, kullanılmayan sanal fonksiyon tablolarını ve RTTI verilerini ortadan kaldırmak için kullanılabilir. Bu analiz özellikle dinamik bağlantı kullanmayan nispeten küçük programlar için uygundur."
Bunu, "Bu --- gerçekten zor bir problemdir" iddiasıyla, bunu durdurma problemiyle karşılaştıracak kadar ileri giderek iddia ediyor. Gerçekte, bu tür bir şey değil - aslında, Zortech C++ (hemen hemen ilk 1980'lerde MS-DOS için C++ derleyicisi dahil) bağlayıcı bunu yaptı. Muhtemel yabancı olmayan her bitin elimine edildiğinden emin olmak zor, ama yine de oldukça adil bir iş yapmak için tamamen makul.
Bununla birlikte, ne olursa olsun, çok daha önemli olan nokta, bunun her durumda çoğu programcı için tamamen alakasız olmasıdır. Biraz kod demonte edenlerin bildiği gibi, Meclis dilini hiç kütüphane olmadan yazmazsanız, yürütülebilir dosyalarınız neredeyse kesinlikle adil bir miktar "şeyler" (tipik olarak kod ve veri) içerir. muhtemelen bilmiyorum bile, aslında kullanarak bahsetmiyorum bile. Çoğu insan için, çoğu zaman, sadece önemli değil - en küçük gömülü sistemler için geliştirmediğiniz sürece, ekstra depolama tüketiminin basitçe alakasız olması.
Sonunda, bu rantın Linus'un aptallığından biraz daha fazla maddeye sahip olduğu doğru - ama bu ona hak ettiği hafif övgü ile tam olarak lanet veriyor.
Kaçınılmaz durumlar nedeniyle C++ 'da kodlamak zorunda kalan bir C programcısı olarak, işte benim deneyimim. Kullandığım çok az şey var, bu C++ ve çoğunlukla C'ye bağlı. Ana nedeni, C++ 'ı bu kadar iyi anlamamış olmam. Bana C++ inceliklerini ve nasıl iyi kod yazmak için bir akıl hocası yoktu. Ve çok çok iyi bir C++ kodundan rehberlik olmadan, C++ 'da iyi kod yazmak son derece zordur. IMHO, bu C++ 'ın en büyük dezavantajıdır, çünkü yeni başlayanları tutmak isteyen iyi C++ kodlayıcılarına ulaşmak zordur.
Gördüğüm performans hitlerinden bazıları genellikle STL'nin sihirli bellek tahsisine bağlı (evet, ayırıcıyı değiştirebilirsiniz, ancak C++ ile başladığında bunu kim yapar?). Genellikle C++ uzmanları tarafından vektörlerin ve dizilerin benzer performans sunduğuna dair argümanlar duyarsınız, çünkü vektörler dizileri dahili olarak kullanır ve soyutlama süper verimlidir. Bunu, vektör erişimi ve mevcut değerleri değiştirmek için pratikte doğru buldum. Ancak vektörlerin yeni bir girişi, inşası ve imhası için doğru değil. gprof, bir uygulama için kümülatif olarak% 25 zamanın vektör yapıcılar, yıkıcılar, memmove (yeni eleman eklemek için tüm vektörün taşınması için) ve diğer aşırı yüklenmiş vektör operatörlerinde (++ gibi) harcandığını gösterdi.
Aynı uygulamada, bir şeyin vektörüSmall bir şeyi temsil etmek için kullanıldıBig. Bir şeye rastgele erişime gerek yoktuKüçük bir şeydeBüyük. Liste yerine hala bir vektör kullanıldı. Vektörün kullanılmasının nedeni? Çünkü orijinal kodlayıcı, vektörlerin sözdizimi gibi diziye aşinaydı ve listeler için gerekli yineleyicilere çok aşina olmadığından (evet, C arka planından geliyor). C++ 'ı doğru yapmak için uzmanlardan çok fazla rehberlik gerektiğini kanıtlamaya devam ediyor. C, soyutlama olmadan çok az temel yapı sunar, C++ 'dan çok daha kolay elde edebilirsiniz.
Linus Thorvalds'ı sevmeme rağmen, bu rant madde içermiyor - sadece bir rant.
Eğer önemli bir rant görmek isterseniz, burada bir tane var: "C++ neden çevre için kötüdür, küresel ısınmaya neden olur ve yavruları öldürür" http://chaosradio.ccc.de/camp2007_m4v_1951.html Ek malzeme: http://www.fefe.de/c++/
Eğlenceli bir konuşma, imho
STL ve boost, kaynak kodu düzeyinde taşınabilir. Linus'un neden bahsettiğini tahmin ediyorum, C++ bir ABI (uygulama ikili arayüzü) yok. Bu nedenle, bağladığınız tüm kütüphaneleri aynı derleyici sürümü ve aynı anahtarlarla derlemeniz veya kendinizi dll sınırlamalarındaki C ABI ile sınırlamanız gerekir. Ben de bu annyoing buluyorum .. ama 3. parti kütüphaneler yapmak sürece, inşa ortamınız üzerinde kontrol altına almak gerekir. Kendimi C ABI ile sınırlamanın zahmete değmeyeceğini düşünüyorum. Dizeleri, vektörleri ve akıllı işaretçileri bir dll'den diğerine geçirebilmenin kolaylığı, derleyicileri yükseltirken veya derleyici anahtarlarını değiştirirken tüm kütüphaneleri yeniden oluşturmak zorunda kalmaya değer. Takip ettiğim altın kurallar:
-Uygulamayı değil arabirimi yeniden kullanmayı devral
-Raset üzerinde toplu toplama
-Mümkün olan her yerde serbest yöntemleri üye yöntemlerine tercih edin
-Her zaman kodunuzu özel olarak güvenli kılmak için RAII deyimini kullanın. Yakalamayı deneyin.
-Akıllı işaretçiler kullanın, çıplak (sahipsiz) işaretçilerden kaçının
- Değer semantiği referans semantiğe tercih eder
- Tekerleği yeniden icat etmeyin, stl kullanın ve artırın
-Özel gizlemek ve/veya derleyici güvenlik duvarı sağlamak için Pimpl deyimini kullanın