如何判断指针是否指向派生类型的实例?

时间:2011-02-07 04:49:43

标签: c++ inheritance casting

我有一个我无法修改的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。

这样做的正确方法是什么?

3 个答案:

答案 0 :(得分:5)

这是一个糟糕的情况:首先,你通常不希望从没有虚拟析构函数的类派生;做错事太容易了。

“检查对象类型”的内置方法是尝试dynamic_cast到该类型并查看是否成功。由于您的基类不是多态的(它没有任何虚拟成员函数),因此您无法执行此操作。

至少有几个选择。按顺序越来越好:

  • 理想情况下,您应该更改设计:使用合成而不是继承或找到某种方法来更改基类。

  • 不要忘记对象是ComplexInstruction的事实:无论您需要依赖于ComplexInstruction的事实,请确保您拥有ComplexInstruction* 1}}或ComplexInstruction&

  • 跟踪作为simple_instr个对象的基础子对象的所有ComplexInstruction个对象。在每个ComplexInstruction构造函数中,在全局列表中保存指向其simple_instr基础子对象的指针,并在析构函数中从列表中删除指针。然后,您可以提供一个功能bool IsComplexInstruction(const simple_instr*),用于检查simple_instr是否在列表中(按“列表”,我的意思是概念上的列表;可能是std::vectorstd::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。