Reputation: 483
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
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);
Upvotes: 3
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
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
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
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