Not many programming languages support three storage types; even among the ones that do, very few allow the programmer to control precisely which type of storage will be used, for every variable and object. This diversity comes at a price: C++ memory management is hard to teach and learn, and it imposes more responsibility on the shoulders of the programmer. But this storage types diversity is also one of C++'s strengths. Without this diversity C++ wouldn't be C++ -- the C++ I like, that is.
Each of the three storage types of C++, static storage, automatic storage and free-store, affect the scope, lifetime and initialization of the objects and variables they host. Could C++ make do with a seemingly simpler memory model whereby all fundamental types are allocated on the stack, and user-defined types (except enums) are allocated exclusively on the free-store? This outwardly simplified memory model would make C++ programming much more complicated as a matter of fact. First, you wouldn't have the RAII idiom. Smart pointers, string classes, file stream objects, threads and mutexes wouldn't exist, unless they were redesigned from scratch in a way that would dictate a complex, error-prone usage protocol with the likelihood of serious performance degradation as a bonus. Secondly, you'd have to get used to a different type of C++ classes: classes that have no destructors at all. Admittedly, some of the RAII classes we use today are the result of the lack of an automatic garbage collector, so if the so-called simplified model is adopted, smart pointers won't be needed in the first place. The problem is that RAII classes aren't just smart pointers. Mutexes, threads and file stream objects allocate various types of resources (locks, file descriptors etc.) that a garbage collector doesn't reclaim. In fact, if you contrast how these classes are implemented in C++ with similar classes in other programming languages that have a built-in garbage collector you will notice that the non-C++ versions entail a more rigid, verbose and manual usage protocol. In these languages, the burden of releasing resources falls on the programmer each time a resource is used.
You could argue that a different simple memory model could be used instead of the simplistic model proposed above. Instead of sacrificing automatic storage for class objects, why not sacrifice the free-store? No one has ever proposed this seriously, but it's not an unprecedented model. In the early days of C (and I mean, very early!) many implementations didn't support malloc(). Under these constraints, programmers had to preallocate as much storage as they thought the program would consume -- at compile-time. Usually, this was done by allocating large char arrays with static (or automatic) storage type and then "allocating" memory from that pool dynamically (this is quite similar to how placement new is used today). There were two serious problems with that technique: either the application had preallocated too little memory, or it had preallocated too much. Remember: in those days memory was measures in bytes and kilobytes, not gigabytes and terabytes so the threshold was very low.
What about static memory? Seemingly, it's the least critical storage type of all three for end users but think what the cost of sacrificing it would be: you wouldn't have cin and cout or any other objects that are initialized automatically before main() starts. In addition, you wouldn't have local static objects. Worse yet, you wouldn't have static data members.
Nearly 40 years ago, Edsger Dijkstra criticized PL/1 for its size and diversity: "using PL/1 must be like flying a plane with 7000 buttons…" he wrote in his Turing Award paper. Contemporary C++ makes PL/1 look slim and parsimonious, but we're not in 1972. In 2008, a successful general-purpose programming language simply can't be cute and small; it must be flexible. Flexibility is achieved among other things by giving programmers a wider choice, at the right places -- exactly what C++ is all about. Is it only a coincidence that C++ flies more airplanes than does any other high-level programming language?