Ben olmamalı zaman karışık C ve C ++ kodu var inanıyorum; Bu bir sorun mu ve nasıl düzeltilir?


10

Arka plan / Senaryo

Tamamen C'de bir CLI uygulaması yazmaya başladım ("Merhaba Dünya" ya da bunların bir varyasyonu olmayan ilk uygun C veya C ++ programım). Çevresinde ben kullanıcı girişi (char dizileri) "dizeleri" ile çalışıyordu ve C ++ dize flama nesnesi keşfetti. Bunları kullanarak kod kaydedebilirsiniz gördüm, bu yüzden onları uygulama aracılığıyla kullandım. Bu, dosya uzantısını .cpp olarak değiştirdiğim ve şimdi uygulamayı g++bunun yerine derlediğim anlamına gelir gcc. Buna dayanarak, uygulamanın şimdi teknik olarak bir C ++ uygulaması olduğunu söyleyebilirim (kodun% 90'ı + C olarak adlandırdığım şeyde yazılır, ancak sınırlı deneyimim verildiğinde iki dil arasında çok fazla geçiş olduğu için iki). 900 satır uzunluğunda tek bir .cpp dosyasıdır.

Önemli Faktörler

Programın ücretsiz (paradaki gibi) olmasını ve herkesin sahip olması için serbestçe dağıtılabilir ve kullanılabilir olmasını istiyorum. Benim endişem birisi koduna bakacak ve etkisi için bir şeyler düşünecek olmasıdır:

Oh, kodlamaya bak, korkunç, bu program bana yardımcı olamaz

Ne zaman potansiyel olabilir! Başka bir konu kodun verimli olmasıdır (Ethernet bağlantısını test etmek için bir programdır). Kodun, uygulamanın performansını veya çıktısını ciddi şekilde engelleyebilecek kadar verimsiz olan hiçbir parçası olmamalıdır. Ancak, bunun belirli işlevler, yöntemler, nesne çağrıları, vb. İle ilgili yardım isterken Stack Overflow için bir soru olduğunu düşünüyorum.

Benim sorum

(Bence) karışık C ve C ++ olması belki de olmamalı. Hepsini C ++ ile yeniden yazmak için bakmalıyım (bununla birlikte, daha yeni C ++ teknikleri kullanılarak yoğunlaştırılabilecek bir C tarzında bir şey kodladığım daha fazla C ++ nesnesi ve yöntemi uygulamak veya dize flama nesnelerinin kullanımını kaldırmak anlamına gelir ve hepsini "geri" C koduna getirmek? Burada doğru bir yaklaşım var mı? Ben kayboldum ve kitlelerin gözünde bu uygulama "İyi" tutmak için bazı rehberlik gerekir, bu yüzden onu kullanacak ve bundan yararlanacaklar.

Kod - Güncelleme

İşte koda bir bağlantı. Yaklaşık% 40 yorum, daha akıcı hissedene kadar neredeyse her satırı yorumluyorum. Bağlantı kurduğum kopyada, hemen hemen tüm yorumları kaldırdım. Umarım bu okumayı zorlaştırmaz. Yine de hiç kimsenin tam olarak anlaması gerekmediğini umuyorum. Eğer ölümcül tasarım kusurları yapmışsam, kolayca tanımlanabilmelerini umuyorum. Ayrıca bahsetmeliyim, birkaç Ubuntu masaüstü ve dizüstü bilgisayar yazıyorum. Kodu diğer işletim sistemlerine taşımak istemiyorum.


3
CLI'yi sizin için netleştirdim. CLI, C perspektifinden fazla bir anlam ifade etmeyen Ortak Dil Altyapısı'na da başvurabilir.
Robert Harvey

FORTRAN'da yeniden yazabilirsin, OOF'u hiç duymadım.
ott--

2
"Güzel" değilse yazılımınızı kullanmayan insanlar için çok fazla endişe etmem. Kullanıcılarınızın% 99'u koda bakmayacak veya yapmaları gerekeni başardıkları sürece nasıl yazıldığına bile bakmayacak. Kod yardım etmek vb tutarlı olması, ancak, önemli olan sizi o uzun vadeli korunmasında.
Evicatos

1
Neden özgür yazılım (örneğin GPLv3 lisansı altında ), örneğin github koduyla yapmıyorsunuz . Kodunuzda bir LICENSEdosya yok. İlginç geri bildirimler alabilirsiniz.
Basile Starynkevitch

Yanıtlar:


13

En baştan başlayalım: karışık C ve C ++ kodu oldukça yaygındır. Yani başlamak için büyük bir kulüptesiniz. Vahşi doğada büyük C kod tabanlarımız var. Ancak açık nedenlerden dolayı, birçok programcı aynı derleyicide C ++ 'a erişimi olan C'ye en az yeni şeyler yazmayı reddediyor, yeni modüller bu şekilde yazılmaya başlıyor - ilk önce mevcut parçaları yalnız bırakıyor.

Sonunda mevcut bazı dosyalar C ++ olarak yeniden derlenir ve bazı köprüler silinebilir ... Ama gerçekten uzun zaman alabilir.

Biraz ilerliyorsunuz, tam sisteminiz artık C ++, sadece çoğu "C-stili" yazıyor. Ve stillerin karışımını bir sorun olarak görüyorsunuz, ne yapmamanız gerekir: C ++, birçok stili destekleyen çok paradigma bir dildir ve onların iyilik için birlikte var olmalarına izin verir. Aslında ana güç budur, tek bir tarza zorlanmıyorsunuz. Burada ve orada yetersiz olan biri, her yerde olmayan şanslarla.

Kod tabanının yeniden çalışması iyi bir fikirdir, eğer bozulursa. Ya da gelişme yolundaysa. Ancak eğer işe yarıyorsa (orijinal anlamda), lütfen en temel mühendislik prensibini takip edin: eğer kırılmazsa, düzeltmeyin. Soğuk parçaları yalnız bırakın, çabalarınızı önemli olduğu yere koyun. Kötü, tehlikeli - veya yeni özelliklerde ve sadece yatak yapmak için parçaları yeniden düzenleyin.

Adres almak için genel şeyler arıyorsanız, bir C kod tabanından çıkmaya değer:

  • tüm str * işlevleri ve char [] - bir dize sınıfıyla değiştirilir
  • sprintf kullanıyorsanız, sonucu içeren bir dize döndüren veya dizeye koyan bir sürüm oluşturun ve kullanımı değiştirin. (Akışlarla hiç uğraşmadıysanız, kendinize bir iyilik yapın ve beğenmedikçe onları atlayın; gcc, formatları kontrol etmek için kutudan mükemmel tipte güvenlik sağlar, sadece uygun özelliği ekleyin.
  • en malloc ve ücretsiz - DEĞİL yeni ve sil, ancak vektör, liste, harita ve diğer koleksiyon.
  • bellek yönetiminin geri kalanı (önceki iki noktadan sonra oldukça nadir olmalıdır, akıllı işaretçilerle örtün veya özel koleksiyonlarınızı uygulayın
  • RAII sarmalayıcılarını veya sınıflarını kullanmak için diğer tüm kaynak kullanımını (DOSYA *, muteks, kilit vb.) değiştirin

İşiniz bittiğinde, kod tabanının makul bir şekilde istisna açısından güvenli olabileceği noktaya yaklaşırsınız, böylece dönüş kodlu futbolu istisnalar ve yalnızca üst düzey işlevlerde nadir deneme / yakalama kullanarak bırakabilirsiniz .

Bunun ötesinde, sadece sağlıklı C ++ 'da yeni kodlar yazın ve eğer mevcut kodda iyi bir yedek olan bazı sınıflar doğmuşsa, onları alın.

Sözdizimi ile ilgili şeylerden bahsetmedim, açıkçası tüm yeni kodlarda işaretçiler yerine refs kullanıyorum, ancak sadece bu değişiklik için eski C parçalarını değiştirmek iyi bir değer değil. Eğer hitap etmelisiniz, yapabildiğiniz her şeyi ortadan kaldırmalı ve geri kalanı için sarmalayıcı işlevlerinde C ++ varyantlarını kullanmalısınız. Ve en önemlisi, mümkün olan yerlerde const ekleyin. Bunlar daha önceki mermilerle araya girer. Ve makrolarınızı birleştirin ve numaralandırma, satır içi işlev veya şablon haline getirebileceklerinizi değiştirin.

Henüz yapılmadıysa Sutter / Alexandrescu'nun C ++ Kodlama Standartlarını okumanızı ve yakından takip etmenizi öneririm.


Balog girişi için çok teşekkürler, gözlerimdeki tüm sesli tavsiyeler ve hepsine katılıyorum. Kod bölümünü yavaş yavaş çalışma koduna öncelik vererek düşünüyorum.
jwbensley

7

Kısacası: Cehenneme gitmeyeceksin, kötü bir şey olmayacak, ama herhangi bir güzellik yarışmasını da kazanamayacaksın.

C ++ 'ı "daha iyi bir C" olarak kullanmak mükemmel bir şekilde mümkün olsa da, C ++' ın faydalarının çoğunu atmış olursunuz, ancak kendinizi vanilya C ile kısıtlamıyorsunuz, C'nin faydalarından (basitlik, taşınabilirlik) yararlanmıyorsunuz , şeffaflık, birlikte çalışabilirlik). Başka bir deyişle: C ++, C'nin bazılarını kazanmak için bazı niteliklerini feda eder - örneğin, C'nin daha güçlü soyutlamaları için bellek ayırmalarının nerede ve ne zaman yapıldığını her zaman net bir şekilde görebileceğiniz şeffaflık.

Kodunuz şimdi iyi çalışıyor gibi göründüğünden, muhtemelen sadece bunun için yeniden yazmak iyi bir fikir değildir: şimdilik olduğu gibi saklayın ve gittikçe daha fazla deyimsel C ++ ile değiştirin, her seferinde bir parça, herhangi bir bölüm üzerinde çalıştığınızda. Ve bir sonraki projeniz için bu şekilde öğrenilen dersleri aklınızda bulundurun, hepsinden önemlisi: C ve C ++ aynı dil değildir ve projeniz boyunca C ++ 'a geçmeye karar vermekten daha iyi bir seçim yapmaktan daha iyidir. .


Tavsiye için teşekkürler tdammers. Söylediklerinize katılıyorum ve gemiye alıyorum, teşekkürler!
jwbensley

4

C ++, birçok özelliğe sahip olması bakımından çok karmaşık bir dildir. Aynı zamanda çoklu paradigmaları destekleyen bir dildir, yani herhangi bir nesne kullanmadan tamamen prosedürel kod yazmanıza izin verir.

Bu yüzden C ++ çok karmaşık olduğu için, insanların kodlarında özelliklerinin sınırlı bir alt kümesini kullandığını görürsünüz. Yeni başlayanlar genellikle akış G / Ç, dize nesnelerini ve malloc / free yerine new / delete ve belki de işaretçiler yerine referansları kullanırlar. Nesneye yönelik özellikleri öğrendikçe, "Sınıflarla C" adlı bir stilde yazmaya başlayabilirsiniz. Sonunda, C ++ hakkında daha fazla bilgi edindikçe şablonları, RAII , STL , akıllı işaretçiler vb. Kullanmaya başlarsınız .

Yapmaya çalıştığım nokta, C ++ öğrenmenin zaman alan bir süreç olması. Evet, şu anda kodunuz muhtemelen C ++ yazmaya çalışan bir C programcısı tarafından yazılmış gibi görünüyor. Ve sadece öğrendiğiniz için, bu mükemmel. Ancak, üretim kodunda, daha iyi bilmeli tecrübeli programcılar tarafından yazılan bu tür bir şey gördüğümde, beni rahatsız ediyor.

Unutmayın, iyi bir Fortran programcısı herhangi bir dilde iyi Fortran kodu yazabilir. :)


2

C ++ 'a giderken eski C programcılarının birçoğunun izlediği yolu tam olarak tekrar özetlediğiniz anlaşılıyor. "Daha iyi bir C" olarak kullanıyorsunuz. Yirmi yıl önce bu bir anlam ifade etti, ancak bu noktada C ++ 'da (Standart Şablon Kütüphanesi gibi) çok daha güçlü yapılar var, şimdi nadiren mantıklı. En azından, kodunuza bakarken C ++ programcılarına anevrizma vermekten kaçınmak için aşağıdakileri yapmanız gerekir:

  • ? Yerine akış kullanın printfaile.
  • Yerine newkullanın malloc.
  • Tüm veri yapıları için STL kap sınıflarını kullanın.
  • Mümkün std::stringolan char*her yerde kullanın
  • RAII'yi anlama ve kullanma

Programınız kısaysa (ve ~ 900 satır kısa görünüyorsa) kişisel olarak sağlam sınıflar oluşturmaya çalışmanın gerekli veya hatta yararlı olduğunu düşünmüyorum.


Tavsiyen için teşekkürler Steven, Evet sınıflar hakkında aynı fikrim vardı ve bence kodu tek bir dosyada toplayabilirim. Teşekkürler!
jwbensley

2

Kabul edilen cevap, C ++ kodunu idiomatik C ++ 'a dönüştürmenin artılarını, sanki C ++ kodu C kodundan daha iyi bir anlamda olurdu. Diğer yanıtlar ile karışık kodda herhangi bir büyük değişiklik yapılmasının gereksiz olduğunu muhtemelen kabul ediyorum.

C ve C ++ 'ın karıştırılmasının sürdürülebilir olup olmadığı, karıştırmanın nasıl yapıldığına bağlıdır. Küçük hatalar, örneğin C kodunda (istisna güvenli olmayan) istisnalar oluştuğunda, bellek sızıntılarına veya veri bozulmalarına neden olduğunda ortaya çıkabilir. Daha güvenli ve oldukça yaygın bir yol, sınıflara C kütüphanelerini veya arayüzlerini sarmak veya bir C ++ projesinde başka bir tür izolasyonda kullanmaktır.

Tüm projenizi deyimsel C ++ (veya C) olarak yeniden yazmaya karar vermeden önce, diğer yanıtlarda sunulan değişikliklerin çoğunun programınızı daha yavaş hale getirebileceğini veya diğer istenmeyen etkilere neden olabileceğini bilmelisiniz. Örneğin:

  • yığın tahsisli C dizelerini std :: dizelerine değiştirmek gereksiz yığın tahsislerine neden olabilir
  • ham işaretçileri bazı paylaşılan işaretçi türlerine (std :: shared_ptr gibi) değiştirmek referans sayımı, dahili sanal üye işlevleri ve iş parçacığı güvenliği nedeniyle erişim yüküne neden olur
  • standart kütüphane akışları C muadillerinden daha yavaştır
  • kaplar gibi RAII sınıflarının dikkatsiz kullanımı, özellikle C ++ 11'in hareket semantiği kullanılamadığında gereksiz işlemlere neden olabilir
  • şablonlar daha uzun derleme sürelerine, belirsiz derleme hatalarına, kod şişkinliğine ve derleyiciler arasında taşınabilirlik sorunlarına neden olabilir
  • istisna-güvenli kod yazmak zordur, bu da dönüşüm sırasında ince hataların girilmesini kolaylaştırır
  • paylaşılan bir kitaplıktaki C ++ kodunu düz C'den daha kullanışlıdır
  • C ++, örneğin C89 kadar geniş bir şekilde desteklenmez

Sonuç olarak, karışık C ve C ++ kodlarından deyimsel C ++ 'a dönüştürmek, sadece uygulama süresinden çok daha fazlasına sahip bir araç olarak görülmeli ve araç kutunuzu görünüşte kullanışlı özelliklerle genişletmelidir. Bu nedenle, genel duruma "bağlıdır" dışında bir cevap vermek çok zordur.


"standart kütüphane akışları C muadillerinden daha yavaştır": Muhtemelen uluslararası baskı için printf'den kesinlikle daha zordur.
Tekilleştirici

1

C ve C ++ 'ı karıştırmak kötü bir formdur. Farklı dillerdir ve gerçekten bu şekilde ele alınmalıdır. En uygun olanı seçin ve o dilde deyimsel kod yazmaya çalışın.

C ++ size çok fazla kod kaydediyorsa, C ++ ile yapıştırın ve C parçalarını yeniden yazın. Endişeleniyorsanız, performans farkının bile farkedileceğinden şüpheliyim.


Bu yüzden ben C ile yazılmış kullanmak istediğiniz bu bir kütüphane var ve ben C ++ ile yazılmış kullanmak istediğiniz bu diğer kütüphane var ... Sadece iyi çalışır kod yeniden yazma sadece gereksiz çalışma oluşturmaktır.
gnasher729

1

Her zaman C ve C ++ arasında gidip geliyorum ve bu şekilde çalışmayı tercih eden oddball tipiyim. Daha yüksek düzey şeyler için C ++ 'ı tercih ediyorum, C-veri türlerini röntgenlememe ve bunları bit ve bayt gibi tedavi etmeme izin veren kör bir araç olarak kullanıyorum, örneğin, memcpydüşük seviyeli veri yapılarını ve bellek ayırıcıları uygulamak için kullanışlı buldum . Kendinizi ham bitler ve bayt düzeyinde çalışırken bulursanız, C ++ 'ın gerçekten zengin tip sistemi hiçbir şeye yardımcı olmaz ve genellikle bu tür kodu yazmak için daha kolay buluyorum. herhangi bir C veri türüne sadece bit ve bayt olarak davranır.

Akılda tutulması gereken en önemli şey, bu iki dilin iyi bir şekilde birbirine bağlanmadığıdır.

1. AC fonksiyonu hiçbir zaman bir C ++ fonksiyonunu çağırmamalıdır throw. Günlük C ++ kod türünüzün bir istisna olarak kaç yerde çalıştırılabileceği göz önüne alındığında, bu genellikle C işlevlerinizin genel olarak C ++ işlevlerini çağırmaması gerektiği anlamına gelir. Aksi takdirde, C ++ istisnasını yakalamak için pratik bir yolu olmadığından, C kodunuz yığın gevşemesi sırasında ayırdığı kaynakları serbest bırakamaz. C kodunuzdan bir C ++ işlevini geri arama olarak çağırmasını gerektiriyorsanız, C ++ tarafı, C koduna geri dönmeden önce karşılaştığı istisnaların yakalandığından emin olmalıdır.

2. Veri türlerini ham bitler ve baytlar olarak işleyen, tür sistemi üzerinde buldozerleme yapan C kodu yazarsanız (bu aslında C'yi ilk başta kullanmamın ana nedenidir), o zaman bu kodu C ++ verilerine karşı kullanmak istemezsiniz. kopya kurucuları, yıkıcıları ve sanal göstergeleri olabilecek türler ve bunlara saygı duyulması gereken şeyler. Bu yüzden genellikle bu tür C kodu kullanarak C kodu, bu tür C kodu kullanarak C ++ kodu olmalıdır.

C ++ kodunuzun böyle bir C kodu kullanmasını istiyorsanız, genellikle onu genel C veri yapısında depoladığı verinin yapılandırılması önemsiz bir veri türü olduğundan emin olan bir sınıfın uygulama detayı olarak kullanmak istersiniz. ve yok et. Neyse yapı özellikleri vardır böyle sen jenerik C veri yapısı içine saklamak olan tipleri önemsiz yıkıcı ve Kurucular ve bu şekilde kalacaktır emin olarak statik bir assert kontrol etmek kullanabilir.

Hepsini C ++ ile yeniden yazmak için bakmalıyım (bununla birlikte, daha yeni C ++ teknikleri kullanılarak yoğunlaştırılabilecek bir C tarzında bir şey kodladığım daha fazla C ++ nesnesi ve yöntemi uygulamak veya dize flama nesnelerinin kullanımını kaldırmak anlamına gelir ve hepsini "geri" C koduna getirmek?

Bence yukarıdaki kurallara uyursanız ve kodunuzun yazdığınız testlere karşı iyi test edildiğinden emin olmanız gerekmez. İyileştirmeye değer olan kısım şu ana kadar C ++ ile yazdığınız koddur, ancak bu kesinlikle C + 'ya dayalı C ++ kodunu taşımak zorunda olduğunuz anlamına gelmez.

C ++ ile yazdığınız ve C ++ kodu olarak derlediğiniz kod ile dikkatli olmalısınız. C ++ 'da C benzeri kodlama kullanmak sorun istiyor. Örneğin, kaynakları el ile ayırır ve serbest bırakırsanız, kodunuz freekaynağı kullanabilmeniz için önce işlevden dolaylı olarak çıkacağınız bir istisna ile karşılaşabileceğinden, kodunuzun istisna açısından güvenli olmaması ihtimali vardır . C ++, RAII ve akıllı işaretçiler gibi şeyler kabarık bir kolaylık değildir, çünkü istisnai yolları düşünmeden sadece normal yürütme yollarına bakarsanız görünebilirler. Genellikle bir istisna ile karşılaştığında her yere sızmaya başlamayacak basit ve etkili doğru istisna-güvenli kod yazmak için temel bir gerekliliktir.

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.