Some things any junior C++ dev should know

First of all, I consider myself to be an intermediate C++ programmer at best. Perhaps this is because I've noticed that a programmer's opinion of their abilities is usually inversely proportional to their actual skill level. Like most other development shops, my coworkers and I have a standard list of interview questions we draw from when interviewing programming applicants. Many of them are code fragments with various issues, defects, design issues, etc. While many of these questions try not to focus on language-level details, there are a few things in particular that most "senior" applicants don't know that really amaze me. This is by no means a complete list of things a junior C++ programmer should know, but the regularity with which people are unfamiliar with these specific items blows my mind.


prefix vs. postfix increment/decrement

Most people know the behavioral difference between the two, but are never aware of the efficiency difference. To understand why one is slower than the other, think about how you would implement overloads of both of these operators for some class "Integer":

// Prefix operator
Integer Integer::operator++()
{
    return ( *this + 1 );
}

// Postfix operator
// Note that C++ discerns between the prefix and postfix
// overloads by requiring the postfix overload to have an
// unused "dummy" integer parameter...a kludge if I ever saw one.
Integer Integer::operator++( int dummy )
{
    Integer previousValue = *this;

    *this = *this + 1;

    return previousValue;
}

Looking at the above code, it's obvious that postfix is less efficient since it requires caching the previous value in a temporary variable. Still, most people continue to use postfix everywhere, even when the previous value isn't needed. How many times have you seen a for loop that looked like this?

for( int i = 0; i < n; i++ )
{
    // do something
}

The performance penalty can be even worse when "i" is a heavier object, such as an STL iterator. Using postfix in such cases means an unnecessary copy of the heavier object is being made. Sometimes the compiler can tell when prefix would be sufficient and fix it for you, but you'd be surprised at which compilers don't. Besides, it's just as easy to type ++i instead of i++.


C++ casting operators vs. C-style casts

Lots of people still use old C-style casts in C++ code and I really wish they would stop. Can you tell me what the following code does?

Derived* derived = new Derived;
Base* base = ( Base* )derived;

Unless your answer was "I don't know", then you're wrong. For all you know, the class "Derived" has an overloaded casting operator for "Base" pointers that modifies the object or allocates all available memory or goes out over the internet and asks a human what type should be returned. The point is, you can't know. Besides their ambiguity, C-style casts are also a maintenance nightmare. With C-style casts, changes to code can introduce subtle bugs that may go undetected until after a product ships. The same changes would have failed to even compile had the appropriate C++ casting operator been used. C-style casts also freely cast away a variable's constness...scary stuff. I could describe each of the four C++ casting operators in detail, but that information is widely available online. Here's a decent description. Anyone familiar with them could easily tell me what the following modification of the above code does (and doesn't) do:

Derived* derived = new Derived;
Base* base = static_cast<Base*>( derived );

It's best to avoid explicit casts whenever possible, but if it must be done, don't use a C-style cast.


use the initialization list

I recently looked over some code with a "senior" C++ programmer. When he saw a class constructor making typical use of the initialization list he exclaimed, "Hey wow, I didn't know you could put members in that thing too! I thought it was just for calling base class constructors!" It took all I had not to cry. Because he lacked that one peice of basic C++ knowledge, it meant his code could never be const-correct, exception-safe, optimal, or well-designed. For example, he would have had to hold any class members that took parameters in their constructors by pointer and then dynamically allocated them in the constructor body. Say he had a std::vector member that needed to be initialized in the constructor. If he did so in the constructor body, it means that std::vector was likely unnecessarily default-constructed prior to filling it with the passed-in data. He also couldn't have any const or reference members since it's mandatory that they be initialized in the initialization list. It also meant he probably wasn't familiar with RAII, which means his code likely wasn't exception safe (I later found out he'd never used exceptions...what a surprise). So my point is...use the initialization list! With that in mind, there is one less-than-obvious thing you should know about initialization lists. I'll use an example:

class Foo
{
public:
    Foo();

private:
    int a;
    int b;
    int c;
};

Foo::Foo()
    : a( 0 )
    , c( a )
    , b( c )
{

}

What is the initial value of "b"? If you said "0", you'd be wrong. The correct answer is "undefined" because "c" is still uninitialized when "b" is given the value of "c". This is because variables in an initialization list are initialized in the order they appear in the class definition (so a, b, c in this case), not in the initialization list itself. Many compilers will generate a compilation warning with the code above to let you know that the initialization list's ordering doesn't match the class definition's.


pass non-trivial objects by constant reference

Take an STL container for example. Would you rather pass a "std::vector" or a "const std::vector&" to a function? The first one may make a completely new copy of your vector for the function to use, which is expensive and often unnecessary. In C++0x we'll also have move semantics, but that's outside the scope of this post.


be const-correct

Rather than explain the various uses of const in C++, here's a good article. Essentially, proper use of const makes a promise to client code that whatever is declared as const won't be modified. This is useful information to have in many situations. For example, if I call a const member function of an object, the function has promised me that it won't modify the internal state of that object in any meaningful way.


There's countless other things that could be on this list, but these specific few come up in interviews more times than I would have ever expected.