
Popular Myths about C++, Part 2 - ingve
http://isocpp.org/blog/2014/12/myths-2
======
mikeash
Really, "For reliable software, you need Garbage Collection" is the straw man
you're going to attack?

What comes to mind when you hear "reliable software"? Personally, I
immediately think of critical embedded systems like rocket and spacecraft
guidance and automotive control systems. And one thing I hear repeatedly is
that many well-known coding standards for building such critical software
prohibit _all_ dynamic memory allocation, because dynamic allocation is a
potential failure point.

And of course, if you don't have dynamic allocation, you don't need garbage
collection, because it would have nothing to do. So if the software that needs
reliability the most often doesn't use dynamic allocation, only the most
ignorant could think that you need garbage collection for reliable software.

~~~
blub
All software can be reliable of course, even if it's not embedded software.
Not leaking memory at every step, avoiding double freeing of pointers or other
memory-related errors tends to make software more reliable...

Why so upset if I may ask? I think Apple declared GCs "bad" anyway.

~~~
mikeash
I'm upset because a leading figure of the programming community who a _lot_ of
people listen to is spouting nonsense, and lots of people are going to believe
it.

Why do you think I'd care what Apple says about garbage collection...?

~~~
blub
But it's not nonsense, realiability isn't all or nothing, and GC has the
effect that it completely removes a class of problems that happen often in
languages like C or C++ when doing manual memory management. It is making
those programs more reliable, even if they aren't as bug-free as your typical
spacecraft software.

That was just a joke with Apple. :)

~~~
mikeash
I'm not arguing against GC or the reliability thereof. I'm merely arguing that
"GC is required for reliability" is a ridiculous straw man of a myth. I am in
fact a fan of GC, but I also recognize that when lives are on the line, GC and
indeed dynamic memory management of any type is usually out of the picture.

~~~
mark-r
The reason this isn't a straw man is that it's a true belief held by many
people; most of us aren't exposed to the extremes of reliability that you're
talking about. When lives _aren 't_ on the line, memory management is
generally a necessity.

~~~
mikeash
Who actually holds this belief, that garbage collection is a _requirement_ for
reliable software? Most people don't work with such software but surely
everyone who even thinks about programming is aware that it exists. I mean,
you don't have to dive deep into computing to know that cars are full of
computers and software these days.

~~~
AnimalMuppet
Keep reading HN. You'll see articles and comments that state (or imply) that
normal humans can't handle memory management, and therefore GC is the way to
avoid bugs and memory leaks.

Sorry, I can't give you references off the top of my head, but I've seen that
stated, here, in the last couple of months at least.

~~~
72deluxe
I get this feeling too, and I wonder if it is from a large vocal group of
people who have never ever developed anything in C or C++, yet appear to know
all about how "dangerous" it is and enjoy mocking the languages, whilst
pointing to niche languages with much smaller development tool ecosystems and
support libraries.

Is it just me?

------
tinco
Here's a good myth:

    
    
        "Only program in C++ if you absolutely have no other option."
    

I believe in this myth, I also am currently working on a project in C++ and
reading one of Strousup's books. I think even Strousup shares this belief, but
he would probably phrase it a bit differently. (My project is interfacing with
the Unreal Engine by Epic that exposes a C++ API.)

Just putting this out there for the discussion's sake, perhaps someone could
engage on it. I recently discovered to my horror that Apache Mesos is written
in C++. They use the nice futuristic style Strousup would probably applaud
though, still I think any other practical programming language would have been
a better fit since it's just a sysadmin tool. Obviously they disagree.

~~~
pyrois
Depends on how strongly you mean "no other option". For example, is Rust
another option? I'm not sure how comfortable I am writing for a language with
a version of "0.12.0". My understanding is that they make breaking changes
fairly frequently (at least compared to the religious mania that is C++'s
approach to backwards-compatibility). D seems like an option. Go, maybe,
depending on what you're doing.

Here's my standard example for when I would happily choose C++: I am writing
an image processing library that I want to be able to run on both the PC and
embedded platforms (e.g. one of TI's DSPs). I also want to be able to write
low-cost bindings in python, ruby, etc. There are other options, but I think
C++ is the best one in this case.

~~~
Dewie
> Depends on how strongly you mean "no other option". For example, is Rust
> another option?

What _I_ tend to think people mean when they say that, is that you have some
cycles to "waste" on a simpler - while also being high level - language.

> Here's my standard example for when I would happily choose C++:

I think the standard examples are certain application-level programs.

~~~
k__
> What I tend to think people mean when they say that, is that you have some
> cycles to "waste" on a simpler language.

Isn't C simpler than C++ and doesn't waste cycles?

The only "no other option" scenario I could imagine is, legacy code in C++ or
using a framework that needs C++.

~~~
maxlybbert
> Isn't C simpler than C++ and doesn't waste cycles?

I gave a couple of C++ books to a recent CS graduate. She assured me that C++
would be easy to pick up because it has a much smaller standard library than
Java. I smiled but didn't say any anything.

Yes, C is simpler than C++. But that simplicity comes with a cost, mainly that
the programmer has to keep track of more details. And the C standard library
is smaller than the C++ standard library (and that would be true even if the
C++ standard library didn't include the C standard library).

------
debacle
I feel like it's a bit grasping if you need such a complex example to debunk a
straw man.

Implicit garbage collection is about the ease of writing code, not
reliability. You make the compiler work for you by not making what you want
explicit. Yes, for bad programmers, this leads to better code, but that
doesn't mean you don't benefit from implicit garbage collection if you are a
good programmer.

~~~
WhitneyLand
>>garbage collection is about the ease of writing code, not reliability

No, it is about reliability. You are confusing not always
necessary/perfect/optimal with average case reliability. On average, automatic
memory management has less bugs because certain classes of mistakes can be
entirely eliminated.

Also this argument of good programmers don't have to rely on X as a crutch is
more about pride than productivity. In the real world, most teams have code
touched by developers at a variety of skill sets and everyone's contributions
affect quality of code so tools should be measured across levels of expertise.

If you look at the highly cited research on garbage collectors I believe
you'll find most of it concurs as to the benefits, edge cases not
withstanding.

~~~
ijk
> Also this argument of good programmers don't have to rely on X as a crutch
> is more about pride than productivity. In the real world, most teams have
> code touched by developers at a variety of skill sets and everyone's
> contributions affect quality of code so tools should be measured across
> levels of expertise.

Not to mention that sometimes even Homer nods.

If something needs to happen in a certain way and you have to manually ensure
that it happens every time, sooner or later even the best of us will slip up.
Now, the tradeoff in power and flexibility might be worth the risk of shooting
ourselves in the foot, but it's still a tradeoff.

------
outworlder
The problem is not what C++ can or cannot do. Or if you can avoid its issues
(which are many), or even how long you need to be to get competent at it.

The problem is that you spend most of the time dealing with issues that are
created by the language, not from the problem domain.

Every minute you are debating which kind of smart pointer to use, interpreting
error messages (which are quite verbose), writing copy constructors and
assignment operators, or forgetting to add a virtual destructor, is a minute
not working on your problem domain.

If it is performance you're after, then C++ is also bad in the sense that it's
very difficult to reason about. Due to optimizing compilers, that's true to C,
but the abstraction is way thinner.

------
sbi
This article correctly observes that "many resources are not plain memory" and
then goes on to advise users to close() a file in a destructor. However, one
of the ways that files are not like memory is that while free() can never
fail, fclose() can fail if writing is buffered and the final write fails. This
is especially problematic in C++ since throwing from a destructor is
dangerous. Whether you rely on RAII or GC to free files, you will not be able
to catch this sort of error. It is precisely for this reason that GC is a
mechanism for memory management, not resource management in general.

------
AnimalMuppet
So far, all the comments seem to be missing Stroustrup's biggest point:
_garbage collection only works for memory._ Destructors work for everything -
memory, file handles, semaphores/locks, database connections, everything.

Garbage collection handles 90% of the problem transparently to the programmer.
That's wonderful (really - I'm not being sarcastic here). But the problem is,
garbage collecting languages usually don't have destructors, and so you're
left having to manage the other 10% of the problem yourself. You have to
remember to close all your own files, clean up all your database connections,
and so on. Garbage collection usually makes the 90% trivial, at the price of
giving you no tool at all for dealing with the other 10% (non-memory portion)
of resource management.

~~~
ptx
Many modern languages have a convenient solution for those cases as well:
Python has the "with" statement combined with context managers; C# has the
"using" block with the "IDisposable" interface that does essentially the same
thing; even Java recently got its corresponding "try-with-resources".

Before those were introduced we had try/finally which works equally well but
is slightly more verbose.

~~~
AnimalMuppet
Yes, you can do this with try/finally. But if you have an object that has an
open file as a member, then you open the file in the constructor, and then use
the open file in the member methods, and then... what? You may have a scope
that you are exiting where that object becomes irrelevant, but it may be
several layers away. Having to close that object's handle in a finally in that
scope seems likely to be forgotten at least some of the time.

~~~
ptx
That "scope that you are exiting ... several layers away" is where you put the
try/finally.

If an object has resources to dispose of (such as an open file), it should
implement (to use C# as an example – it looks similar in Python and Java) the
IDisposable interface, which allows you to do either:

    
    
      MyFileWrapperObject obj = ...;
      try {
        foo();
        bar();
        ...
      }
      finally {
        obj.Dispose();
      }
    

or:

    
    
      using (MyFileWrapperObject obj = ...) {
        foo();
        bar();
        ...
      }
    

Or to take a Python example from what I'm working on right now:

    
    
      with open("something.json") as f:
        data = json.load(f)
    

The file will automatically be closed at the end of the "with" block. Just
like in C#, this feature can be used for any kind of resource, not just files.

~~~
AnimalMuppet
> That "scope that you are exiting ... several layers away" is where you put
> the try/finally.

Yes, I know that it's where you're supposed to put the try/finally... if you
remember. Each time. And if you move the action in the finally clause if the
variable changes scope or lifetime.

Destructors are considerably cleaner than this approach. You just put the
close in the destructor, and let it do its job.

------
lelandbatey
I've heard so much laid at C++ feet over the years, but I don't find it all
that horrible to work with. It seems about on the same level as Python to get
most things done, just without all the magic of the python standard library.

Are there some good stories of the bad of C++ that anyone can share?

~~~
makecheck
Though not updated for C++11, a place to start would be the C++ FQA [1].

[1]
[http://yosefk.com/c++fqa/defective.html](http://yosefk.com/c++fqa/defective.html)

~~~
expr-
C++ FQA is rarely a good place to start for anything else than finding
examples of biased writing.

~~~
daxelrod
I found that combining the C++ FAQ and the C++ FQA made me a much better C++
programmer when I was learning the language.

Sure, it's ranty and biased, but that's kind of the point.

------
Animats
"My precious language isn't broken! It's not! Not! Not!" Strostrup writes
something like this about once a year. There's some denial there.

The basic problem with C++ is that it's hard to tell if something really bad
is happening. Think of C++ from the view of the maintenance programmer
assigned to figure out why a C++ program is crashing intermittently. Or worse,
the security expert trying to figure out how a system was penetrated. C++ has
hiding ("abstraction") without safety. This is a bad combination, one seen in
few other major languages.

We make progress in programming partly by having the language eliminate some
problem. High-level languages mean you don't have to worry about register
allocation, saving and restoring registers, or calling sequence conventions.
Assembler programmers have to obsess on those issues. C++ doesn't eliminate
any problems from C. It helps with some of them, but not to the point that
they're just gone.

The three big questions in C are "How big is it?", "Who owns it?", and "Who
locks it?". C++ helps a lot with the first one, even though it doesn't have
enough subscript checking to guarantee the absence of buffer overflows.

C++ has struggled with the second one, with three rounds of auto_ptr,
unique_ptr, and now reference-counted smart pointers. But because these are an
afterthought, implemented using templates, they're neither optimized nor
airtight. The mold keeps seeping through the wallpaper, in the form of raw
pointers. Rust has a designed-in solution to this problem. (Rust's borrow
checker is perhaps where C++ should have gone, but C++ will never get there.)

C++ as a language has no clue about who locks what. The language totally
ignores concurrency. That's said to be an operating system problem. This is
now a very dated concept.

(As I say occasionally, I really hope the Rust crowd doesn't screw up. They
address all three of those big questions in effective ways. But I see too much
use of "unsafe" in Rust code, which indicates weaknesses in the language
design. The use of Rust's "unsafe" for "performance" is a big problem. From a
semantic standpoint, only "Vec" needs "unsafe", because somebody has to
convert raw memory to an array of objects. Everything else can be built on top
of "Vec". But there is unsafe code for "performance" in hash classes and
such.)

~~~
72deluxe
How is abstraction unsafe?

Additionally, raw pointers are not dangerous. If you're passing them around as
const pointers, the receiver can't do things like deleting them. If you are
writing a container class (instead of using one of the existing myriad of
containers in the STL) then you can use raw pointers. But typically you would
not need to write your own container class; just use one of the STL's and the
move semantics. You can then use references everywhere instead. Or const
references. Everyone forgets about const correctness in their C++ bashing.

For concurrency, you may wish to see
[http://en.cppreference.com/w/cpp/thread](http://en.cppreference.com/w/cpp/thread)

There's even mutexes in there.

~~~
Jweb_Guru
Creating a dangling const pointer is pretty trivial. Hell, make it a const
reference.

    
    
        #include <iostream>
    
        struct Foo {
            const int &dangling;
    
            Foo(const int &dangling): dangling(dangling) { }
        };
    
        Foo foo() {
            int dangling = 0;
            Foo foo(dangling);
            return foo;
        }
    
        int main() {
            std::cout << foo().dangling << std::endl;
            return 0;
        }
    
    

Neither g++ nor clang warn on this, either. I am not sure why you think const
pointers are safe. They aren't. Flat out.

~~~
72deluxe
Well I was assuming for pointers that they'd be initialised to something
sensible. That would be a coding standard problem if they're not.

The example you give is also a problem with coding standards, I'd argue. It
isn't the languages fault that you're using a reference that's going out of
scope - that's just bad coding. (A pointer going out of scope isn't so bad,
no?)

