Sunday, April 15, 2007

Why do I dislike C++?

Let me count the ways...

1. Overloading Operators


Let's suppose we have the following:

std::vector<int> stack;
vector<int>::const_pointer iter = stack.begin();


This instantiates a vector of integers and an iterator for that vector. So far so good.

To access the element at which iter is currently looking, we type:

int b = *iter;

In other words, it looks as though we dereference iter. So it would follow that we can code:

int * ptr = iter;

WRONG! iter is not actually a pointer to anything, but rather a class that has cleverly overloaded the * operator to return the object at which it is currently looking. So how do we get a pointer to the current integer? As far as I can tell, like this:

int * ptr = &(*iter);

That's right, we use the & and * operators, usually inverses of each other, to get our pointer. Thank you, Standard Template Library.

2. Constants


C originally lacked constants; they were not added until shortly before the creation of C++ and therefore needed to be built around the existing language. Although constants in C++ generally are not a problem, there is one case that bothers me.

const int * a;
int * const b;


These are not equivalent statements. a is now a mutable pointer to a constant integer, while b is a constant pointer to a mutable integer. It gets better.

const int * const c;

This declares c to be a constant pointer to a constant integer. Surely there could have been a better way to do this — it seems very clunky to me to use the same keyword twice in the same statement.

3. C-Strings


This one is really a problem with C, but it was inherited by C++.

Back in the good (bad?) old days of the '60s, the next generation of programming languages was emerging. FORTRAN, COBOL, and BASIC all have a string type, which holds a series of characters and can have functions called on it to determine its length, the position of substrings, and so on.

But not C.

The makers of C decided that a string type would be superfluous. How did they decide to implement the string concept? A simple array of one-letter chars. It would end with a null character (the character whose ASCII value is 0, represented as '\0' in C). In C, arrays are extremely simple. They are really only pointers, and as primitive types they cannot store extra information such as their length.

How will we know how long our strings are, the developers must have asked themselves. We will count them, they answered themselves, or keep track of it in another variable. How will we call functions on strings, they further wondered. We will pass the function a pointer to the string and an integer for the length. How will we return strings? We won't; we'll modify their values as side effects of the functions. Won't that make otherwise simple operations like concatenation or comparison difficult and ugly? Who cares, this is C.

Years later C++ was created, and with it came a string class via the Standard Template Library. However, the remnants of c-strings still linger. String literals are first interpreted as c-strings, then converted to strings if needed. The getline member function of an input stream expects a c-string; there needs to be a special non-member function to read in to a string object.

4. Switch


The switch statement in C++ reeks of the days of assembly; it is essentially a glorified GOTO.

switch (someVar)
{
    case VALUE_1:
        // Do something

    case VALUE_2:
        // Do something else
        break;
}


What is wrong with this picture? Any programmer can tell me that I forgot the break in the first case. If someVar is indeed equal to VALUE_1, the program will do something, then it will continue right on and do something else. It seems odd for C++ to rely on a statement to tell it where the code in a block ends; C++ normally uses curly braces for that. We must examine this more closely.

100 IF someVar = VALUE_1 THEN GOTO 500
200 IF someVar = VALUE_2 THEN GOTO 700

500 REM Do something
700 REM Do something else
800 GOTO 1000

1000 END


The above is our switch program written in BASIC. We can see the behavior much more plainly here. The switch is like a series of IF statements that then GOTO the proper place in the program. The cases are labels. The break is also translated into a GOTO. If the break in C++ or the GOTO in BASIC is forgotten, the program just keeps on going. There are no actual blocks, only a series of labels.

For the record, BASIC does have a switch construct too, called SELECT. It looks and behaves just like its C++ cousin.

Almost half a decade later, we have decided that GOTO is probably not the best idea anymore. Very few, if any, modern languages make use of it (Java keeps it as a reserved word but has never used it).

It gets better though. Because there are no blocks in a switch statement, there is only one scope.

switch (a)
{
    case 1:
        int i = 3;
        break;

    case 2:
        int i = 4;
        break;
}


This code would produce an error, because i is declared twice in the scope of this switch. One solution might be:

switch (a)
{
    case 1:
        {
            int i = 3;
        }
        break;

    case 2:
        {
            int i = 4;
        }
        break;
}


Here we force each i to have its own scope. At this point, though, we have triple indentation. It might have been easier to just use a series of if statements, as they would each have their own scope.

These are some of the gripes that I've had with C++ so far as an undergraduate computer scientist. It was quite good to vent, and now I'll be able to refer people to this blog when I say that I dislike C++. I'll probably wind up returning to this entry later, once I've had time to discover more of C++'s more, er, fascinating idiosyncrasies.

Labels: , ,

0 Comments:

Post a Comment


Links to this post:

Create a Link

<< Home