我有一个我无法修改的struct simple_instr。我从中创建了一个派生类型:
struct ComplexInstruction : simple_instr
{
ComplexInstruction(const simple_instr& simple) : simple_instr(simple)
{
}
bool isHead;
bool isTail;
bool isPreHeader;
};
我希望能够判断simple_instr的实例是否实际上是ComplexInstruction。我像这样创建ComplexInstructions:
ComplexInstruction comInstr = *current; // current is a pointer to a simple_instr
ComplexInstruction* cInstr = &comInstr;
我尝试过使用
ComplexInstruction* cInstr = static_cast<ComplexInstruction*>(current);
并检查它是否等于null,但问题是转换总是成功,并且cInstr永远不会等于null。
这样做的正确方法是什么?
答案 0 :(得分:5)
这是一个糟糕的情况:首先,你通常不希望从没有虚拟析构函数的类派生;做错事太容易了。
“检查对象类型”的内置方法是尝试dynamic_cast
到该类型并查看是否成功。由于您的基类不是多态的(它没有任何虚拟成员函数),因此您无法执行此操作。
至少有几个选择。按顺序越来越好:
理想情况下,您应该更改设计:使用合成而不是继承或找到某种方法来更改基类。
不要忘记对象是ComplexInstruction
的事实:无论您需要依赖于ComplexInstruction
的事实,请确保您拥有ComplexInstruction*
1}}或ComplexInstruction&
。
跟踪作为simple_instr
个对象的基础子对象的所有ComplexInstruction
个对象。在每个ComplexInstruction
构造函数中,在全局列表中保存指向其simple_instr
基础子对象的指针,并在析构函数中从列表中删除指针。然后,您可以提供一个功能bool IsComplexInstruction(const simple_instr*)
,用于检查simple_instr
是否在列表中(按“列表”,我的意思是概念上的列表;可能是std::vector
或std::set
是理想的,取决于你有多少物体。
答案 1 :(得分:4)
我可以想到两种方法。首先,让复杂指令的每个构造函数将其地址存储在一个集合中,并在转换之前检查它。其次,如果您可以为所有对象定义自己的分配器,并在对象之前存储必要的标记字段。我已经看到这两种方法在生产代码中使用非常成功。
以下是设定的方法:
#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;
}
分配器方法在以下几行:
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;
}
为Complex添加您自己的代码,并确保添加相应的驱逐舰。
想到另一种可能的方法。也许太明显了,你已经考虑过了,但是你可以使用simple_instr来标记它真的很复杂吗?如果是这样,你可以写:
ComplexInstruction* tryCast(simple_instr* instr)
{
ComplexInstruction* ret = 0;
if (hasComplexFlag(instr))
ret = static_cast<ComplexInstruction*>(instr);
return ret;
}
答案 2 :(得分:3)
如果simple_instr
没有任何虚拟方法,那么绝对没有办法做到这一点。如果是,那么ComplexInstruction * cInstr = dynamic_cast<ComplexInstruction *>(current)
如果不是派生类型,则会给出NULL。