Reputation: 501
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;
}
Upvotes: 8
Views: 4026
Reputation: 732
I wrote a generic template to do it. Full code available here (it became too elaborate for the scope of this question). StackVariant object contains a buffer of the size of the biggest type out of the provided types, and biggest alignment as well. The Object is constructed on the stack using a 'placement new' and operator->() is used for polymorphic access to suggest the indirection. Also, it is important to make sure that if a virtual detor is defined, it should be called upon destruction of the object on the stack, so the template detor is doing just that using a SFINAE definition.
(see usage example and output below):
// compile: g++ file.cpp -std=c++11
#include <type_traits>
#include <cstddef>
// union_size()/union_align() implementation in gist link above
template<class Tbaseclass, typename...classes>
class StackVariant {
alignas(union_align<classes...>()) char storage[union_size<classes...>()];
public:
inline Tbaseclass* operator->() { return ((Tbaseclass*)storage); }
template<class C, typename...TCtor_params>
StackVariant& init(TCtor_params&&...fargs)
{
new (storage) C(std::forward<TCtor_params>(fargs)...); // "placement new"
return *this;
};
template<class X=Tbaseclass>
typename std::enable_if<std::has_virtual_destructor<X>::value, void>::type
call_dtor(){
((X*)storage)->~X();
}
template<class X=Tbaseclass>
typename std::enable_if<!std::has_virtual_destructor<X>::value, void>::type
call_dtor() {};
~StackVariant() {
call_dtor();
}
};
Usage example:
#include <cstring>
#include <iostream>
#include "StackVariant.h"
class Animal{
public:
virtual void makeSound() = 0;
virtual std::string name() = 0;
virtual ~Animal() = default;
};
class Dog : public Animal{
public:
void makeSound() final { std::cout << "woff" << std::endl; };
std::string name() final { return "dog"; };
Dog(){};
~Dog() {std::cout << "woff bye!" << std::endl;}
};
class Cat : public Animal{
std::string catname;
public:
Cat() : catname("gonzo") {};
Cat(const std::string& _name) : catname(_name) {};
void makeSound() final { std::cout << "meow" << std::endl; };
std::string name() final { return catname; };
};
using StackAnimal = StackVariant<Animal, Dog, Cat>;
int main() {
StackAnimal a1;
StackAnimal a2;
a1.init<Cat>("gonzo2");
a2.init<Dog>();
a1->makeSound();
a2->makeSound();
return 0;
}
// Output:
// meow
// woff
// woff bye!
Few things to note:
Upvotes: 3
Reputation: 7915
Run this short program and you'll see why polymorphic objects do not work on the stack very well. When you create a stack object of a derived type that is unknown and expect it to be returned from a function call, what happens is the object is destroyed when that calling function goes out of scope. Thus the object only lives as long as that function is within scope. In order to return a valid object that will outlive the calling function you need to use the heap. This is demonstrated with this simple hierarchy and two versions of the same function with a switch statement except one does the stack and the other does it on the heap. Look at the output from both implementations and look to see what methods are called, what class they are being called from and when they are being called.
#include <string>
#include <iostream>
class Base {
public:
enum Type {
DERIVED_A = 0,
DERIVED_B,
DERIVED_C
};
protected:
Type type_;
public:
explicit Base(Type type) : type_(type) {
std::cout << "Base Constructor Called." << std::endl;
}
virtual ~Base() {
std::cout << "Base Destructor Called." << std::endl;
}
virtual void doSomething() {
std::cout << "This should be overridden by derived class without making this a purely virtual method." << std::endl;
}
Type getType() const { return type_; }
};
class DerivedA : public Base {
public:
DerivedA() : Base(DERIVED_A) {
std::cout << "DerivedA Constructor Called." << std::endl;
}
virtual ~DerivedA() {
std::cout << "DerivedA Destructor Called." << std::endl;
}
void doSomething() override {
std::cout << "DerivedA overridden this function." << std::endl;
}
};
class DerivedB : public Base {
public:
DerivedB() : Base(DERIVED_B) {
std::cout << "DerivedB Constructor Called." << std::endl;
}
virtual ~DerivedB() {
std::cout << "DerivedB Destructor Called." << std::endl;
}
void doSomething() override {
std::cout << "DerivedB overridden this function." << std::endl;
}
};
class DerivedC : public Base {
public:
DerivedC() : Base(DERIVED_C) {
std::cout << "DerivedC Constructor Called." << std::endl;
}
virtual ~DerivedC() {
std::cout << "DerivedC Destructor Called." << std::endl;
}
void doSomething() override {
std::cout << "DerivedC overridden this function." << std::endl;
}
};
Base* someFuncOnStack(Base::Type type) {
Base* pBase = nullptr;
switch (type) {
case Base::DERIVED_A: {
DerivedA a;
pBase = dynamic_cast<Base*>(&a);
break;
}
case Base::DERIVED_B: {
DerivedB b;
pBase = dynamic_cast<Base*>(&b);
break;
}
case Base::DERIVED_C: {
DerivedC c;
pBase = dynamic_cast<Base*>(&c);
break;
}
default: {
pBase = nullptr;
break;
}
}
return pBase;
}
Base* someFuncOnHeap(Base::Type type) {
Base* pBase = nullptr;
switch (type) {
case Base::DERIVED_A: {
DerivedA* pA = new DerivedA();
pBase = dynamic_cast<Base*>(pA);
break;
}
case Base::DERIVED_B: {
DerivedB* pB = new DerivedB();
pBase = dynamic_cast<Base*>(pB);
break;
}
case Base::DERIVED_C: {
DerivedC* pC = new DerivedC();
pBase = dynamic_cast<Base*>(pC);
break;
}
default: {
pBase = nullptr;
break;
}
}
return pBase;
}
int main() {
// Function With Stack Behavior
std::cout << "Stack Version:\n";
Base* pBase = nullptr;
pBase = someFuncOnStack(Base::DERIVED_B);
// Since the above function went out of scope the classes are on the stack
pBase->doSomething(); // Still Calls Base Class's doSomething
// If you need these classes to outlive the function from which they are in
// you will need to use heap allocation.
// Reset Base*
pBase = nullptr;
// Function With Heap Behavior
std::cout << "\nHeap Version:\n";
pBase = someFuncOnHeap(Base::DERIVED_C);
pBase->doSomething();
// Don't Forget to Delete this pointer
delete pBase;
pBase = nullptr;
char c;
std::cout << "\nPress any key to quit.\n";
std::cin >> c;
return 0;
}
Output:
Stack Version:
Base Constructor Called.
DerivedB Constructor Called.
DerivedB Destructor Called.
Base Destructor Called.
This should be overridden by derived class without making this a purely virtual method.
Heap Version:
Base Constructor Called.
DerivedC Constructor Called.
DerivedC overridden this function.
DerivedC Destructor called.
Base Destructor Called.
I'm not saying that it can not be done; I'm just stating the caveats in trying to do so. It may be ill-advised to try to do something of the sort. I do not know of any way to do this unless if you have a wrapper class that will contain the stack allocated objects to manage their life time. I'll have to try and work on that to see if I can come up with something of the sort.
Upvotes: 0
Reputation: 234594
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:
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.
Upvotes: 7
Reputation: 3244
You can't create a polymorphic local variable, since a subclass B
of A
might have more attributes than A
, thus take more place, so the compiler would have to reserve enough space for the largest subclass of A
.
A
you received as a parameter, and you put your code in a dynamic library, then the code linking with it could declare a subclass larger than those in your library, so the compiler wouldn't have allocated enough space on the stack anyway.Using placement new, you can initialize the object in a space you allocated through some other means:
alloca
, but seeing this SO question it seems it's not the best option.aligned_union<A, B, C>::type
, as suggested by R. Martinho Fernandes in a comment to this answerHowever, these techniques may use a lot of extra space, and don't work if you are given a reference (pointer) to an unknown-at-compile-time subclass of A
that is larger than the types you accounted for.
The solution I propose is to have a kind of factory method on each subclass, that calls a supplied function with a pointer to a stack-allocated instance of the given subclass. I added an extra void* parameter to the supplied function's signature, so one can pass it arbitrary data.
@MooingDuck suggested this implementation using templates and C++11 in a comment below. In case you need this for code that can't benefit from C++11 features, or for some plain C code with structs instead of classes (if struct B
has a first field of type struct A
, then it can be manipulated somewhat like a "substruct" of A
), then my version below will do the trick (but without being type-safe).
This version works with newly defined subclasses, as long as they implement the ugly
factory-like method, and it will use a constant amount of stack for the return address and other informations required by this intermediate function, plus the size of an instance of the requested class, but not the size of the largest subclass (unless you choose to use that one).
#include <iostream>
class A {
public:
int fieldA;
static void* ugly(void* (*f)(A*, void*), void* param) {
A instance;
return f(&instance, param);
}
// ...
};
class B : public A {
public:
int fieldB;
static void* ugly(void* (*f)(A*, void*), void* param) {
B instance;
return f(&instance, param);
}
// ...
};
class C : public B {
public:
int fieldC;
static void* ugly(void* (*f)(A*, void*), void* param) {
C instance;
return f(&instance, param);
}
// ...
};
void* doWork(A* abc, void* param) {
abc->fieldA = (int)param;
if ((int)param == 4) {
((C*)abc)->fieldC++;
}
return (void*)abc->fieldA;
}
void* otherWork(A* abc, void* param) {
// Do something with abc
return (void*)(((int)param)/2);
}
int main() {
std::cout << (int)A::ugly(doWork, (void*)3);
std::cout << (int)B::ugly(doWork, (void*)1);
std::cout << (int)C::ugly(doWork, (void*)4);
std::cout << (int)A::ugly(otherWork, (void*)2);
std::cout << (int)C::ugly(otherWork, (void*)11);
std::cout << (int)B::ugly(otherWork, (void*)19);
std::cout << std::endl;
return 0;
}
By then, I think we might have outweighed the costs of a simple malloc
, so you might wand to use that after all.
Upvotes: 2
Reputation: 19052
You can do it with placement new. This will place the items on the stack, in the memory contained in the buffer. However, these variables are not automatic. The downside is that your destructors won't run automatically, you would need to properly destruct them just as you've created them when they go out of scope.
A reasonable alternative to manually calling the destructor is to wrap your type in a smart pointer, as shown below:
class A
{
public:
virtual ~A() {}
};
class B : public A {};
class C : public B {};
template<class T>
class JustDestruct
{
public:
void operator()(const T* a)
{
a->T::~T();
}
};
void create(int x)
{
char buff[1024] // ensure that this is large enough to hold your "biggest" object
std::unique_ptr<A, JustDestruct<T>> t(buff);
switch(x)
{
case 0:
ptr = new (buff) A();
break;
case 1:
ptr = new (buff) B();
break;
case 2:
ptr = new (buff) C();
break;
}
// do polymorphic stuff
}
Upvotes: 1
Reputation: 11782
If B is your base types D1, D2, and D3 are your derived types:
void foo()
{
D1 derived_object1;
D2 derived_object2;
D3 derived_object3;
B *base_pointer;
switch (some_var)
{
case 1: base_pointer = &derived_object1; break;
....
}
}
If you want to avoid wasting the space of the three derived objects, you could break up your method into two parts; the part that chooses which type you need, and the part of the method that works on it. Having decided which type you need, you call a method that allocates that object, creates a pointer to it, and calls the second half of the method to complete the work on the stack-allocated object.
Upvotes: 4
Reputation: 67782
It is possible, but it's a lot of effort to do cleanly (without manual placement new and exposed raw buffers, that is).
You're looking at something like Boost.Variant, modified to restrict the types to a base class and some derived classes, and to expose a polymorphic reference to the base type.
This thing (PolymorphicVariant ?) would wrap all the placement new stuff for you (and also take care of safe destruction).
If it's really what you want, let me know and I'll give you a start. Unless you really need exactly this behaviour though, Mike Seymour's suggestion is more practical.
Upvotes: 0
Reputation: 254631
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;
}
Upvotes: 7
Reputation: 2554
trying to avoid heap allocation with new)?
Well in that case you create object on stack as usual and assign address to the base pointer. But remember, if this is done inside a function, don't pass the address as return value, because stack will unwind after the function call returns.
So this is bad.
A* SomeMethod()
{
B b;
A* a = &b; // B inherits from A
return a;
}
Upvotes: 0
Reputation: 258618
To strictly answer your question - what you have now does just that - i.e. a = A();
and a = B()
and a = C()
, but these objects are sliced.
To achieve polymorphic behavior with the code you have, I', afraid that's not possible. The compiler needs to know the size beforehand of the object. Unless you have references or pointers.
If you use a pointer, you need to make sure it doesn't end up dangling:
A* a = NULL;
switch (some_var)
{
case 1:
A obj;
a = &obj;
break;
}
won't work because obj
goes out of scope. So you're left with:
A* a = NULL;
A obj1;
B obj2;
C obj3;
switch (some_var)
{
case 1:
a = &obj1;
break;
case 2:
a = &obj2;
break;
case 3:
a = &obj3;
break;
}
This of course is wasteful.
For references it's a bit trickier because they have to be assigned on creation, and you can't use temporaries (unless it's a const
reference). So you'll probably need a factory that returns a persistent reference.
Upvotes: 0
Reputation: 109219
A combination of a char
array and placement new
would work.
char buf[<size big enough to hold largest derived type>];
A *a = NULL;
switch (some_var)
{
case 1:
a = new(buf) A;
break;
case 2:
a = new(buf) B;
break;
default:
a = new(buf) C;
break;
}
// do stuff with a
a->~A(); // must call destructor explicitly
Upvotes: 0
Reputation: 8831
Polymorphism doesn't work with values, you need a reference or a pointer. You can use a const reference to a temporary object polymorphically and it will have the lifetime of a stack object.
const A& = (use_b ? B() : A());
If you need to modify the object, you have no choice but to dynamically allocate it (unless you're using Microsoft's non-standard extension that lets you bind a temporary object to a non-const reference).
Upvotes: 0