Topic : Why C++ Sucks
Author : buzzard
Page : 1 Next >>
Go to page :


Why C++ Sucks


C++ sucks because it is a mis-designed pile of crap.

All you have to do is read Bjarne Stroustrop's book on the design and evolution of C++ to understand why: C++ is the iMac of computing languages.

It was designed to have those features necessary to achieve popular success--not those features necessary to be a good programming language.

In the case of something like the iMac, much of the "prettiness" does not necessarily come at the expense of functionality--but this is not always the case. Reducing the iMac's expandability makes it simpler to use--but also potentially consigns it to a gutter market a few years down the road.

The Big Mistake: C Compatibility
In Stroustrop's mind, making C++ compatible with C was instrumental, crucial to its success.

I don't disagree. Plenty of other good object oriented languages are out there, and they've never found much success. Certainly the overhead of learning a brand new language is undoubtedly a significant barrier to acceptance.

I don't think it's any coincidence that Java chose to use C syntax for its core constructs, either.

But C++ went far further than Java in compatibility. C code works nearly unchanged in a C++ compiler. The model of how a C++ program can be separately compiled (i.e. split into multiple separate files) is identical to that of C.

But this introduces hosts of other problems, some which C++ addresses, some of which it addresses with problematic design, and some of which it simply falls down on.

This is not to laud Java--I do not have a particularly high opinions of that language. It does many good things, but owing to its history it also has some odd design elements. (Foremost amongst them being reliance on interpreters during its initial introduction.)

Keep It Simple, Stupid
C++'s biggest problem is its size. People will tell you that this is no big deal; just subset C++ down to whatever part of it you're willing to use; that can be a C subset or something a little larger, or the whole thing.

This logic can be used to justify an infinitely complex language, so one should hesitate to accept it at face value.

The biggest flaw with the argument is that it forces compiler writers to implement a larger language; correctness will be harder to get right, and optimization will suffer. In the case of C++, however, this no longer matters; there are essentially no "just C" compilers anymore to compare to, so any lowered performance of C++ is no doubt also seen by C.

Another significant problem is that it requires an investment of effort to select an appropriate subset. Moreover, you will have difficulty finding books that teach this subset; and, indeed, if you acquire a C++ algorithms book, the odds that the subset chosen for that book matches that of yours is low.

In other words, subsetting is the same as fragmenting the language into lots of separate dialects; it has all the same problems as that, with the added cost of none of those dialects having unique names. What does it mean to say "I've used C++ for six years" on a resume?

Similarly, learning a subset does you no good if you work with other people's code, and they do not use the subset you are expecting; you must be able to understand their subset, and even write it.

Finally, learning a subset doesn't guarantee that you won't avoid being bit by something that's not in the subset you've chosen. For example, you might choose (wisely, IMHO) to avoid using function overloading. You can't tell the compiler this, however, and thus you might unintentionally name two functions the same thing, and cause unimaginable problems thereby. Sure, you'll eventually figure it out, it'll just be another dumb bug, but why use a language that has any number of such gotchas lurking around every corner?

Suppose you choose to just use the C subset of C++. One of the changes C++ makes to C rules is that you can no longer automatically cast from (void *) to other pointer types. The reason for this is clear; in C++, (void *) types are used sufficiently often that hidden bugs might occur. (There is a counter-argument to this even in a C++ context: C++ encourages you to typecast more often than necessary, possibly masking bugs because your typecast hides what would be a real warning.)

Is this a real problem for C? No, it's not, it just means you need to do some extra casting. The following code doesn't work:


   x = malloc(sizeof(x[0]) * num_elements);

Instead you must code it as


    x = (mytype *) malloc(sizeof(x[0]) * num_elements);

Of course, if you are familiar with the idiom found in the first example, you see the flaw in the second; the first version avoids a bug by not explicitly naming x's type; thus if that type changes, the code still (most likely) does the right thing. With the second code, if you change x's type, C++ will complain about the type error, so it won't introduce an error--just unnecessary typing to fix it.

If you're using C++ proper, you would just use the new operator to sidestep this... ignoring the fact that new requires the name of the type...

Too Much Typing
I guess I'm just a whiner if many of my problems boil down to C++ requiring too much typing. Typing is such a tiny fraction of programming that it's not that big a deal. Yet it grates to see such extra 'accidental' (in the sense of Fred Brooks 'No Silver Bullet', i.e. in opposition to 'essential') work required for no good reason.

Example #1
In C, I write a new function, and then add a prototype to the header file. Having to add that prototype is annoying 'accidental' effort; there is no reason this redundancy must be shoveled onto the programmer (it could easily be automatically generated, a la Java or Borland's Turbo Pascal). That something like a header file is needed for separate compilation is undeniable; but that I must maintain it by hand is largely silly.

Still, it's not that much work. Cut and paste, put an 'extern' at the front of the line and a ';' at the end of the line, and you're done:



C file:
   int myFooBar(Foo *foothing, Bar *barthing)
   {
      ...
   }


H file:
   extern int myFooBar(Foo *foothing, Bar *barthing);

Whether this worked out by chance or not, these are easy editting operations to perform without moving your hands from the keyboard; cut a single line (in 'vi', 'yy'; in a windows editor: home, shift-end, ctrl-c), switch buffers, paste, go to beginning of line and type 'extern', go to end of line and type ';'.

The effort is justified because all this information needs to be available for separate compilation.

Consider the equivalent thing for a method in C++:


CPP file:
   int Foo::myBar(Bar *barthing)
   {
      ...
   }


H file:
   class Foo
   {
      ...
      int myBar(Bar *barthing);
      ...
   };


Sure, in this example, the function declaration itself may be shorter, making C++ look better than C, but I'm comparing C++ to a similar, imaginary OO language that doesn't suck.

To make the C++ cut and paste, I don't need to add 'extern' at the front. Instead I have to reach into the middle of the declaration and delete the 'Foo::'. This is actually more work--at least for me, it takes longer, and more thinking, to do this. (You have to actually parse the declaration, which gets more complex as the return value type gets more complex.)

Example #2
Worse yet, C++ makes this necessary in circumstances that it shouldn't be.

Suppose that class Foo in the example above inherits from Baz; and Baz includes as a member in its declaration virtual int myBar(Bar *barthing);. Now, when I want to go implement Foo, I choose to override the definition of myBar found in Baz.

C++ makes me spell out in the declaration of class Foo exactly which methods I'm going to override.

Even though the whole point of virtual functions is that the dispatch occurs at run-time--no compile-time support needed.

Pointless.

Oh, and did I mention that this sort of thing leads to extra unnecessary recompilation?

Why?
I think I know why C++ does it this way. The thing is, if I subclass Foo to make, say, Biz, then if Biz doesn't define myBar for itself, it will need to store a pointer to Foo::myBar in its virtual function table. Thus, the compiler needs to know about everything that goes on under the hood with Foo to build Biz correctly. (Similarly if Biz defines it itself, but calls ::myBar.)

That

Page : 1 Next >>