Heisenbugs aren't actually that rare. Often they are caused by timing/race conditions that change when a debugger is attached, and others talked about this. Sometimes, they can be caused by uninitialized local variables in C/C++ applications.
Someone forgets to init some variable or other memory (often used for a pointer), but the whole app usually happens to work just because the values found on the stack at its location are almost always "good enough" not to make the app crash.
Then, someone starts to look after the bug with a debugger: bang, the behavior starts to change: it crashes more often, or it never crashes, or something like that. This usually happens because, when running in debug mode, the the CRT/the OS allocator1 fill uninitialized the memory with special patterns in order to make easier to spot uninitialized variables. This makes the bug alter its effects, sometimes making it easier to track, in other occasions just making you think "nasty magic is going on here", because just attaching a debugger make the behavior change.
In general, having to deal with heisenbugs is not funny at all.
1. the tragic thing is that, on Windows+VC++, when you use the debug CRT (which usually means that you're compiling in Debug configuration), the CRT allocator fills memory with its magic pattern, and when you start an application with a debugger attached, the Windows debug heap is used. If you don't know this, things may get very creepy, since you can get several sets of "strange behavior" depending on the combinations of CRT/OS debug heap.
When you find out that starting the application from the IDE or starting it separately and connecting the debugger later yields different results... well, you have quite a situation. :)