You can't create a polymorphic local variable
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
.
- In case you have dozens of subclasses, and one of them has a large number of attributes, this would waste a lot of space.
- In case you put in the local variable an instance of a subclass of
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.
So allocate space for it yourself
Using placement new, you can initialize the object in a space you allocated through some other means:
However, 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.