SigmaX
SigmaX

Reputation: 483

Initialize Polymorphic Variable on Stack

Coming to C++ with a Java background, I'd like to set up some polymorphic code by initializing a variable of type A with one of two implementations, B or C.

My question is whether there is an easy way to do this on the stack. I have a case where I'm only using A inside the method body, and want it destroyed at the end of the function, so touching the heap is optional.

Here's how I would do it on the heap:

A* a = NULL;
if (p) {
  B* b = new B();
  b->setSomethingImplementationSpecific();
  a = b;
}
else {
  a = new C();
}

doSomething(a);
delete(a);

In practice I'd probably pull this out into a factory method, and use an auto_ptr to avoid the delete(a).

This works, but can I do it on the stack? My thought pattern is something like this:

A* a = NULL;
if (p) {
  B b;
  b.setSomethingImplementationSpecific();
  a = &b;
}
else {
  C c;
  a = &c;
}

doSomething(a);

Now I don't have to bother with delete(a), but doSomething(a) won't work, since the B or C is destroyed when they go out of scope.

I've been trying to figure out a way to do part of it with the ternary operator as well, but I end up both borking up the syntax and taking the address of a temporary -- so am I right that there is no way to do this?

A * const a = &(p ? B() : C());

Advice on whether it's a silly idea to implement polymorphism on the stack in the first place is welcome, but mostly I'm trying to better understand the limits of C/C++ in this area, independently of design sense.

Upvotes: 1

Views: 245

Answers (5)

Casey
Casey

Reputation: 42574

You can do this cleanly using std::aligned_union for storage:

template <typename...T>
using storage_t = typename std::aligned_union<0, T...>::type;

and a custom unique_ptr deleter:

struct placement_deleter {
  template <typename T>
  void operator () (T* ptr) const {
    ptr->~T();
  }
};

template <typename T>
using stack_ptr = std::unique_ptr<T, placement_deleter>;

Resulting in the usage:

storage_t<B, C> storage;
stack_ptr<A> a;
if (p) {
  auto b = new (&storage) B();
  a.reset(b);
  b->setSomethingImplementationSpecific();
} else {
  a.reset(new (&storage) C());
}

doSomething(*a);

See it live at Coliru.

Upvotes: 3

aschepler
aschepler

Reputation: 72431

You could do something like this with boost::optional:

#include <boost/optional.hpp>

void example(bool p) {
  boost::optional<B> b;
  boost::optional<C> c;
  A* a = nullptr;
  if (p) {
    b = B();
    b->setSomethingImplementationSpecific();
    a = b.get_ptr();
  }
  else {
    c = C();
    a = c.get_ptr();
  }
  doSomething(a);
}

Note that b and c must have a long-enough lifetime. But only one of them calls a constructor and destructor for B or C.

Upvotes: 2

Nemo
Nemo

Reputation: 71555

If you are using C++11, you can get "stack semantics" by using a unique_ptr:

std::unique_ptr<A> a = (p ? new B() : new C());

Although the object itself will still be allocated on the heap.

std::auto_ptr<A> is the equivalent idiom in C++03.

Upvotes: 4

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145379

Instead of this original code,

A* a = NULL;
if (p) {
  B* b = new B();
  b->setSomethingImplementationSpecific();
  a = b;
}
else {
  a = new C();
}

doSomething(a);
delete(a);

you can do this:

void doSomething( A const& ) {}

void doBeeDoo( B&& b )
{
    b.doSomethingImeplementationSpecific();
    doSomething( b );
}

void foo()
{
    if( p ) { doBeeDoo( B() ); } else { doSomething( C() ); }
}

Upvotes: 2

R Sahu
R Sahu

Reputation: 206697

What you have won't work. b and c will be deleted from the stack by the time you get to doSomething(a);. However, you could do this:

if (p) {
  B b;
  b.setSomethingImplementationSpecific();
  doSomething(&b);
}
else {
  C c;
  doSomething(&c);
}

Upvotes: 1

Related Questions