It gives you an excellent overview of the various ways in which things can go wrong, and C++ figured quite prominently in the 'gotcha' department.
C has it's share of issues, double frees, failure to initialize (but most compilers catch that one nowadays), and stale pointers. With a good discipline you can work around those.
C++ can obscure the bugs in such a way that it takes you a long long time before you can 'nail' them. The biggest problem I have with the language is that the code tends to obscure what is going on at the machine level.
I guess that's a 'feature' too, but I prefer to have a more direct correspondence between program code and what goes on below the surface. That's a problem with OO in general by the way, and I think that this is part of what makes software so terribly inefficient these days.
Gigabytes of ram are barely enough to accomodate a single user os, it's really pretty weird.
Which totally defeats the purpose of being backward compatible with C.
If it had been D instead, an actual new version of C designed to incorporate new features, instead of C++, additional features on top of C, it probably would be less reviled but there would have been a lot more resistance to adoption.
In general, there is at least the linearizing (Common lisp) multiple inheritance with a well defined method lookup semantics (even though there are good ways to shoot one into the foot there), and the C++-local-hackity-multiple inheritance (where the programmer has to resolve lookup ambiguities, resulting in unpredictable semantics). And this even avoids the discussions of traits, mixins and such.
Also, note that there is also a semantic difference here between C++ (and Java) and most other languages. In C++ multiple inheritance, if classes A and B define method foo, and A and B have no inheritance relationship, then A::foo and B::foo are entirely unrelated methods -- just as if they were named A::foo and B::bar. The fact that they have the same name does not automatically create a relationship between them, as it would in CLOS or in duck-typed languages like Python. That means that a call to foo in a class that inherits from A and B is just as ambiguous as if the programmer had typed "foo, or maybe bar." This unrelatedness is inherent to C++'s type system. It would make no sense to a C++ programmer to treat A::foo and B::foo as being alternative dispatch targets for the same method call.
Even if you allow the compiler to gloss over this unrelatedness, I still think it is a matter of taste whether it is simpler to require programmer disambiguation or to have a well-defined algorithm for resolving ambiguous names. C++ usually takes heat for providing powerful more-than-meets-the-eye mechanisms that allow programmers to "hide" program semantics inside language features. Here C++ takes the opposite approach and requires the programmer to explicitly resolve ambiguous names, and it takes heat from a CLOS programmer, who could write an entire operating system using a single family of generic functions all having the same name ;-) Damned if you do, damned if you don't.
I sense an upcoming challenge! ;)
C++ is definitely a language where you can get screwed by "clever" programmers who would rather be reading TC++PL than actually coding, but it's not so bad if the people you work with exercise good judgment. I've never had to deal with multiple inheritance in real production code, for instance.