Does C# give you “less rope to hang yourself” than C++? [closed]

Joel Spolsky characterized C++ as “enough rope to hang yourself”. Actually, he was summarizing “Effective C++” by Scott Meyers:

It’s a book that basically says, C++ is enough rope to hang yourself, and then a couple of extra miles of rope, and then a couple of suicide pills that are disguised as M&Ms…

I don’t have a copy of the book, but there are indications that much of the book relates to pitfalls of managing memory which seem like would be rendered moot in C# because the runtime manages those issues for you.

Here are my questions:

  1. Does C# avoid pitfalls that are avoided in C++ only by careful programming? If so, to what degree and how are they avoided?
  2. Are there new, different pitfalls in C# that a new C# programmer should be aware of? If so, why couldn’t they be avoided by the design of C#?

7

The fundamental difference between C++ and C# stems from undefined behavior.

It has nothing to do with doing manual memory management. In both cases, that is a solved problem.

C/C++:

In C++, when you make a mistake, the result is undefined.
Or, if you try to make certain kinds of assumptions about the system (e.g. signed integer overflow), chances are that your program will be undefined.

Maybe read this 3-part series on undefined behavior.

This is what makes C++ so fast — the compiler doesn’t have to worry about what happens when things go wrong, so it can avoid checking for correctness.

C#, Java, etc.

In C#, you’re guaranteed that many mistakes will blow up in your face as exceptions, and you’re guaranteed a lot more about the underlying system.
That is a fundamental barrier to making C# as fast as C++, but it’s also a fundamental barrier to making C++ safe, and it makes C# easier to work with and debug.

Everything else is just gravy.

9

Does C# avoid pitfalls that are avoided in C++ only by careful programming? If so, to what degree and how are they avoided?

Most it does, some it doesn’t. And of course, it makes some new ones.

  1. Undefined behavior – The biggest pitfall with C++ is that there’s a whole lot of the language that is undefined. The compiler can literally blow up the universe when you do these things, and it will be okay. Naturally, this is uncommon, but it is pretty common for your program to work fine on one machine and for really no good reason not work on another. Or worse, subtly act different. C# has a few cases of undefined behavior in its specification, but they’re rare, and in areas of the language that are infrequently traveled. C++ has the possibility of running into undefined behavior every time you make a statement.

  2. Memory Leaks – This is less of a concern for modern C++, but for beginners and during about half of its lifetime, C++ made it super easy to leak memory. Effective C++ came right around the evolution of practices to eliminate this concern. That said, C# can still leak memory. The most common case people run into is event capture. If you have an object, and put one of its methods as a handler to an event, the owner of that event needs to be GC’d for the object to die. Most beginners don’t realize that event handler counts as a reference. There’s also issues with not disposing disposable resources that can leak memory, but these are not nearly as common as pointers in pre-Effective C++.

  3. Compilation – C++ has a retarded compilation model. This leads to a number of tricks to play nice with it, and keep compile times down.

  4. Strings – Modern C++ makes this a little better, but char* is responsible for ~95% of all security breaches before the year 2000. For experienced programmers, they’ll focus on std::string, but it’s still something to avoid and a problem in older/worse libraries. And that’s praying that you don’t need unicode support.

And really, that’s the tip of the iceberg. The main issue is that C++ is a very poor language for beginners. It is fairly inconsistent, and many of the old really, really bad pitfalls have been dealt with by changing the idioms. The problem is that beginners then need to learn the idioms from something like Effective C++. C# eliminates a lot of these problems altogether, and makes the rest less of a concern until you get further along the learning path.

Are there new, different pitfalls in C# that a new C# programmer should be aware of? If so, why couldn’t theybe avoided by the design of C#?

I mentioned the event “memory leak” issue. This isn’t a language problem so much as the programmer expecting something that the language can’t do.

Another is that the finalizer for a C# object is not technically guaranteed to be run by the runtime. This doesn’t usually matter, but it does cause some things to be designed differently than you might expect.

Another semi-pitfall I’ve seen programmers run into is the capture semantics of anonymous functions. When you capture a variable, you capture the variable. Example:

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

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

Doesn’t do what naively is thought. This prints 10 10 times.

I’m sure there’s a number of others I am forgetting, but the main issue is that they’re less pervasive.

15

In my opinion, the dangers of C++ are somewhat exaggerated.

The essential danger is this: While C# lets you perform “unsafe” pointer operations using the unsafe keyword, C++ (being mostly a superset of C) will let you use pointers whenever you feel like it. Besides the usual dangers inherent with using pointers (which are the same with C), like memory-leaks, buffer overflows, dangling pointers, etc., C++ introduces new ways for you to seriously screw things up.

This “extra rope”, so to speak, which Joel Spolsky was talking about, basically comes down to one thing: writing classes which internally manage their own memory, also known as the “Rule of 3” (which can now be called the Rule of 4 or Rule of 5 in C++11). This means, if you ever want to write a class that manages its own memory allocations internally, you have to know what you’re doing or else your program will likely crash. You have to carefully create a constructor, copy constructor, destructor, and assignment operator, which is surprisingly easy to get wrong, often resulting in bizarre crashes at runtime.

HOWEVER, in actual every-day C++ programming, it’s very rare indeed to write a class that manages its own memory, so it’s misleading to say that C++ programmers always need to be “careful” to avoid these pitfalls. Usually, you’ll just be doing something more like:

class Foo
{
    public:

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

    private:

    std::string m_first_name;
};

This class looks pretty close to what you’d do in Java or C# – it requires no explicit memory management (because the library class std::string takes care of all that automatically), and no “Rule of 3” stuff is required at all since the default copy constructor and assignment operator is fine.

It’s only when you try to do something like:

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

In this case, it can be tricky for novices to get the assignment, destructor and copy constructor correct. But for most cases, there’s no reason to ever do this. C++ makes it very easy to avoid manual memory management 99% of the time by using library classes like std::string and std::vector.

Another related issue is manually managing memory in a way that doesn’t take into account the possibility of an exception being thrown. Like:

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

If some_function_which_may_throw() actually does throw an exception, you’re left with a memory leak because the memory allocated for s will never be reclaimed. But again, in practice this is hardly an issue any more for the same reason that the “Rule of 3” isn’t really much of a problem anymore. It’s very rare (and usually unnecessary) to actually manage your own memory with raw pointers. To avoid the above problem, all you’d need to do is use an std::string or std::vector, and the destructor would automatically get invoked during stack unwinding after the exception was thrown.

So, a general theme here is that many C++ features which were not inherited from C, such as automatic initialization/destruction, copy constructors, and exceptions, force a programmer to be extra careful when doing manual memory management in C++. But again, this is only a problem if you intend to do manual memory management in the first place, which is hardly ever necessary anymore when you have standard containers and smart pointers.

So, in my opinion, while C++ gives you a lot of extra rope, it’s hardly ever necessary to use it to hang yourself, and the pitfalls which Joel was talking about are trivially easy to avoid in modern C++.

8

I wouldn’t really agree. Maybe less pitfalls than C++ as it existed in 1985.

Does C# avoid pitfalls that are avoided in C++ only by careful
programming? If so, to what degree and how are they avoided?

Not really. Rules like the Rule of Three have lost massive significance in C++11 thanks to unique_ptr and shared_ptr being Standardised. Using the Standard classes in a vaguely sensible fashion isn’t “careful coding”, it’s “basic coding”. Plus, the proportion of the C++ population who are still sufficiently stupid, uninformed, or both to do things like manual memory management is a lot lower than before. The reality is that lecturers who wish to demonstrate rules like that have to spend weeks trying to find examples where they still apply, because the Standard classes cover virtually every use case imaginable. Many Effective C++ techniques have gone the same way- the way of the dodo. Lots of the others aren’t really that C++ specific. Let me see. Skipping the first item, the next ten are:

  1. Don’t code C++ like it’s C. This is really just common sense.
  2. Restrict your interfaces and use encapsulation. OOP.
  3. Two-phase initialization code writers should be burned at the stake. OOP.
  4. Know what value semantics are. Is this really C++ specific?
  5. Restrict your interfaces again, this time in a slightly different way. OOP.
  6. Virtual destructors. Yeah. This one is probably still valid- somewhat. final and override have helped change this particular game for the better. Make your destructor override and you guarantee a nice compiler error if you inherit from someone who didn’t make their destructor virtual. Make your class final and no poor scrub can come along and inherit from it accidentally without a virtual destructor.
  7. Bad things happen if cleanup functions fail. This isn’t really specific to C++- you can see the same advice for both Java and C#- and, well, pretty much every language. Having cleanup functions that can fail is just plain bad and there’s nothing C++ or even OOP about this item.
  8. Be aware of how constructor order influences virtual functions. Hilariously, in Java (either current or past) it would simply incorrectly call the Derived class’s function, which is even worse than C++’s behaviour. Regardless, this issue is not specific to C++.
  9. Operator overloads should behave as people expect. Not really specific. Hell, it’s hardly even operator overloading specific, the same could be applied to any function- don’t give it one name and then make it do something completely unintuitive.
  10. This is actually now considered bad practice. All strongly-exception-safe assignment operators deal with self-assignment just fine, and self-assignment is effectively a logical program error, and checking for self-assignment just isn’t worth the performance cost.

Obviously I’m not going to go through every single Effective C++ item, but most of them are simply applying basic concepts to C++. You would find the same advice in any value-typed object-orientated overloadable-operator language. Virtual destructors is about the only one that’s a C++ pitfall and is still valid- although, arguably, with the final class of C++11, it’s not as valid as it was. Remember that Effective C++ was written when the idea of applying OOP, and C++’s specific features, was still very new. These items are hardly about C++’s pitfalls and more about how to cope with the change from C and how to use OOP correctly.

Edit: The pitfalls of C++ do not include things like the pitfalls of malloc. I mean, for one, every single pitfall you can find in C code you can equally find in unsafe C# code, so that’s not particularly relevant, and secondly, just because the Standard defines it for interoperation does not mean that using it is considered C++ code. The Standard defines goto as well, but if you were to write a giant pile of spaghetti mess using it, I’d consider that your problem, not the language’s. There’s a big difference between “careful coding” and “Following basic idioms of the language”.

Are there new, different pitfalls in C# that a new C# programmer
should be aware of? If so, why couldn’t theybe avoided by the design
of C#?

using sucks. It really does. And I have no idea why something better wasn’t done. Also, Base[] = Derived[] and pretty much every use of Object, which exists because the original designers failed to notice the massive success that templates were in C++, and decided that “Let’s just have everything inherit from everything and lose all our type safety” was the smarter choice. I also believe that you can find some nasty surprises in things like race conditions with delegates, and other such fun. Then there’s other general stuff, like how generics suck horrifically in comparison to templates, the really really unnecessary enforced placing of everything in a class, and such things.

5

Does C# avoid pitfalls that are avoided in C++ only by careful
programming? If so, to what degree and how are they avoided?

C# has the advantages of:

  • Not being backwards-compatible with C, thus avoiding having a long list of “evil” language features (e.g., raw pointers) that are syntactically convenient but now considered bad style.
  • Having reference semantics instead of value semantics, which makes at least 10 of the Effective C++ items moot (but introduces new pitfalls).
  • Having less implementation-defined behavior than C++.
    • In particular, in C++ the character encoding of char, string, etc. is implementation-defined. The schism between the Windows approach to Unicode (wchar_t for UTF-16, char for obsolete “code pages”) and the *nix approach (UTF-8) causes great difficulties in cross-platform code. C#, OTOH, guarantees that a string is UTF-16.

Are there new, different pitfalls in C# that a new C# programmer
should be aware of?

Yes: IDisposable

Is there an equivalent book to “Effective C++” for C#?

There’s a book called Effective C# which is similar in structure to Effective C++.

No, C# (and Java) are less safe than C++

C++ is locally verifiable. I can inspect a single class in C++ and determine that the class does not leak memory or other resources, assuming that all the referenced classes are correct. In Java or C#, it is necessary to check every referenced class to determine if it requires finalization of some sort.

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.

10

Yes 100% yes as i think its impossible to free memory and use it in C# (assuming its managed and you don’t go into unsafe mode).

But if you know how to program in C++ which an unbelievable number of people don’t. You’re pretty much fine. Like Charles Salvia classes don’t really manage their memories as it all is handled in preexisting STL classes. I rarely use pointers. In fact i went projects without using a single pointer. (C++11 makes this easier).

As for making typos, silly mistakes and etc (ex: if (i=0) bc the key got stuck when you hit == really quickly) the compiler complains which is nice as it improves quality of code. Other example are forgetting break in switch statements and not allowing you to declare static variables in a function (which i dislike sometimes but is a good idea imo).

4

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật