tags:

views:

470

answers:

6

Is it always necessary to allocate memory from the heap to facilitate dynamic polymorphism? All the examples i've come across so far point to the same. Dynamic memory allocation is usually avoided in real-time-programming.So,is there any disadvantage of using the stack for dynamic polymorphism as shown in the code below.

class Base
{
    public:
        virtual void Display()= 0;
};

class Derived:public Base
{
    public:
        void Display()
        {
            cout << "In derived" << endl;
        }
};

int main()
{
    Base*   base;
    Derived derived1;

    base = &derived1;
    base->Foo();       
    return 0;
}
+5  A: 

A better example would be:

void func(Base &base);

int main()
{
    Derived derived;

    func(derived);

    return 0;
}

The use of polymorphism doesn't have to be near where the object is created on the stack.

A lot of our unit-testing code these days looks roughly like:

void test()
{
    MockObject mockObj;
    RealObject objectToBeTested(mockObj);

    // Do tests
}

This depends intimately on polymorphism, but creates objects on the stack.

David Norman
A: 

I think this is perfectly fine. The only possible drawback is the limited scope of an object created on the stack, but that's not necessarily related to polymorphism.

Igor Oks
A: 

There's no problem with using the stack.

When you're using the stack then you often know exactly what type of object it is ... so there's no need for the method to be virtual, it's harmless but unnecessary, for example:

Derived derived;
derived.Foo();

However, the object (which exists on the stack) can be passed to subroutines which accept a superclass instance as a parameter (in which case the fact that it's a subclass is useful/used):

void foo(Base* base)
{
  ...
}

void test()
{
  Derived derived;
  foo(&derived);
}
ChrisW
A: 

Using the stack will be fine for the simple case you show. One real time issue with dynamic polymorphism vs static is the added time to to go through the indirection of the method call. Which is an extra memory access per method call.

You need to explain more about what your doing to analyze other factors, e.g. is the stack frame guaranteed to be in physical memory, sharing, number of instances, lifespan of instances

jeffD
A: 

Thanks for the help guys. I understand that the example could have been put in a better way. But,that's what i basically meant to say. Thanks again.

vaibhav
Please choose one of the answers as your accepted answer (using the green V). And please don't write anything besides the answers in the "Answers" section, this is not a forum.
Igor Oks
+3  A: 

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).

Johannes Schaub - litb