
C++ pitfalls - rohshall
http://www.horstmann.com/cpp/pitfalls.html
======
lbrandy
It's missing my favorite, std::(unordered_)map::operator[]

    
    
      map<int,int> a;
      cout << a[11] << endl;
      cout << a.size() << endl;
    

This works fine and has well defined behavior: it will print '0' followed by
'1' since the middle line actually inserts a {key=11, value=0}.

This is a landmine I think every journeyman c++ programmer steps on a few
times.

~~~
angersock
The amazing shittiness of std::map and friends drove us to write our own, with
blackjack and hookers.

I really wish there was an API "Hall of Shame" with attached discussion
minutes showing the exact points where a group of otherwise sane people
decided to kludge these things in.

~~~
tcwc
I agree this particular example can be confusing the first time you hit it,
especially coming from other languages.

It's better than the alternatives though, I would be interested to hear how
you handle this in your version. Leaving the behaviour undefined for non-
existent keys is likely to cause far worse bugs, throwing an exception would
be inconsistent with the rest of the stl.

The could have left it out altogether, but would mean losing some nice
properties - operator[] returning a reference makes it possible to assign into
the map directly ( a[3] = 5; ). Also since the value is default initialized,
you can write something like a counter easily, much like a python defaultdict:

    
    
      for (auto id in ids) {
        a[id] += 10;
      }
    

You can always stick to .find and .insert if you prefer the more explicit
behaviour.

~~~
cobrausn
In the version we ended up writing, 'operator []' is equivalent to a call to
'Get', which also returns a reference to the mapped value. In the event that
key is not mapped, it asserts. If you handle the assert or have disabled
runtime asserts, it returns a reference to a static value instance, so you can
handle this kind of error yourself or even use the static value as a 'default'
(though we never use it that way).

Though not consistent with how STL works, it is consistent with how our
containers work and how we use maps. YMMV.

Also, since we wrote it, we're free to change the behavior if a better way
manifests itself. So, if you have any suggestions, let fly.

------
CJefferson
This is an interesting list to look through, but things are not as bad as they
look.

Many of these are warned about on modern compilers, if you use '-Wextra
-Wall'. Some of them are even on by default. I think compilers should warn
more eagerly. I would put -Wextra -Wall on by default -- it is easier to turn
off things you don't want, than to discover things you do not know exist. But
I know this is not a popular opinion!

------
givan
C++ provides high level stuff at the expense of making the programmer part of
the compiler that must give it hints about memory, types or cope with multiple
page error outputs because of one missing semicolon.

C++ exists because real high level languages does not have very efficient
compilers yet.

And where high performance is really needed and programmers time or effort is
not a problem C++ is preferred.

I expect that with the increasing processing power compilers will get some AI
incorporated and get smarter in the future and high level languages will
output binaries that are at least as fast as those produced by C++ or C
compilers.

~~~
npsimons
C++ exists because processing power and RAM _used_ to be at a premium; these
days, hardware is so cheap, you can get away with writing inefficient code in
any language. However, two things are of note here: C++ has had a lot of
higher level features bolted on after the fact, including compiler warnings
and tools to help reduce burden on the programmer, and higher level languages
have had a lot of performance "bolted on" (plus hardware has gotten so much
faster and cheaper) so that if you code properly (in any language),
performance differences are near negligible.

C++ mostly hangs on because of backwards compatibility and a large user base;
I suspect that as time goes on, we'll see it fall more and more by the wayside
(and I say this as someone whose bread and butter is C++), especially with the
fact that programmer time is a big cost driver.

~~~
comicjk
If you're writing games or scientific programs, you still need C++, if not
Fortran or assembly.

------
octopus
This article is from 1997, you should at least mention this in the title.

~~~
cousin_it
Which of the listed pitfalls are gone in C++11?

~~~
pandaman
Not with the C++11 by itself but with better compilers we've got since about
2000 (don't forget that the earliest C++ standard is from 1998):

1\. Member initialization order being broken in the constructor initialization
is a warning pretty much everywhere (except Microsoft compilers, I think).

2\. Having a virtual function and no virtual destructor - a warning even in
Microsoft compilers.

3\. Trivial infinite recursion (i.e. Manager::print() { print(); } is a
warning at least in some compilers. I have seen only a handful of projects
where such code existed at all so I cannot say how widespread this warning
(For me it popped due to problems with scripts generating code, people don't
write this stuff ).

This is just from glancing over the article, I am sure if you tried all
pitfalls with a modern compiler like clang or even gcc, you'd get a few more
warnings.

Though I agree with the main point the author is trying to make - C++ is not
just Java with pointers so if you are coming from Java background you should
be very careful not to assume the conventions you've got used in Java are the
same in C++.

~~~
UnFleshedOne
I never understood the need for blanket warnings for member initialization
order on gcc. 99% of the time the order doesn't matter (who cares if I assign
zero to one int or another first). And when it does, it is better to move
initialization to the body of constructor anyway. Basically I have to do
annoying reordering every time I do some changes on msvc and then compile on
gcc just to remove the warnings.

------
430gj9j
All languages have pitfalls. Including the author's beloved Java. For example:

    
    
       String x = "foo";
       String y = "foo";
       // Appears to work but compares references
       System.out.println(x[0] == x[1]);
    

There's no substitute for knowing your tools well.

~~~
diek
I assume you meant to say:

    
    
        System.out.println(x == y);

~~~
430gj9j
Yes. In a previous version I was trying to bypass string caching to make x==y
return false.

------
activepeanut

      template<typename T>
      Array<T>::Array(int size)
      :  _size(size), 
         _data(new T(size)) // should have been new T[size]
      {}
    

That's not the only thing wrong here.

    
    
      private:
         T* _data;
         int _size;
      };
    

The order of initialization is the order of declaration, NOT the order you use
in your constructor.

_data depends on _size being initialized first. Therefore _size must be
declared above _data.

Note: I understand the next example goes over this issue. I just think this
example should've been declared properly as to avoid distracting the reader
from the main problem, "() vs []".

~~~
aidenn0
Why does _data rely on _size being initialized first? It uses "size" not
"_size" in the expression.

~~~
activepeanut
Good catch. You're correct. I wish I could delete my original comment.

------
16s
This is 15 years old.

~~~
wonderzombie
And C++ is approx. twice as old as that. What's your point? :)

Seriously, though: I'm no expert, but many people upthread have suggested
these are still applicable even in C++11. Compiler warnings help, though you
still have to learn what they mean and how to fix them.

------
gregsqueeb
Do you go to SJSU? Hit me up! gregdmathews@gmail.com

