I have a struct simple_instr that I can't modify. I create a derived type from it as so:
struct ComplexInstruction : simple_instr
{
ComplexInstruction(const simple_instr& simple) : simple_instr(simple)
{
}
bool isHead;
bool isTail;
bool isPreHeader;
};
I want to be able to tell whether an instance of simple_instr is actually a ComplexInstruction. I create ComplexInstructions like so:
ComplexInstruction comInstr = *current; // current is a pointer to a simple_instr
ComplexInstruction* cInstr = &comInstr;
I tried using
ComplexInstructi开发者_C百科on* cInstr = static_cast<ComplexInstruction*>(current);
and checking if it equaled null, but the problem is that the cast always succeeds, and cInstr is never equal to null.
What's the proper way to do this?
This is a bad situation to be in: for one thing, you usually don't want to derive from a class that doesn't have a virtual destructor; it's too easy to do the wrong thing.
The built-in way to "check the type" of an object is to try dynamic_cast
ing to that type and seeing whether that succeeds. Since your base class isn't polymorphic (it doesn't have any virtual member functions), you can't do this.
There are at least a few options. In order from better to worse:
Ideally, you should change your design: use composition instead of inheritance or find some way to change the base class.
Don't lose track of the fact that the object is a
ComplexInstruction
: wherever you need to rely on the fact that it is aComplexInstruction
, make sure you have aComplexInstruction*
orComplexInstruction&
.Keep track of all the
simple_instr
objects that are base subobjects ofComplexInstruction
objects. In everyComplexInstruction
constructor, save in a global list a pointer to itssimple_instr
base subobject and in the destructor remove the pointer from the list. You can then provide a functionbool IsComplexInstruction(const simple_instr*)
that checks whether thesimple_instr
is in the list (by "list" I mean conceptually a list; either astd::vector
orstd::set
would probably be ideal, depending on how many objects you have).
I can think of two approaches. Firstly, have each constructor for a complex instruction store its address in a set, and check this before casting. Secondly, if you can define youir own allocator for all the objects, and store the necessary tag field before the object. I've seen both approaches used very sucessfully in production code.
Here's the set approach:
#include <assert.h>
#include <set>
// Can't be touched!
struct simple_instr
{
};
struct ComplexInstruction : simple_instr
{
ComplexInstruction(const simple_instr& simple) ;
~ComplexInstruction();
bool isHead;
bool isTail;
bool isPreHeader;
};
std::set<simple_instr*> complexInstructions;
ComplexInstruction::ComplexInstruction(const simple_instr& simple) : simple_instr(simple)
{
complexInstructions.insert(this);
}
ComplexInstruction::~ComplexInstruction()
{
complexInstructions.erase(this);
}
ComplexInstruction* tryCast(simple_instr* instr)
{
ComplexInstruction* ret = 0;
if (complexInstructions.find(instr) != complexInstructions.end())
ret = static_cast<ComplexInstruction*>(instr);
return ret;
}
int test()
{
simple_instr si;
ComplexInstruction* siCast = tryCast(&si);
assert(!siCast);
ComplexInstruction ci(si);
ComplexInstruction* ciCast = tryCast(&ci);
assert(ciCast);
return 0;
}
The allocator approach is on these lines:
enum InstructionType { eSimple, eComplex } ;
simple_instr* createSimple()
{
// Highly naive - MUST make sure on alignment.
size_t storage = sizeof(InstructionType) + sizeof(simple_instr);
void* raw = malloc(storage);
InstructionType* header = reinterpret_cast<InstructionType*>(raw);
*header = eSimple;
simple_instr* ret = reinterpret_cast<simple_instr* >(header + 1);
return ret;
}
Add your own code for the Complex, and make sure to add corresponding destroyers.
Just thought of another possible approach. Perhaps too obvious and you have already considered this, but is there any value you can use for simple_instr to flag that it is really complex? If so, you can write:
ComplexInstruction* tryCast(simple_instr* instr)
{
ComplexInstruction* ret = 0;
if (hasComplexFlag(instr))
ret = static_cast<ComplexInstruction*>(instr);
return ret;
}
If simple_instr
doesn't have any virtual methods, then there is absolutely no way to do this. If it does, then ComplexInstruction * cInstr = dynamic_cast<ComplexInstruction *>(current)
will give NULL if it's not the derived type.
精彩评论