You don't have to use the heap if you want to use polymorphism, as you pointed out in your question. But you often have no other choice. Small contrived example:
void doSomething(int what) {
// figure out which implementation to create
Base * b;
if(doA) {
b = new ConcreteA; // 1a
} else if(doB) {
b = new ConcreteB; // 1b
}
...
b->...; // 2
}
You can't use the stack, because at the moment you know what to do, 1a and 1b, every storage you get from the stack will be reclaimed when that scope is left again. You have to use the heap because you need some storage that lasts that local scope.
Some libraries advertise with them being able to not use the heap, but still behave polymorphic. They usually do that with placement new:
void doSomething(int what) {
// allocate form *the stack*, outside the if blocks, so this
// storage lasts until the function returns
char buffer[MAX(sizeof (ConcreteA), sizeof(ConcreteB))];
if(doA) {
new (buffer) ConcreteA; // 1a
} else if(doB) {
new (buffer) ConcreteB; // 1b
}
Base *b = static_cast<Base*>(static_cast<void*>(buffer));
b->...; // 2
}
The new calls in 1a and 1b now use the buffer created on the stack as the storage for the created object. So, no heap memory allocation is required anymore. That form of allocation has the main disadvantage that it's currently not possible in C++ to tell whether the buffer is correctly aligned for the types ConcreteA and ConcreteB though. So, it can be that the array is aligned on a 2 byte boundary, but the objects are required to be created on a 4 byte boundary, resulting in undefined behavior when you try to create those objects into that buffer.
Boost.Function is one of those libraries that use such a placement new approach to create objects of polymorphic types without using heap allocation using a small buffer (hence, what it does is called small buffer optimization).