Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I create a polymorphic object on the stack?

How do I allocate a polymorphic object on the stack? I'm trying to do something similar to (trying to avoid heap allocation with new)?:

A* a = NULL;

switch (some_var)
{
case 1:
    a = A();
    break;
case 2:
    a = B(); // B is derived from A
    break;
default:
    a = C(); // C is derived from A
    break;
}
like image 975
user1040229 Avatar asked Aug 15 '12 17:08

user1040229


People also ask

What is polymorphic object in C++?

Polymorphism in C++ means, the same entity (function or object) behaves differently in different scenarios. Consider this example: The “ +” operator in c++ can perform two specific functions at two different scenarios i.e when the “+” operator is used in numbers, it performs addition.

What is polymorphism with example in OOP?

Polymorphism is one of the OOPs feature that allows us to perform a single action in different ways. For example, lets say we have a class Animal that has a method sound() . Since this is a generic class so we can't give it a implementation like: Roar, Meow, Oink etc. We had to give a generic message.

How do you handle polymorphism?

You can create a list of type []Intf where Intf is an interface common to your types, and then you can put any type that implements that interface into your list. With the first one, if f modified the array elements, the variables a and b are modified because the interface contains a pointer.


2 Answers

You can't structure a single function to work like that, since automatic or temporary objects created inside a conditional block can't have their lifetimes extended into the containing block.

I'd suggest refactoring the polymorphic behaviour into a separate function:

void do_something(A&&);

switch (some_var)
{
case 1:
    do_something(A());
    break;
case 2:
    do_something(B()); // B is derived from A
    break;
default:
    do_something(C()); // C is derived from A
    break;
}
like image 106
Mike Seymour Avatar answered Nov 01 '22 17:11

Mike Seymour


Disclaimer: I definitely don't think this is a good solution. The good solutions are to either rethink the design (maybe OO polymorphism is not warranted here given that there is a bounded number of possibilities?), or to use a second function to pass along said polymorphic object by reference.

But since other folks mentioned this idea, but got details wrong, I'm posting this answer to show how to get it right. Hopefully I get it right.

It is clear the the number of possible types is bounded. This means that a discriminated union, like boost::variant could solve the problem, even if it's not pretty:

boost::variant<A, B, C> thingy = 
    some_var == 1? static_cast<A&&>(A())
    : some_var == 2? static_cast<A&&>(B())
    : static_cast<A&&>(C());

The fact that now you can use things like static visitors is one if the things that keeps making me think this isn't a good use of OO polymorphism.

If instead of a ready-made solution, you want to use placement new by hand as suggested in other answers, there are a number of things that need care because we lose some of the properties of regular automatic objects in the process:

  • the compiler no longer gives us the right size and alignment;
  • we no longer get an automatic call to the destructors;

In C++11, these are both easy to fix with aligned_union and unique_ptr, respectively.

std::aligned_union<A, B, C>::type thingy;
A* ptr;
switch (some_var)
{
case 1:
    ptr = ::new(&thingy.a) A();
    break;
case 2:
    ptr = ::new(&thingy.b) B();
    break;
default:
    ptr = ::new(&thingy.c) C();
    break;
}
std::unique_ptr<A, void(*)(A*)> guard { ptr, [](A* a) { a->~A(); } };
// all this mechanism is a great candidate for encapsulation in a class of its own
// but boost::variant already exists, so...

For compilers that don't support these features, you can get alternatives: Boost includes aligned_storage and alignment_of traits which can be used to build aligned_union; and unique_ptr can be replaced with some kind of scope guard class.

Now that that is out of the way, just so it's clear, don't do this and simply pass a temporary along to another function, or revisit the design altogether.

like image 39
R. Martinho Fernandes Avatar answered Nov 01 '22 18:11

R. Martinho Fernandes