Type conversion rules
A well known and often used pattern is simultaneous assignment of an expression to a variable and its comparison with another value.
1
2
3
| if ((a = b) != c) { // do something } |
In both Java and C would this code would have the same behaviour. The problem arises when a parenthesis is misplaced, resulting in an assignment of a boolean expression to a:
1
2
3
| if ((a = b != c)) { // do something } |
Since a boolean expression can be converted to an integer and the assignment expression is contained in a parenthesis, the compiler may even not ensue a warning. For Java this code isn’t legal anymore while perfectly fine in C. The error strikes most hard when the result of the comparison, namely 0 or 1, is a valid value. A good example is a call to socket(), that may return 0 as a file descriptor for stdin. The probably simplest solution to this problem is separating the assignment from comparison – even at the cost of a temporary variable.
Memory management
The behaviour of standard containers is sometimes combined with incomplete/misunderstood behaviour of pointers. An example:
1
2
3
4
5
6
7
8
9
10
11
12
| class A {} class B { public : void foo() { std::vector<A*> theContainer; for ( int i = 0; i < 100; i++) { theContainer.push_back( new A()); } } } |
Every call to foo() would result in a memory leak due to not deleted A’s. When the vector is destructed, a destructor of each contained item is called. For pointers and other scalar types this is a no-op, resulting in missing call to the destructor of pointed to class. A solution to this problem could be the use of smart pointers wrapping the actual pointers or an explicit destruction of pointed to objects before the vector goes out of scope.
Deterministic destruction
Coming from language with automatic memory management there is some uncertainty when it comes to the order of destruction when multiple objects leave the scope. Consider this example:
1
2
3
4
5
6
7
8
9
| void foo() { std::lock_guard<std::mutex> lock(mutex); std::ifstream input .... //some operations //?? } |
In this case the stream is destructed before the lock, guaranteeing that the stream is destructed before the execution reaches the destructor of the lock. This pattern is exploited by the RAII.
Exception handling
This is my personal favourite. Here is a little quiz: what is printed to the screen?
1
2
3
4
5
6
7
| try { throw new SomeException(); } catch (SomeException& e) { std::cout << "first" << std::endl; } catch (...) { std::cout << "second" << std::endl; } |
As some may already have guessed from the question: the answer is “second”. To make the code work, the reference in the catch block has to be replaced by the pointer. Another, and probably better alternative is to create the exception on the stack. The reason behind this mistake is that in java any thrown object is constructed with new. Explicit hints or experience are required to avoid such flawed exception handling.