C # size “kendinizi asmak için daha az ip” verir mi C ++? [kapalı]


14

Joel Spolsky, C ++ 'ı "kendinizi asmak için yeterli ip" olarak nitelendirdi . Aslında Scott Meyers tarafından "Effective C ++" özetleniyordu:

Temel olarak, C ++ kendinizi asmak için yeterli ip ve daha sonra birkaç mil fazladan ip ve daha sonra M & Ms olarak gizlenmiş birkaç intihar hapı olduğunu söyleyen bir kitap ...

Kitabın bir kopyası yok, ancak kitabın büyük bir kısmının , çalışma zamanı sizin için bu sorunları yönettiği için C # 'da tartışılan gibi görünen belleği yönetmenin tuzaklarıyla ilgili olduğuna dair göstergeler var .

Sorularım:

  1. C # C ++ 'da sadece dikkatli programlama ile önlenen tuzaklardan kaçınır mı? Eğer öyleyse, ne derece ve nasıl önlenirler?
  2. C # 'da yeni bir C # programcısının bilmesi gereken yeni, farklı tuzaklar var mı? Eğer öyleyse, neden C # tasarımı ile önlenemediler?

10
Gönderen SSS : Your questions should be reasonably scoped. If you can imagine an entire book that answers your question, you’re asking too much.. Bunun böyle bir soru olarak nitelendirdiğine inanıyorum ...
Oded

@Oded Karakter sınırlı başlık sorusuna mı başvuruyorsunuz? Ya da yazımın gövdesindeki 3+ daha kesin sorularım?
alx9r

3
Açıkçası - hem başlık hem de "daha kesin sorular" ın her biri.
Oded

3
Bu soru hakkında bir Meta tartışması başlattım .
Oded

1
Şimdi silinmiş 3. sorunuzla ilgili olarak, Bill Wagner'in Etkili C # serisi (şimdi 3 kitap), C # programlaması hakkında, konuyla ilgili okuduğum diğer her şeyden daha çok şey öğretti. EC # 'ın Martins incelemesi, Effective C ++ için asla doğrudan bir yedek olamayacağı için doğrudur, ancak olması gerektiğini düşünmek yanlıştır. Artık kolay hatalar hakkında endişelenmeniz gerekmediğinde, daha zor hatalara geçmelisiniz.
Mark Booth

Yanıtlar:


33

C ++ ve C # arasındaki temel fark, tanımlanmamış davranıştan kaynaklanır .

It has bir şey manuel bellek yönetimini yapıyor ilgisi yok. Her iki durumda da, bu çözülmüş bir sorundur.

C / C ++:

C ++ 'da, bir hata yaptığınızda sonuç tanımsız olur.
Veya, sistem hakkında belirli türde varsayımlar yapmaya çalışırsanız (örn. İmzalı tamsayı taşması), programınızın tanımsız olma ihtimali vardır.

Belki tanımlanmamış davranışlarla ilgili bu 3 bölümlük diziyi okuyun .

Bu kadar hızlı C ++ kılan - derleyici terslik zaman doğruluğu kontrol önlemek, böylece, ne olduğu hakkında endişe zorunda değildir.

C #, Java vb.

C #, sen ediyoruz garantili fazla hata istisna olarak yüzüne patlatmak edeceğini ve siz garanti olsun daha çok altta yatan sistem hakkında.
Bu, C # 'ı C ++ kadar hızlı hale getirmek için temel bir engeldir, ancak aynı zamanda C ++' ı güvenli hale getirmek için de temel bir engeldir ve C # ile çalışmayı ve hata ayıklamayı kolaylaştırır.

Diğer her şey sadece sos.


Tüm tanımlanmamış şeyler gerçekten uygulama tarafından tanımlanır, bu nedenle Visual Studio'da imzasız bir tamsayı taşarsanız, doğru derleyici bayraklarını açtıysanız bir istisna alırsınız. Şimdi bunun hakkında konuştuğunuzu biliyorum, ama tanımsız bir davranış değil , sadece insanlar genellikle bunu kontrol etmiyor. (operatör ++ gibi tanımlanmamış davranışlarla aynıdır, her derleyici tarafından iyi tanımlanmıştır). Aynı şeyi C # ile de söyleyebilirsiniz, sadece 1 uygulama var - Mono'da çalıştırırsanız bol miktarda 'tanımsız davranış' - örn. bugzilla.xamarin.com/show_bug.cgi?id=310
gbjbaanb

1
Gerçekten tanımlanmış mı yoksa yalnızca geçerli Windows sürümündeki geçerli .net uygulaması ne tarafından tanımlanmış? Eğer g ++ ne yaparsa onu tanımlarsanız, c ++ tanımsız davranışı bile tam olarak tanımlanır.
Martin Beckett

6
İmzasız tam sayıların taşması UB değildir. UB olan işaretli tamsayıları taşar .
DeadMG

6
@gbjbaanb: gibi DeadMG dedi - işaretli tamsayı taşması olduğunu tanımsız. O var olmayan uygulama tanımlı. Bu ifadelerin C ++ standardında belirli anlamları vardır ve aynı şey değildir. Bu hatayı yapma.
user541686

1
@CharlesSalvia: Uh, "C ++, CPU önbelleğinden yararlanmayı" C # 'den tam olarak nasıl kolaylaştırır? C ++, C # 'da sahip olamayacağınız bellek üzerinde ne tür bir kontrol sağlıyor?
user541686

12

C # C ++ 'da sadece dikkatli programlama ile önlenen tuzaklardan kaçınır mı? Eğer öyleyse, ne derece ve nasıl önlenirler?

Çoğu öyle, bazıları değil. Ve elbette, bazı yenilerini yapar.

  1. Tanımsız davranış - C ++ ile ilgili en büyük tuzak, tanımlanmamış bir sürü dil olmasıdır. Derleyici, bunları yaptığınızda tam anlamıyla evreni havaya uçurabilir ve sorun olmayacaktır. Doğal olarak, bu nadir, ancak bir tek makine üzerinde ve gerçekten sebepsiz başka değil iş için çalışma cezası programınız için oldukça yaygın. Ya da daha kötüsü, kurnazca farklı davranır. C #, spesifikasyonunda tanımlanmamış davranışa sahip birkaç vakaya sahiptir, ancak nadirdir ve seyrek olarak seyahat edilen dil alanlarındadır. C ++ her ifade yaptığınızda tanımsız davranışa girme olanağına sahiptir.

  2. Bellek Sızıntıları - Bu, modern C ++ için daha az endişe kaynağıdır, ancak yeni başlayanlar için ve ömrünün yaklaşık yarısı boyunca C ++, bellek sızmasını kolaylaştırdı. Etkili C ++, bu endişeyi ortadan kaldırmak için uygulamaların evrimi etrafında geldi. Yani C #, söz konusu olabilir hala bellek sızdırıyor. İnsanların karşılaştığı en yaygın olay olay yakalamadır. Bir nesneniz varsa ve yöntemlerinden birini bir etkinliğe işleyici olarak koyarsanız, nesnenin ölmesi için o etkinliğin sahibinin GC'lenmiş olması gerekir. Yeni başlayanların çoğu, olay işleyicisinin referans olarak sayıldığını fark etmez. Bellek sızdırabilen tek kullanımlık kaynakların atılmamasına ilişkin sorunlar da vardır, ancak bunlar Etkili C ++ öncesi işaretçiler kadar yaygın değildir.

  3. Derleme - C ++ geciktirilmiş bir derleme modeline sahiptir. Bu onunla güzel oynamak ve derleme sürelerini aşağı tutmak için bir dizi hileye yol açar.

  4. Dizeler - Modern C ++ bunu biraz daha iyi hale getiriyor, ancak char*2000 yılından önce tüm güvenlik ihlallerinin ~% 95'inden sorumlu. Deneyimli programcılar için odaklanacaklar std::string, ancak yine de kaçınılması gereken bir şey ve eski / daha kötü kütüphanelerde bir sorun . Ve bu unicode desteğe ihtiyacınız olmadığı için dua ediyor.

Ve gerçekten, bu buzdağının görünen kısmı. Ana sorun, C ++ 'ın yeni başlayanlar için çok zayıf bir dil olmasıdır. Oldukça tutarsızdır ve eski, gerçekten kötü tuzakların birçoğu deyimleri değiştirerek ele alınmıştır. Sorun şu ki, yeni başlayanlar deyimleri Effective C ++ gibi bir şeyden öğrenmeleri gerekiyor. C #, bu sorunların çoğunu tamamen ortadan kaldırır ve öğrenim yolunda ilerleyene kadar geri kalanını daha az endişelendirir.

C # 'da yeni bir C # programcısının bilmesi gereken yeni, farklı tuzaklar var mı? Eğer öyleyse, neden C # tasarımından kaçınılamadı?

Olayda "bellek sızıntısı" sorunundan bahsettim. Bu, programcı dilin yapamayacağı bir şey beklediği kadar bir dil sorunu değildir.

Başka bir C # nesnesi için sonlandırıcının teknik olarak çalışma zamanı tarafından çalıştırılacağı garanti edilmez . Bu genellikle önemli değildir , ancak bazı şeylerin beklediğinizden farklı tasarlanmasına neden olur.

Programcıların karşılaştığı bir diğer yarı tuzak, anonim işlevlerin yakalama semantiği. Bir değişkeni yakaladığınızda değişkeni yakalarsınız . Misal:

List<Action> actions = new List<Action>();
for(int x = 0; x < 10; ++x ){
    actions.Add(() => Console.WriteLine(x));
}

foreach(var action in actions){
    action();
}

Safça düşünülen şeyi yapmaz. Bu 1010 kez yazdırır .

Eminim unuttuğum birkaç kişi daha var, ancak asıl mesele daha az yaygın olmaları.


4
Bellek sızıntıları artık geçmişte kaldı char*. C # 'da hala bellek sızdırabileceğini söylememe gerek yok.
DeadMG

2
Şablonları "yüceltilmiş dize yapıştırma" olarak adlandırmak biraz fazla. Şablonlar gerçekten C ++ en iyi özelliklerinden biridir.
Charles Salvia

2
@CharlesSalvia Elbette, C ++ ' ın gerçekten ayırt edici özelliği. Ve evet, bu derleme etkisi için belki de aşırı basitleştirme. Ancak, özellikle dikkatli değilseniz derleme sürelerini ve çıktı boyutunu orantısız olarak etkiler.
Telastyn

2
@deadMG kesinlikle, yine de C ++ 'da kullanılan / ihtiyaç duyulan şablon meta-programlama hilelerinin çoğunun ... farklı bir mekanizma yoluyla daha iyi uygulandığını iddia ediyorum.
Telastyn

2
@Telastyn, type_traits'ın tüm mesele derleme zamanında tür bilgisi almaktır, böylece bu bilgileri, şablonları özelleştirmek veya aşırı işlevler gibi belirli yollarla yapmak için kullanabilirsinizenable_if
Charles Salvia

10

Bence C ++ 'ın tehlikeleri biraz abartılı.

Temel tehlike şudur: C #, unsafeanahtar kelimeyi kullanarak "güvensiz" işaretçi işlemlerini gerçekleştirmenize izin verirken , C ++ (çoğunlukla C'nin bir üst kümesidir), istediğiniz zaman işaretçileri kullanmanıza izin verir. Bellek sızıntıları, arabellek taşmaları, sarkan işaretçiler vb.Gibi işaretleyicilerin (C ile aynı olan) kullanımındaki doğal tehlikelerin yanı sıra, C ++ bir şeyleri ciddiye almanız için yeni yollar sunar.

Tabii ki Joel Spolsky'nin bahsettiği bu "ekstra ip" temelde bir şeye iniyor: " 3 Kuralı " olarak da bilinen kendi belleklerini dahili olarak yöneten sınıflar yazmak (şimdi Kural olarak adlandırılabilir) 4 veya C ++ 11'de 5 kuralı). Bu, kendi bellek ayırmalarını dahili olarak yöneten bir sınıf yazmak istediğinizde, ne yaptığınızı bilmeniz gerekir, aksi takdirde programınız büyük olasılıkla çökecektir. Dikkatlice yanlış yapmak şaşırtıcı derecede kolay bir kurucu, kopya yapıcı, yıkıcı ve atama operatörü oluşturmanız gerekir, genellikle çalışma zamanında tuhaf çökmelere neden olur.

ANCAK , gerçek günlük C ++ programlamasında, gerçekten kendi belleğini yöneten bir sınıf yazmak çok nadirdir , bu nedenle C ++ programcılarının bu tuzaklardan kaçınmak için her zaman "dikkatli" olması gerektiğini söylemek yanıltıcıdır. Genellikle, daha çok bir şey yapıyorsunuz:

class Foo
{
    public:

    Foo(const std::string& s) 
        : m_first_name(s)
    { }

    private:

    std::string m_first_name;
};

Bu sınıf, Java veya C # ile yaptıklarınıza oldukça yakın görünüyor - açık bir bellek yönetimi gerektirmiyor (çünkü kütüphane sınıfı std::stringtüm bunları otomatik olarak hallediyor) ve varsayılandan bu yana hiç "3 Kural" gerektirmiyor kopya oluşturucu ve atama operatörü iyidir.

Sadece böyle bir şey yapmaya çalıştığınızda:

class Foo
{
    public:

    Foo(const char* s)
    { 
        std::size_t len = std::strlen(s);
        m_name = new char[len + 1];
        std::strcpy(m_name, s);
    }

    Foo(const Foo& f); // must implement proper copy constructor

    Foo& operator = (const Foo& f); // must implement proper assignment operator

    ~Foo(); // must free resource in destructor

    private:

    char* m_name;
};

Bu durumda, acemilerin atama, yıkıcı ve kopya oluşturucuyu doğru hale getirmesi zor olabilir. Ancak çoğu durumda, bunu yapmak için hiçbir neden yoktur. C ++, std::stringve gibi kütüphane sınıflarını kullanarak zamanın% 99'unu manuel bellek yönetiminden kaçınmayı kolaylaştırır std::vector.

İlgili başka bir sorun, belleği özel durum oluşma olasılığını hesaba katmayacak şekilde elle yönetmektir. Sevmek:

char* s = new char[100];
some_function_which_may_throw();
/* ... */
delete[] s;

Eğer some_function_which_may_throw()gerçekte yok bir istisna için ayrılan bellek nedeniyle, bir bellek sızıntısı ile kalacaksın sıslah asla. Fakat yine de, pratikte bu, "3 Kuralı" nın artık gerçekten bir sorun olmadığı için aynı sebepten kaynaklanmıyor. Kendi hafızanızı ham işaretçilerle gerçekten yönetmek çok nadirdir (ve genellikle gereksizdir). Yukarıdaki problemden kaçınmak için tek yapmanız gereken bir std::stringveya kullanmaktır std::vectorve yıkıcı istisna atıldıktan sonra yığın çözme sırasında otomatik olarak çağrılır.

Bu nedenle, genel bir tema , otomatik başlatma / imha, kopya oluşturucular ve istisnalar gibi C'den miras alınmayan birçok C ++ özelliğinin , bir programcıyı C ++ 'da manuel bellek yönetimi yaparken ekstra dikkatli olmaya zorlamasıdır. Ama yine de, bu sadece manuel bellek yönetimi yapmak istiyorsanız, standart konteynırlarınız ve akıllı işaretçileriniz olduğunda neredeyse hiç gerekmeyen bir problemdir.

Benim düşünceme göre, C ++ size fazladan bir ip verirken, kendinizi asmak için kullanmak neredeyse hiç gerekmiyor ve Joel'in bahsettiği tuzaklar modern C ++ 'da kaçınmak çok kolay.


C ++ 03'te üçün kuralı ve şimdi C ++ 11'de dörtün kuralı.
DeadMG

1
Kopya oluşturucu, taşıma kurucu, kopya atama, taşıma atama ve yıkıcı için buna "5 kuralı" diyebilirsiniz. Ancak sadece doğru kaynak yönetimi için hareket semantiği her zaman gerekli değildir.
Charles Salvia

Ayrı taşıma ve kopyalama atamasına ihtiyacınız yoktur. Kopyala-takas deyimi her iki operatörü bir arada yapabilir.
DeadMG

2
Soruyu cevaplar Does C# avoid pitfalls that are avoided in C++ only by careful programming?. Cevap "gerçekten değil, çünkü Joel modern C ++ 'da bahsettiği tuzaklardan kaçınmak çok kolay"
Charles Salvia

1
IMO, C # veya Java gibi üst düzey diller size bellek yönetimi ve size yardımcı olması gereken diğer şeyler sağlarken , her zaman olduğu gibi yapmaz. Hala kod tasarımınızı bakıyorum böylece bellek sızıntısı bırakmazsınız (bu tam olarak C ++ ile aradığınız şey değildir). Benim tecrübelerime göre, C ++ 'da hafızayı yönetmeyi daha kolay buluyorum çünkü yıkıcıların çağrıldığını biliyorsunuz ve çoğu durumda temizlemeyi yapıyorlar. Sonuçta, C ++, tasarımın verimli bellek yönetimine izin vermediği durumlar için akıllı işaretçiler içerir. C ++ harika ama aptallar için değil.
Pijusn

3

Gerçekten katılıyorum. Belki 1985'te var olduğu için C ++ 'dan daha az tuzak vardır.

C # C ++ 'da sadece dikkatli programlama ile önlenen tuzaklardan kaçınır mı? Eğer öyleyse, ne derece ve nasıl önlenirler?

Pek sayılmaz. Üçüncül Kural gibi kurallar, C ++ 11'de Standartlaştırılmış unique_ptrve shared_ptrStandartlaştırılmış olması nedeniyle büyük önemini yitirmiştir . Standart sınıfları belirsiz bir şekilde kullanmak "dikkatli kodlama" değil, "temel kodlama" dır. Ayrıca, manuel bellek yönetimi gibi şeyler yapmak için hala yeterince aptal, bilgisiz veya her ikisini birden yapan C ++ popülasyonunun oranı öncekinden çok daha düşüktür. Gerçek şu ki, böyle kurallar göstermek isteyen öğretim görevlileri, hala geçerli oldukları yerde örnekler bulmaya çalışmak için haftalar geçirmek zorunda kalmaktadırlar, çünkü Standart sınıflar akla gelebilecek her kullanım durumunu kapsar. Birçok Etkili C ++ tekniği aynı şekilde - dodonun yolu. Diğerlerinin birçoğu gerçekten C ++ 'a özgü değildir. Bir bakayım. İlk öğeyi atladıktan sonraki on adet:

  1. C ++ 'yı C gibi kodlamayın. Bu gerçekten sağduyu.
  2. Arabirimlerinizi kısıtlayın ve kapsülleme kullanın. OOP.
  3. İki aşamalı başlatma kodu yazarları kazıkta yakılmalıdır. OOP.
  4. Anlam semantiğinin ne olduğunu bilir. Bu gerçekten C ++ 'ya özel mi?
  5. Arayüzlerinizi tekrar kısıtlayın, bu sefer biraz farklı bir şekilde. OOP.
  6. Sanal yıkıcılar. Evet. Bu muhtemelen hala geçerlidir. finalve overridebu oyunun daha iyisi için değiştirilmesine yardımcı oldular. Yıkıcıyı yapın ve yıkıcılarını overrideyapmayan birinden miras alırsanız güzel bir derleyici hatasını garanti edersiniz virtual. Sınıfınızı yapın finalve kötü bir ovma gelmez ve sanal bir yıkıcı olmadan yanlışlıkla miras alınamaz.
  7. Temizleme işlevleri başarısız olursa kötü şeyler olur. Bu gerçekten C ++ 'a özgü değildir - hem Java hem de C # için aynı tavsiyeyi görebilirsiniz - ve hemen hemen her dil. Başarısız olabilecek temizleme işlevlerine sahip olmak sadece kötüdür ve bu öğe hakkında C ++ veya hatta OOP yoktur.
  8. Yapıcı düzeninin sanal işlevleri nasıl etkilediğinin farkında olun. Komik bir şekilde, Java'da (şimdiki veya geçmiş), yanlış yanlış C ++ davranışından daha kötü olan Derived sınıfının işlevini çağırır. Ne olursa olsun, bu sorun C ++ 'a özgü değildir.
  9. Operatör aşırı yükleri insanların beklediği gibi davranmalıdır. Gerçekten spesifik değil. Cehennem, operatöre aşırı yüklenme bile neredeyse yok, aynı herhangi bir işleve uygulanabilir - ona bir isim vermeyin ve sonra tamamen sezgisel olmayan bir şey yapmasını sağlayın.
  10. Bu aslında kötü uygulama olarak kabul edilmektedir. Tüm istisna açısından güvenli atama operatörleri kendi kendine atama ile ilgilenir ve kendi kendine atama etkili bir şekilde mantıksal bir program hatasıdır ve kendi kendine atama kontrolü performans maliyetine değmez.

Açıkçası, her bir Effective C ++ maddesinden geçmeyeceğim, ancak çoğu basit bir şekilde C ++ 'a temel kavramları uyguluyor. Aynı tavsiyeyi, herhangi bir değer-tipli nesne yönelimli yüklenebilir-operatör dilinde bulabilirsiniz. Sanal yıkıcılar bir C ++ tuzağı olan tek şeyle ilgilidir ve hala geçerlidir - tartışmasız, finalC ++ 11 sınıfı ile olduğu kadar geçerli değildir. Etkili C ++ 'ın OOP ve C ++' ın spesifik özelliklerini uygulama fikri hala çok yeni olduğunda yazılmıştır. Bu öğeler, C ++ 'ın tuzakları ve C'deki değişiklikle nasıl başa çıkılacağı ve OOP'un nasıl doğru kullanılacağı hakkında daha fazla değildir.

Düzenleme: C ++ tuzakları tuzakları gibi şeyler içermez malloc. Yani, biri için güvenli olmayan C # kodunda eşit olarak bulabileceğiniz C kodunda bulabileceğiniz her bir tuzak, yani bu özellikle alakalı değil ve ikincisi, Standart'ın birlikte çalışabilmesi için tanımladığı için onu kullanmanın C ++ olduğu düşünülmediği anlamına gelmez. kodu. Standart da tanımlıyor goto, ama onu kullanarak dev bir spagetti karışıklığı yığını yazacak olsaydınız, dilin değil probleminizin olduğunu düşünürdüm. "Dikkatli kodlama" ve "Dilin temel deyimlerini takip etme" arasında büyük bir fark vardır.

C # 'da yeni bir C # programcısının bilmesi gereken yeni, farklı tuzaklar var mı? Eğer öyleyse, neden C # tasarımından kaçınılamadı?

usingberbat. Gerçekten öyle. Ve neden daha iyi bir şey yapılmadığı hakkında hiçbir fikrim yok. Ayrıca, Base[] = Derived[]orijinal tasarımcıların şablonların C ++ 'da olduğu büyük başarıyı fark etmedikleri için var olan Object'in hemen hemen her kullanımı ve "Her şeyin her şeyden miras alalım ve tüm tip güvenliğimizi kaybedelim" kararının daha akıllıca bir seçim olduğuna karar verdi. . Ayrıca delegelerle yarış koşulları ve diğer eğlenceli şeyler gibi kötü sürprizler bulabileceğinize inanıyorum. Daha sonra, jeneriklerin şablonlara kıyasla nasıl korkunç bir şekilde emdiği, genel olarak gerçekten her şeyin a class.


5
Eğitimli bir kullanıcı tabanı veya yeni yapılar halatı gerçekten azaltmıyor. Onlar sadece iş başındalar, bu yüzden daha az insan asılıyor. Her şey Etkili C ++ ve dilin evrimindeki bağlamı hakkında iyi bir yorum olsa da.
Telastyn

2
Hayır. Etkili C ++ 'daki bir grup öğenin, herhangi bir değer türü nesne yönelimli dilde eşit olarak uygulanabilecek kavramlar olmasıyla ilgilidir. Ve C yerine gerçek C ++ kodlamak için kullanıcı tabanını eğitmek kesinlikle C ++ 'nin verdiği ipi azaltır. Ayrıca, yeni dil yapıları beklenebilir olan ipi azalan. C ++ Standardı'nın tanımlaması malloc, bunu yapmanız gerektiği anlamına gelmez, sadece gotobir orospu gibi fahişe yapabileceğinizden, kendinizi asabileceğiniz ip anlamına gelir.
DeadMG

2
C ++ 'ın C bölümlerini kullanmak, tüm kodunuzu unsafeC #' da yazmaktan farklı değildir . İsterseniz C # gibi C # kodlamasının tüm tuzaklarını da listeleyebilirim.
DeadMG

@DeadMG: gerçekten soru "bir C ++ programcısı bir C programcısı olduğu sürece kendini asmak için yeterli ipi olmalıdır" olmalıdır
gbjbaanb

"Artı, hala yeterince aptal, bilgisiz veya her ikisinin de manuel bellek yönetimi gibi şeyler yapmak için olan C ++ popülasyonunun oranı öncekinden çok daha düşük." Kaynak belirtilmeli.
dan04

3

C # C ++ 'da sadece dikkatli programlama ile önlenen tuzaklardan kaçınır mı? Eğer öyleyse, ne derece ve nasıl önlenirler?

C # avantajlarına sahiptir:

  • C ile geriye doğru uyumlu olmama, böylece sözdizimsel olarak uygun ancak şimdi kötü stil olarak kabul edilen uzun bir "kötü" dil özellikleri listesine (örneğin, ham işaretçiler) sahip olmaktan kaçınmak.
  • Etkili C ++ öğelerinin en az 10unu tartışan (ancak yeni tuzaklar getiren) değer semantiği yerine referans semantiğe sahip olmak .
  • C ++ 'dan daha az uygulama tanımlı davranışa sahip olmak.
    • Özellikle, C ++ karakteri kodlayan char, stringvs uygulama-tanımlanmıştır. Unicode'a Windows yaklaşımı ( eski "kod sayfaları" wchar_tiçin UTF-16 chariçin) ve * nix yaklaşımı (UTF-8) arasındaki şema, platformlar arası kodda büyük zorluklara neden olur. C #, OTOH, a'nın stringUTF-16 olduğunu garanti eder .

C # 'da yeni bir C # programcısının bilmesi gereken yeni, farklı tuzaklar var mı?

Evet: IDisposable

C # için "Etkili C ++" eşdeğer bir kitap var mı?

Yapısında Etkili C ++ 'a benzeyen Effective C # adlı bir kitap var .


0

Hayır, C # (ve Java) C ++ 'tan daha az güvenlidir

C ++ yerel olarak doğrulanabilir . Tek bir sınıf C ++ incelemek ve sınıf başvurulan tüm sınıflar doğru olduğunu varsayarak, bellek veya diğer kaynakları sızıntı olmadığını belirlemek. Java veya C # 'da, bir tür sonlandırma gerektirip gerektirmediğini belirlemek için referans verilen her sınıfı kontrol etmek gerekir.

C ++:

{
   some_resource r(...);  // resource initialized
   ...
}  // resource destructor called, no leaks here

C #:

{
   SomeResource r = new SomeResource(...); // resource initialized
   ...
} // did I need to finalize that?  May I should have used 'using' 
  // (or in Java, a grotesque try/finally construct)?  No way to tell
  // without checking the documentation for SomeResource

C ++:

{
    auto_ptr<SomeInterface> i = SomeFactory.create(...);
    i->f(...);
} // automatic finalization and memory release.  A new implementation of
  // SomeInterface can allocate and free resources with no impact
  // on existing code

C #:

{
   SomeInterface i = SomeFactory.create(...);
   i.f(...);
   ...
} // Sure hope someone didn't create an implementation of SomeInterface
  // that requires finalization.  In C# and Java it is necessary to decide whether
  // any implementation could require finalization when the interface is defined.
  // If the initial decision is 'no finalization', then no future implementation  
  // can acquire any resource without creating potential leaks in existing code.

3
... modern IDE'lerde bir şeyin IDisposable'dan miras alıp almadığını belirlemek oldukça önemsizdir. Asıl sorun ihtiyaç vardır biliyorum kullanımına auto_ptr(veya akrabası birkaç). Meşhur ip budur.
Telastyn

2
@Telastyn hayır, asıl önemli olan, bir tanesine ihtiyacınız olmadığını gerçekten bilmediğiniz sürece her zaman akıllı bir işaretçi kullanmanızdır. C # 'da using deyimi, atıfta bulunduğunuz ip gibidir. (yani C ++ 'da akıllı bir işaretçi kullanmayı hatırlamak zorundasınız, o zaman neden her zaman bir kullanım ifadesi kullanmayı hatırlamanıza rağmen C # kadar kötü değil)
gbjbaanb

1
@gbjbaanb Çünkü ne? C # sınıflarının en fazla% 5'i atılabilir mi? Ve biliyorum onlar tek kullanımlık iseniz bunları bertaraf etmek gerekir. C ++ 'da her nesne tek kullanımlıktır. Ve özel vakanızın ele alınması gerekip gerekmediğini bilmiyorsunuz . Bir fabrikadan alınmayan iadeler için ne olur? Onları temizlemek sizin sorumluluğunuzda mı? Bu olmamalı , ama bazen öyle. Ve yine, her zaman akıllı bir işaretçi kullanmanız gerektiği anlamına gelmez. Özellikle yeni başlayanlar için bu önemli bir tuzak.
Telastyn

2
@Telastyn: Kullanmayı bilmek, arayüzleri kullanmayı bilmek ya da kullanmayı bilmek auto_ptrkadar basittir IEnumerableya da para birimi ya da bunun için kayan nokta kullanmayın. DRY'nin temel bir uygulamasıdır. Nasıl programlanacağının temellerini bilen kimse bu hatayı yapmaz. Aksine using. Sorun, her sınıf için Tek Kullanımlık olup olmadığını usingbilmeniz gerektiğidir (ve umarım asla, asla değişmez) ve Tek Kullanımlık değilse, Tek Kullanımlık olması gereken tüm türetilmiş sınıfları otomatik olarak yasaklarsınız.
DeadMG

2
kevin: Ah, cevabın hiçbir anlam ifade etmiyor. Yanlış yaptığınız C # 'ın hatası değildir. Sen do DEĞİL düzgün yazılı C # kodu finalizers bağlıdır . Bir Disposeyöntemi olan bir alanınız varsa, uygulamanız gerekir IDisposable('doğru' yol). Sınıfınız bunu yaparsa (C ++ 'da sınıfınız için RAII uygulamasına eşdeğerdir) ve kullanırsanız using(C ++' daki akıllı işaretçiler gibi), hepsi mükemmel çalışır. Sonlandırıcı çoğunlukla kazaları önlemek içindir - Disposedoğruluktan sorumludur ve eğer kullanmıyorsanız, bu sizin hatanızdır, C # 'değil.
user541686

0

Evet% 100 evet, hafızayı boşaltmak ve C # 'da kullanmak imkansız olduğunu düşündüğümden evet (yönetildiği varsayılarak güvensiz moda girmiyorsunuz).

Ama eğer inanılmaz sayıda insanın yapmadığı C ++ programlamayı biliyorsan. Çok iyisin. Charles Salvia sınıfları gibi, önceden varolan STL sınıflarında işlendiği gibi anılarını gerçekten yönetmezler. Nadiren işaretçiler kullanıyorum. Aslında tek bir işaretçi kullanmadan projelere gittim. (C ++ 11 bunu kolaylaştırır).

Yazım hataları, aptalca hatalar ve vb (ex: if (i=0)bc == gerçekten hızlı bir şekilde vurduğunuzda sıkışmış var) yapmak için gelince, kod kalitesini artırır gibi güzel şikayet ediyor. Diğer örnek, breakswitch deyimlerini unutmak ve bir işlevde statik değişkenler bildirmenize izin vermiyor (ki bazen sevmiyorum ama iyi bir fikir imo).


4
Java ve C #, referans eşitliği için kullanarak ve değer eşitliği için giriş yaparak =/ ==problemini daha da kötüleştirdi . Kötü programcı artık bir değişkenin 'çift' veya 'Çift' olup olmadığını takip etmek ve doğru değişkeni çağırdığınızdan emin olmak zorundadır. ==.equals
kevin cline

@kevincline +1, ancak C # 'da structbunu yapabilirsiniz ==. Kendi kodunda ben asla dizileri karşılaştırmak istediğinizde dışında bu sorunu elde. Hiç liste veya yapı olmayan türleri (dize, int, kayan nokta, DateTime, KeyValuePair ve diğerleri) karşılaştırmak

2
Python bunu ==, değer eşitliği ve isreferans eşitliği için kullanarak doğru buldu .
dan04

@ dan04 - Kaç eşitlik türü olduğunu düşünüyorsunuz C #? Mükemmel ACCU yıldırım konuşmasına bakın: Bazı nesneler diğerlerinden daha eşittir
Mark Booth
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.