back

by vardump·12y ago·view on hn ↗
I don't think I need to say C++ has been used to build great things. The web browser you're using is very likely built in it.

But it's also been the security nightmare of the century. And been used to build the most horrible messy codebases in existence.

C++ encourages coding style that leads to very complicated execution flow. Multiple inheritance. What's the actual concrete method this thing is going to call? Fire up the debugger and set a breakpoint...

Operator overloading. Yes, there are good uses for it. Like math code. Or some container implementations. Unfortunately it's often just hiding what's really going on or worse, bugs. Yeah, you're so clever overloading /-operator for building file paths...

Exceptions - there to provide untold number of possible execution paths making a typical C++ program flow nearly impossible to comprehensively understand. Exceptions seem to discourage from actually handling the errors as opposed doing something about it at the call site. There's often little you can do five nested calls above, where the exception is caught. Handling means almost always a some sort of logging function or user notification, not actually doing something about it. It's easy to forget it's not only about resource management. Side effects matter too! RAII doesn't undo IO.

Those features hide bugs and make C++ maintenance a true challenge.

Of course one can earn a steady income writing C++...

3 comments
You say C++ has "been used to build the most horrible messy codebases in existence".

There are two kinds of programming languages: those that have been used to create horrible messy codebases, and those that haven't been widely used.

> There are two kinds of programming languages: those that have been used to create horrible messy codebases, and those that haven't been widely used.

I think Bjarne's original statement, "There are only two kinds of languages: the ones people complain about and the ones nobody uses" [0], has been widely paraphrased.

[0] http://www.stroustrup.com/bs_faq.html#really-say-that

So true.
> C++ encourages coding style that leads to very complicated execution flow. Multiple inheritance.

Can you bring another example that leads to "complicated execution flow" because literally no one uses multiple inheritance (the only exception being when a class needs to implement multiple interfaces).

> Operator overloading.

Very useful feature. Enables you to write readable expressions for actual mathematical objects (e.g. complex numbers, quaternions) and used to implement things like smart pointers. Yes, you should exercise caution when using it.

> Exceptions

Exceptions are useful if used to report errors, not to control program flow. Also, nobody "discourages" from handling errors - you should do it if you have enough context to properly handle the error, otherwise you don't have much choice other than to propagate the exception up.

Even single inheritance with multiple subclasses makes following the potential code paths mentally as many times harder as there are classes that extend or override the common base class.

Operator overloading; looks like we agree.

Exceptions do control flow, even if you just use them to report errors. They're often effectively hidden gotos.

> Even single inheritance with multiple subclasses makes following the potential code paths mentally as many times harder as there are classes that extend or override the common base class

But that is a property of inheritance and virtual functions, not something inherent to C++. You could be using another object-oriented language and the logic would be as hard to follow. We could discuss why or why not that property is problematic, but that would be straying from the topic, which is C++.

> Exceptions do control flow, even if you just use them to report errors. They're often effectively hidden gotos.

Again, same logic can be applied to any language that supports exceptions.

>Even single inheritance with multiple subclasses makes following the potential code paths mentally as many times harder as there are classes that extend or override the common base class.

Composition over inheritance is a technique used to reduce this problem.

[1] http://en.wikipedia.org/wiki/Composition_over_inheritance

You don't have to use runtime dispatch in C++. It's pretty easy to just grep and make sure no methods are virtual. You pretty much have to go to town with runtime dispatch with languages like Java.

On exceptions, the Ada people also came to the conclusion they're necessary and that return code checking is not viable.

OTOH, GCC and Clang provide warn_unused_result, and it's easy to define types for returning errors from functions that throw a compile-time error if ignored by the caller.

I find explicit error handling safer and more elegant than exceptions, especially if you consider that writing exception-safe generic code is hard and error prone.