简体   繁体   中英

How can I tell whether a pointer points to an instance of a derived type?

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 ComplexInstruction* 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 a ComplexInstruction , make sure you have a ComplexInstruction* or ComplexInstruction& .

  • Keep track of all the simple_instr objects that are base subobjects of ComplexInstruction objects. In every ComplexInstruction constructor, save in a global list a pointer to its simple_instr base subobject and in the destructor remove the pointer from the list. You can then provide a function bool IsComplexInstruction(const simple_instr*) that checks whether the simple_instr is in the list (by "list" I mean conceptually a list; either a std::vector or std::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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM