Как я могу сказать, указывает ли указатель на экземпляр производного типа? - PullRequest
1 голос
/ 07 февраля 2011

У меня есть 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); и проверка, равняется ли он нулю, но проблема в том, что приведение всегда успешно, а cInstr никогда не равно нулю.

Как правильно это сделать?

Ответы [ 3 ]

5 голосов
/ 07 февраля 2011

Это плохая ситуация: во-первых, вы обычно не хотите наследовать класс, у которого нет виртуального деструктора;слишком легко сделать неправильную вещь.

Встроенный способ «проверить тип» объекта состоит в том, чтобы попытаться dynamic_cast перейти к этому типу и посмотреть, удастся ли это.Поскольку ваш базовый класс не является полиморфным (он не имеет никаких виртуальных функций-членов), вы не можете сделать это.

Есть по крайней мере несколько вариантов.В порядке от лучшего к худшему:

  • В идеале вы должны изменить свой дизайн: использовать композицию вместо наследования или найти какой-либо способ изменить базовый класс.

  • Не забывайте о том, что объект является ComplexInstruction: там, где вам нужно полагаться на тот факт, что это ComplexInstruction, убедитесь, что у вас есть ComplexInstruction* или ComplexInstruction&.

  • Отслеживайте все simple_instr объекты, которые являются базовыми подобъектами ComplexInstruction объектов.В каждом конструкторе ComplexInstruction сохраните в глобальном списке указатель на его базовый подобъект simple_instr и в деструкторе удалите указатель из списка.Затем вы можете предоставить функцию bool IsComplexInstruction(const simple_instr*), которая проверяет, находится ли simple_instr в списке (под «списком» я подразумеваю концептуально список; либо std::vector, либо std::set, вероятно, будет идеальным, в зависимости от количества объектову вас есть).

4 голосов
/ 07 февраля 2011

Я могу придумать два подхода.Во-первых, пусть каждый конструктор для сложной инструкции хранит свой адрес в наборе и проверяет это перед приведением.Во-вторых, если вы можете определить свой собственный распределитель для всех объектов и сохранить необходимое поле тега перед объектом.Я видел, как оба подхода очень успешно использовались в производственном коде.

Вот подход сета:

#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 голосов
/ 07 февраля 2011

Если simple_instr не имеет никаких виртуальных методов, то нет абсолютно никакого способа сделать это. Если это так, то ComplexInstruction * cInstr = dynamic_cast<ComplexInstruction *>(current) выдаст NULL, если это не производный тип.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...