Archive for the ‘C++ Is Bad’ Category.

C++ is bad: structs aren’t the same as structs

Here’s a fun little problem. Suppose you’ve got a system that makes use of the following struct:

struct User {
  unsigned int user_id;
  unsigned short access_level;
  float account_balance;  // stored as US dollars
}

Your system is ridiculously well-tested, all the tests pass, everything works totally fine. I’ll be so bold as to say it’s (kinda) bug-free.

One day, you decide to add a new field to the User struct, a string called name. You recompile everything, and suddenly all kinds of tests fail! New users have all kinds of access levels, some have account balances in the billions while others have accounts worth negative billions. What happened, why, and how do you fix it (without removing name again)? Edit: and no, the code does not rely on brittle assumptions about the value of sizeof(User), nor does it break type safety and have unsafe casts or void*’s pointing into the middle of Users. That would be too easy.

The answers, and more →

C++ is bad: inheritance is counterintuitive

In our latest installment of “why not to program in C++,” let’s take a look at a simple class hierarchy:

class Duck {
 public:
  Duck() {
    InitializeMigratoryRoute();
  }

  // Default behavior is nonmigratory; this is
  // overridden in the Mallard class.
  virtual void InitializeMigratoryRoute();
}

class Mallard : public Duck {
 public:
  Mallard();

  // Migrate south in the fall and north in the spring
  virtual void InitializeMigratoryRoute();
}

class DonaldDuck : public Duck {
 public:
  DonaldDuck() {
    outfit_.AddShirt(Outfit::Shirts::SAILOR);
    outfit_.AddHat(Outfit::Hats::SAILOR);
  }

 private:
  Outfit outfit_;
}

You can assume this code compiles and runs (i.e., the Outfit class is defined, it implements AddShirt(), both versions of InitializeMigratoryRoute() are defined, etc). I would say this looks like perfectly reasonable code: its intention is clear and straightforward, and any sane language should have no trouble implementing this. [1] However, we’re dealing with C++, which means there are always more problems, and in fact this code is not in the least alright. In particular, there are two major things wrong with it. Do you see what they are?

This code has two major problems. →

How to swap two integers in C++

Warning: this post got embarrassingly long. For the short version, read up through the paragraph that starts with “If you use.”

This is something that’s been bugging me for a while, and I’d like to lay it to rest. If you’ve done any work in C++, you’ve probably heard the riddle about how to swap two integers without using a temporary variable. The question is exactly what it sounds like: you have two integers (call them x and y), you want to swap their values, and you may or may not want to use a third, temporary variable while doing it. Here are some ways of swapping integers that I have actually seen in professional code written by professional coders:

Method Name Code
Method A
{  // Limit the scope of t
  int t = x;
  x = y;
  y = t;
}
Method B
x ^= y;
y ^= x;
x ^= y;
Method C
x ^= y ^= x ^= y;
Method D
x = x + y;
y = x - y;
x = x - y;
Method E
std::swap(x, y);


Which of these is the fastest way to swap integers? Which way is the fastest method that doesn’t require extra memory? When will you want to use a different method? What is the overall best method?

 

Try to come up with some answers before reading further.

 

When you’ve decided on your answers, let’s take a closer look. No, on second thought, this got really long. For those of you who don’t want to read all the way to the end, let me just get my main point out of the way. After that, we’ll take a closer look. If you don’t want to spoil the ending, skip the next paragraph.

If you use this link you will spoil the ending, so skip to the next paragraph after this one. →

C++ is bad: problems with the ternary operator, addendum

I’ve found another interesting wrinkle in the ternary operator. So, here’s a bonus question for that quiz from last time. You start with the following code:

class Abstract;
class DerivedOne;  // Inherits from Abstract
class DerivedTwo;  // Inherits from Abstract
Abstract x;
DerivedOne one;
DerivedTwo two;
bool test;

Again, you can assume that all of these are defined/initialized (and you can assume the comments are correct—both Derived* classes inherit from Abstract). Now, consider these snippets:

# Code Snippet
A
Code Snippet
B
Bonus
if (test) x = one;
else x = two;
x = (test ? one : two);
 


If you read the previous installment, you’ve probably guessed by now that these are not equivalent, and you’d be right. In what ways do they differ?

Another unexpected answer →

C++ is bad: problems with the ternary operator

In today’s installment of “why not to program in C++,” I give you the following quiz, which Dustin, Steve, Tom, and I had to figure out today (Dustin’s code was doing the weirdest things, and we eventually traced it down to this):

Suppose you start out with the following code:

class Argument;
Argument x;
void Foo(const Argument& arg);
bool test;

You can assume that all of these are defined/initialized elsewhere in the code. For each pair of code snippets below, decide whether the two snippets are equivalent to each other.

# Code Snippet
A
Code Snippet
B
1
if (test) Foo(x);
else Foo(Argument());
Foo(test ? x : Argument());
 
2
{ // limit the scope of y
  Argument y;
  Foo(test ? x : y);
}
Foo(test ? x : Argument());
 
 
 
3
Foo(x);
Foo(test ? x : x);
4
Foo(x);
Foo(true ? x : Argument());


Edit: what I meant by the curly braces in Question 2 is that you shouldn’t consider “y is now a defined variable” to be a significant difference between the two snippets.

The answers are worse than you'd think. →