Hopping between my open-source Android projects and Photoshop Elements at Adobe involves a lot of mental context-switching. A majority of that transition is necessitated by the fact that Java and C++ share similar syntax and semantics but differ in countless ways, often subtly. I often catch myself "thinking in Java" when writing C++, or the other way around, when I'm in the midst of said context-switch.

The other thing to consider is: C++ is a double-edged sword. It's powerful, but that also makes it very easy to make mistakes.

So I thought I'd compile a list of common C++ pitfalls / things to remember / things I find interesting, with references to more detailed discussions. I expect to often refer to this post myself. The format is somewhat terse, so beginners might find it confusing.

I know you don't need me to tell you this. But sometimes I forget to call delete (or delete[] ), so this item is basically a reminder for myself. It doesn't help that accidentally calling vanilla delete on new[] 'ed memory causes undefined behaviour, potentially including subtle memory leaks.

Or, as many commenters rightly pointed out, you could simply use smart pointers like C++11's std::unique_ptr and std::shared_ptr and side-step this whole mess altogether.

Yes, I hear you: "But what if my destructor fails?". Simmer down, whippersnapper. I'm calling in the cavalry on this one. Here's Marshall Cline's suggestion (Cline is author of the treasured C++ FAQ):

Write a message to a log-file. Or call Aunt Tilda. But do not throw an exception!

You at least trust him, if not me, right? You should. Here's the reason: when an exception is being processed, the program begins moving up the call stack in search of a catch block, popping off stack frames and destroying all local objects in them. Guess what happens if the destructor of one those objects throws an exception? Now there are two uncaught exceptions to deal with, and no catch in sight! At this point the program simply explodes in your face, as the realization dawns that you've committed thrown one sin exception too many in the Holy Land of C++.

class A { ~A() { ... } }; class B : public A { ~B() { ... } }; A *a = new B(); delete a; // what happens here?

The call to delete at the end will, in most cases, call A::~A() , resulting in a hard-to-detect memory leak. The standard's stance on this issue is "undefined behaviour". Making A 's destructor virtual resolves the leak.

Spot the best code:

// option 1 try { ... } catch (exception e) { ... } // option 2 try { ... } catch (exception& e) { ... } // option 3 try { ... } catch (exception* e) { ... }

Yes, yes, it's option 2. But why?

Option 1 is bad because: (a) it might involve an unnecessary copy constructor invocation, and (b) it can cause a subtle bug, thanks to slicing! For example, if the try block throws an instance of type derived_exception , derived from the type exception (duh), then the latter's copy constructor gets called, resulting in a hacked-off instance of the former getting sliced into the latter.

Option 3 is bad simply because naked pointers need to be manually de-allocated. This is especially bad considering the fact that when I'm writing a catch block, I already have a lot to think about: how to recover from the error, whether I can punt responsibility up the call stack, and so on. So no naked pointers to exceptions, thank you very much.

const follows a rather simple rule: it acts on whatever precedes it. So:

char const* buffer; // pointer to const char (1) char* const buffer; // const pointer to char (2) char const* const buffer; // const pointer to const char (3)

Much of the confusion arises because many (including me) tend to prefer placing the const before the char in the first case:

const char* buffer; // pointer to const char, same as (1)

If you'll tolerate my hypocrisy for a moment, here's my suggestion: try to avoid putting the const at the beginning like that. Another source of confusion is array declarations with const :

int main(int argc, char* const* argv); // pointer to const pointer to char int main(int argc, char* const argv[]); // equivalent

What's the right signature for a copy constructor?

A::A(A) - infinite recursion

- infinite recursion A::A(A&) - valid but won't work if the argument is a temporary, e.g., A fn() { A a1; return a1; }; A a2 = fn(); // error

- valid but won't work if the argument is a temporary, e.g., A::A(const A&) - valid and works with temporaries (see item #8)

Consider the overridden assignment operator for class A (our favourite class):

??? A::operator=(const A& other) { // guess the return type? // assign other to this // ... return ???; // what to return here? }

The standard says the return type of operator= must be A& and the return value "a reference to the left hand operand", i.e., *this . Without a satisfactory reason, that's just begging to be forgotten. Here's your reason: it's so that chained assignments like a = b = c work correctly (think of it as a.operator=(b.operator=(c)) ).

A getA() { return A(); } const A &a = getA(); // ok A &a = getA(); // error

You cannot assign temporaries to non- const references. You knew that right? Yes, I'm looking at you, Visual Studio 2010 user. Your compiler doesn't even emit a measly warning for decidedly non-standard behaviour. Clang and GCC are kind enough to spit out descriptive compilation errors. This has bitten me more than once, when writing cross-platform code in Visual Studio.

class A { int n; }; A a1 = new A(); A a2 = new A;

What do you think are the values of a1.n and a2.n right after construction? Answer: 0 and {insert garbage value here}. More generally, plain old datatypes are zero-initialized in the former case, but behaviour in the latter case is implementation-defined.

Sure, your compiler helps you out with this. But this is very easy to forget, say, in an interview. So have you thought about why at least one virtual function is needed for using dynamic_cast ? Simply because it's convenient to implement dynamic_cast by piggy-backing on the virtual method infrastructure: the type hierarchy information (conventionally called "run-time type information" or "RTTI") is usually attached to the vtable, and a run-time check is performed, using available RTTI, to determine whether the cast is valid.

I know I bossed you around a bit throughout the post, dear reader. But hey, good news! Vent all you want in the comments, especially if you think I'm downright wrong somewhere above.