【问题标题】:How can I tell whether a pointer points to an instance of a derived type?如何判断指针是否指向派生类型的实例?
【发布时间】:2011-02-07 04:49:42
【问题描述】:

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

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

【问题讨论】:

  • 基类是多态的吗? (即,它有没有虚成员函数?)
  • 不,不幸的是

标签: c++ inheritance casting


【解决方案1】:

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

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

至少有几个选项。从好到坏的顺序:

  • 理想情况下,你应该改变你的设计:使用组合而不是继承,或者想办法改变基类。

  • 不要忘记对象是ComplexInstruction这一事实:无论您需要依赖它是ComplexInstruction这一事实,请确保您拥有ComplexInstruction*或@987654325 @。

  • 跟踪作为ComplexInstruction 对象的基本子对象的所有simple_instr 对象。在每个ComplexInstruction 构造函数中,将指向其simple_instr 基子对象的指针保存在全局列表中,并在析构函数中从列表中删除指针。然后,您可以提供一个函数 bool IsComplexInstruction(const simple_instr*) 来检查 simple_instr 是否在列表中(“列表”是指概念上的列表;std::vectorstd::set 可能是理想的,具体取决于对象的数量你有)。

【讨论】:

    【解决方案2】:

    我可以想到两种方法。首先,让复杂指令的每个构造函数将其地址存储在一个集合中,并在转换之前检查它。其次,如果您可以为所有对象定义自己的分配器,并在对象之前存储必要的标记字段。我已经看到这两种方法都非常成功地用于生产代码。

    这是一套方法:

    #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;
    }
    

    为复合体添加您自己的代码,并确保添加相应的销毁器。

    刚刚想到另一种可能的方法。也许太明显了,您已经考虑过这一点,但是您可以使用 simple_instr 的任何值来标记它真的很复杂吗?如果是这样,你可以写:

     ComplexInstruction* tryCast(simple_instr* instr)
        {
            ComplexInstruction* ret = 0;
            if (hasComplexFlag(instr))
                ret = static_cast<ComplexInstruction*>(instr);
            return ret;
        }
    

    【讨论】:

    • 我在写“绝对不可能”的时候就知道有人会过来证明我错了。干得好!
    【解决方案3】:

    如果simple_instr 没有任何虚方法,那么绝对没有办法做到这一点。如果是这样,那么ComplexInstruction * cInstr = dynamic_cast&lt;ComplexInstruction *&gt;(current) 将在它不是派生类型时给出 NULL。

    【讨论】:

      猜你喜欢
      • 2020-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-26
      • 2012-01-21
      • 2014-06-16
      • 2011-08-09
      相关资源
      最近更新 更多