14

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#?
yannis
  • 39,547
  • 40
  • 183
  • 216
alx9r
  • 289
  • 2
  • 6
  • 10
    From the [FAQ](http://programmers.stackexchange.com/faq#dontask): `Your questions should be reasonably scoped. If you can imagine an entire book that answers your question, you’re asking too much.`. I believe this qualifies as such a question... – Oded Aug 25 '12 at 20:27
  • @Oded Are you referring to the character-limited headline question? Or my 3+ more precise questions in the body of my post? – alx9r Aug 25 '12 at 20:34
  • 3
    Frankly - both the title and _each_ of the "more precise questions". – Oded Aug 25 '12 at 20:38
  • @Oded Being a n00b I almost deleted my question because of your comments. It was only happenstance that I did not -- I was interrupted, then took Sunday off. By Monday there were over 500 views, 7 answers, lots of up votes and 2 favorites. I humbly submit that immediately commenting new questions with a hypertechnical meta reason that it shouldn't have been asked actually damages the community more than it helps. If the question had been sitting for days or even hours it might have been more appropriate, but the above comments happened within minutes of the original post. – alx9r Aug 28 '12 at 15:38
  • 3
    I have started a [Meta discussion](http://meta.programmers.stackexchange.com/questions/3959/summer-of-love-and-questions-that-the-faq-says-should-not-be-asked) about this question. – Oded Aug 30 '12 at 20:06
  • @Oded Great! That's probably a good way to make the best of this. – alx9r Aug 30 '12 at 21:51
  • 1
    Regarding your now deleted 3rd question, Bill Wagner's [Effective C# series (now 3 books)](http://www.amazon.com/Bill-Wagner/e/B001H6WAE0/ref=ntt_athr_dp_pel_1) taught me more about programming C# well than anything else I read on the subject. Martins review of EC# is right in that it can never be a direct replacement for Effective C++, but he is wrong thinking that it should be. Once you no longer have to worry about the *easy* mistakes, you have to move on to *harder* mistakes. – Mark Booth Sep 03 '12 at 13:03

7 Answers7

33

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.

user541686
  • 8,074
  • 8
  • 38
  • 49
  • All undefined stuff is really defined by the implementation, so if you overflow an unsigned integer in Visual Studio, you'll get an exception if you've turned on the right compiler flags. Now I know this is what you're talking about, but it is not *undefined behaviour*, its just that people don't usually check for it. (same with truly undefined behaviour like operator++, it is well defined by each compiler). You could say the same with C#, only there is just 1 implementation - plenty of 'undefined behaviour' if you run in Mono - eg. https://bugzilla.xamarin.com/show_bug.cgi?id=310 – gbjbaanb Aug 26 '12 at 14:30
  • 1
    Is it really defined or only defined by whatever the current .net implementation on the current version of Windows does? Even c++ undefined behaviour is fully defined if you define it as whatever g++ does. – Martin Beckett Aug 26 '12 at 14:46
  • 6
    Overflowing unsigned integers isn't UB at all. It's overflowing *signed* integers that's UB. – DeadMG Aug 26 '12 at 15:47
  • 6
    @gbjbaanb: Like DeadMG said -- signed integer overflow **is** undefined. It's **not** implementation-defined. Those phrases have specific meanings in the C++ standard, and they aren't the same thing. Don't make that mistake. – user541686 Aug 26 '12 at 19:10
  • Undefined behavior is *part* of what makes C++ faster than "managed" languages - but the main reason C++ tends to be faster than Java and C# is because it gives you much [more control over memory layout](http://programmers.stackexchange.com/a/159380/7733 "more control over memory layout"), making it easier to take advantage of CPU cache. – Charles Salvia Aug 27 '12 at 22:03
  • 1
    @CharlesSalvia: Uh, how exactly does "C++ make it easier to take advantage of the CPU cache" than C#? And what kind of control does C++ give you over memory that you cannot have in C#? – user541686 Aug 27 '12 at 22:09
  • @Mehrdad: If a particular implementation documents particular behavior for signed-integer overflow, then the behavior will be defined *on those implementation*. The standard makes no promise about what compilers or generated code will do, but does not in any way disallow compilers from making promises on their own behalf. – supercat Feb 16 '14 at 03:55
  • @supercat: Yeah, but the whole point here is that you can't rely on it behaving in any particular way for a generic implementation. This is in contrast to unspecified/implementation-defined behavior, where you *can* assume the behavior will be one of (potentially many) well-defined possibilities. That's a pretty subtle but also pretty critical difference. – user541686 Feb 16 '14 at 04:05
  • @Mehrdad: UB vs IB is a huge difference, but if one knows the implementation one is using, things which are UB in general may not be UB on that implementation. Personally, I think the only sane way to ensure predictable math rules with various sizes of `int` will be to add types for unsigned numbers (non-wrapping) and algebraic rings (wrapping), and deprecate the old unsigned types which mostly behave as algebraic rings, but promote and compare like numbers. – supercat Feb 16 '14 at 04:40
12

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.

Telastyn
  • 108,850
  • 29
  • 239
  • 365
  • 4
    Memory leaks are a thing of the past, and so is `char*`. Not to mention that you can still leak memory in C# just fine. – DeadMG Aug 25 '12 at 23:34
  • 2
    Calling templates "glorified string pasting" is a bit much. Templates are really one of the best features of C++. – Charles Salvia Aug 25 '12 at 23:36
  • 1
    Templates are much less glorified string pasting than generics. – DeadMG Aug 25 '12 at 23:40
  • 2
    @CharlesSalvia Sure, they are _the_ really distinguishing feature of C++. And yes, that's perhaps an oversimplification for compilation impact. But they do disproportionally impact compile times and output size, especially if you're not careful. – Telastyn Aug 25 '12 at 23:47
  • They absolutely are- and you can do a lot more with them than those awful weak generics. – DeadMG Aug 26 '12 at 00:03
  • 2
    @deadMG certainly, though I would argue that many of the template meta-programming tricks used/needed in C++ are... better implemented via a different mechanism. – Telastyn Aug 26 '12 at 00:05
  • Unless you want to run a full semantic analysis on your whole codebase, it's not often that feasible to use other tools. – DeadMG Aug 26 '12 at 00:08
  • @DeadMG - bah. You're telling me type traits aren't much more nicely provided in a language with dynamic dispatch, or even dynamically typed? – Telastyn Aug 26 '12 at 00:11
  • 2
    @Telastyn the whole point of type_traits is to get type information at *compile time* so you can use this information to do things like specialize templates or overload functions in specific ways using `enable_if` – Charles Salvia Aug 26 '12 at 00:12
  • Yeah, I don't think you'll get the same effect getting type information dynamically as getting it statically. The whole point is to dispatch statically. More relevantly, `type_traits` are more of a beneficial side effect rather than the primary intention. If C++ were designed from scratch again, they would be provided much neater. – DeadMG Aug 26 '12 at 00:14
  • Fair enough, let me edit that red herring out then. – Telastyn Aug 26 '12 at 00:34
  • C# 5 prints 0..9, doesn't it? – fredoverflow Aug 26 '12 at 15:09
  • @FredOverflow No. Variable capture rules forbid that behavior, since it would break other more intuitive behaviors. – Telastyn Aug 26 '12 at 16:23
  • 2
    [Are you sure](http://stackoverflow.com/a/8899347/252000)? – fredoverflow Aug 26 '12 at 16:53
  • @FredOverflow that is an awesome link. I'd upvote you more if I could. Based on the link, it seems as though `for` isn't being fixed, so the above still has the odd behavior. – Telastyn Aug 26 '12 at 17:07
10

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++.

Charles Salvia
  • 7,342
  • 1
  • 35
  • 33
  • It was the rule of three in C++03 and is rule of four in C++11 now. – DeadMG Aug 25 '12 at 23:40
  • 1
    You could call it "Rule of 5" for copy constructor, move constructor, copy assignment, move assignment, and destructor. But move semantics aren't always necessary just for proper resource management. – Charles Salvia Aug 25 '12 at 23:56
  • You don't need separate move and copy assignment. Copy-and-swap idiom can do both operators in one. – DeadMG Aug 26 '12 at 00:02
  • While this is a very interesting overview of the problems, or lack thereof, of memory management in C++, it doesn't actually answer the OP's *C#* question. – Avner Shahar-Kashtan Aug 26 '12 at 04:29
  • 2
    It answers the question `Does C# avoid pitfalls that are avoided in C++ only by careful programming?`. The answer is "not really, because it's trivially easy to avoid the pitfalls Joel was talking about in modern C++" – Charles Salvia Aug 26 '12 at 04:34
  • I think that if you have an idea about what is the design of an application, what the boundaries of your application are supposed to be in terms of semantics and syntax, you will realize that C# is one the popular "modern" solution for people that just want pre-packed design solutions, that's why C# is "easier" to manage, because not only offer an high level approach but also some design solutions ready to be used or to be integrated in a bigger project. – user827992 Aug 26 '12 at 05:03
  • 1
    IMO, while high-level languages like C# or Java provide you with memory management and other stuff that is supposed to *help* you, it doesn't always do as supposed. You still end up looking after your code design so you leave no *memory leak* (which is not exactly what you would call in C++). From my experience, I find it EVEN easier to manage memory in C++ because you know that destructors will be called and in most cases they do the clean up. After all, C++ has smart pointers for cases when design does not allow for efficient memory management. C++ is great but not for dummies. – Pijusn Aug 31 '12 at 05:11
  • @Pius, I agree. I usually find programming in C++ just as productive as programming in Java, and I really love RAII. C++ has come a long way and the alleged "dangers" mentioned by Joel in the OP are basically anachronistic at this point. That said, I do have to admit that @Mehrdad is correct that one of the main dangers of programming in C++ is undefined behavior. Consider something like accidentally using a `vector::iterator` after it's been invalidated by a `resize` or `insert`. This sort of subtle cause of UB never happens in C# or Java, because you can never have a dangling reference. – Charles Salvia Aug 31 '12 at 15:55
3

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.

DeadMG
  • 36,794
  • 8
  • 70
  • 139
  • 5
    An educated userbase or new constructs aren't really diminishing the rope though. They're just work-arounds so less people end up hung. Though this is all good commentary on Effective C++ and its context in the language's evolution. – Telastyn Aug 26 '12 at 00:47
  • 3
    No. It's about how a bunch of the items in Effective C++ are concepts that could equally apply to any value-typed object-orientated language. And educating the userbase to code actual C++ instead of C is definitely diminishing the rope that C++ gives you. Also, I would expect that new language constructs *are* diminishing the rope. It's about how just because the C++ Standard defines `malloc` does not mean that you should do it, any more than just because you can whore `goto` like a bitch means that it's rope you can hang yourself with. – DeadMG Aug 26 '12 at 09:42
  • 3
    Using the C parts of C++ is no different to writing all your code `unsafe` in C#, which is just as bad. I could list every pitfall of coding C# like C as well, if you'd like. – DeadMG Aug 26 '12 at 09:43
  • @DeadMG : so really the question should be "a C++ programmer has enough rope to hang himself as long as he is a C programmer" – gbjbaanb Aug 26 '12 at 14:08
  • "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." Citation needed. – dan04 Aug 27 '12 at 06:24
3

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++.

dan04
  • 3,748
  • 1
  • 24
  • 26
1

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.
kevin cline
  • 33,608
  • 3
  • 71
  • 142
  • 3
    ... it is pretty trivial in modern IDEs to determine if something inherits from IDisposable. The main problem is that you need to _know_ to use `auto_ptr` (or a few of its kin). That is the proverbial rope. – Telastyn Aug 26 '12 at 14:05
  • 3
    @Telastyn no, the point is you always use a smart pointer, unless you really know that you don't need one. In C# the using statement is just like the rope you're referring to. (ie in C++ you have to remember to use a smart pointer, why then is C# not as bad even though you have to remember to always use a using statement) – gbjbaanb Aug 26 '12 at 14:13
  • 1
    @gbjbaanb Because the what? 5% at most of C# classes are disposable? And you _know_ you need to dispose them if they're disposable. In C++, every single object is disposable. And you don't _know_ if your particular instance needs to be dealt with. What happens for pointers being returned that aren't from a factory? Is it your responsibility to clean them up? It **shouldn't** be, but sometimes it is. And again, just because you should always use a smart pointer doesn't mean that the option not to ceases to exist. For beginners especially, this is a significant pitfall. – Telastyn Aug 26 '12 at 14:51
  • 3
    @Telastyn: Knowing to use `auto_ptr` is as simple as knowing to use `IEnumerable` or knowing to use interfaces, or don't use floating-point for currency or such. It's a basic application of DRY. Nobody who knows the basics of how to program would make that mistake. Unlike `using`. The problem with `using` is that you have to know *for every class* whether or not it's Disposable (and I hope that never, ever changes), and if it's not Disposable, you automatically ban all derived classes that might have to be Disposable. – DeadMG Aug 26 '12 at 15:46
  • 1
    @deadmg - except for the decade plus of books and code that have no idea what `auto_ptr` is. Look, it's been maybe 3 years since I did C++ programming professionally but at that time, the _vast_ majority of programmers had no idea that smart pointers existed. Maybe it's suddenly gotten better, or that my experiences have been atypical. Consider me skeptical. – Telastyn Aug 26 '12 at 16:21
  • I agree the `using` keyword is stupid and unnecessary in C# (It CAN be done automatically) but did you seriously say C# is less safe? Unbelievable. –  Aug 26 '12 at 17:55
  • More importantly, even if *you* know how to use smart pointers, that doesn't mean that the guys who wrote most of your codebase 10 years ago did. – dan04 Aug 26 '12 at 20:27
  • 2
    kevin: Uh, your answer makes no sense. It's not C#'s fault that you're doing it wrong. You *do **NOT** depend on finalizers in properly written C# code*. If you have a field that has a `Dispose` method, you must implement `IDisposable` (the 'proper' way). If your class does that (which is the equivalent of implementing RAII for your class in C++), and you use `using` (which is like the smart pointers in C++), it all works perfectly. The finalizer is mostly meant to prevent accidents -- `Dispose` is responsible for correctness, and if you're not using it, well, that's your fault, not C#'s. – user541686 Aug 26 '12 at 20:51
  • 1
    @dan04: The uniformity of C++ destructors makes it easier to write correct C++ code, and more importantly, it makes it easier to review C++ code. In C# or Java, every call to new could potentially leak a resource. – kevin cline Aug 26 '12 at 23:52
  • You don't even need to use dispose... If you dont dispose something (say a file) you just dont release it. And disposed is only used when using unmanaged resources and C# is managed... –  Aug 27 '12 at 00:50
0

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
    Java and C# made the `=`/`==` problem even worse by using `==` for reference equality and introducing `.equals` for value equality. The poor programmer now has to keep track of whether an variable is 'double' or 'Double' and be sure to call the right variant. – kevin cline Aug 26 '12 at 14:08
  • @kevincline +1 but in C# `struct` you can do `==` which works incredibly well since the majority of the time one would only have strings, ints and floats (ie only struct members). In my own code i never get that problem except when i want to compare arrays. I don't think i ever compare list or non struct types (string,int,float,DateTime,KeyValuePair and many others) –  Aug 26 '12 at 17:37
  • 2
    Python got it right by using `==` for value equality and `is` for reference equality. – dan04 Aug 27 '12 at 02:30
  • @dan04 - How many types of equality do you think C# has? See the excellent ACCU lightning talk: [Some objects are more equal than others](http://www.howzatt.demon.co.uk/articles/equals.pdf) – Mark Booth Sep 03 '12 at 13:12