2011-02-07 3 views
1

수정할 수없는 struct simple_instr이 있습니다. 그래서 같이 그것에서 파생 된 유형을 생성 :포인터가 파생 된 유형의 인스턴스를 가리키는 지 어떻게 알 수 있습니까?

struct ComplexInstruction : simple_instr 
{ 
    ComplexInstruction(const simple_instr& simple) : simple_instr(simple) 
    { 
    } 

    bool isHead; 
    bool isTail; 
    bool isPreHeader; 
}; 

내가 simple_instr의 인스턴스가 실제로 ComplexInstruction인지 말할 수 있어야합니다.

ComplexInstruction comInstr = *current; // current is a pointer to a simple_instr 
    ComplexInstruction* cInstr = &comInstr; 

내가 ComplexInstruction* cInstr = static_cast<ComplexInstruction*>(current); 를 사용하여, 그것이 null 말았 경우 확인했지만, 문제는 캐스트는 항상 성공할이며, cInstr가 null로 동일 결코 : 나는과 같이 ComplexInstructions을 만들 수 있습니다.

이 작업을 수행하는 올바른 방법은 무엇입니까?

+1

기본 클래스는 다형성입니까? (즉, 가상 멤버 함수가 있습니까?) –

+0

아니요. 불행히도 – Megatron

답변

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

는 단지에 대한 자신의 코드를 추가하고 해당 구축함을 추가해야합니다.

다른 가능한 접근 방식을 생각해보십시오. 아마도 너무 명백하고 이미 이것을 고려해 보았지만 simple_instr에 대해 사용할 수있는 어떤 가치가있어 실제로 복잡하다는 것을 나타낼 수 있습니까? 그렇다면 다음과 같이 쓸 수 있습니다 :

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

누군가가 와서 틀리게 증명할 때 "절대적으로 절대적으로"쓰고있을 때 알았습니다. 잘 했어! –

3

simple_instr에는 가상 방법이 없으면이를 수행 할 수있는 방법이 전혀 없습니다. 그렇다면 ComplexInstruction * cInstr = dynamic_cast<ComplexInstruction *>(current)은 파생 형식이 아닌 경우 NULL을 제공합니다.

+0

젠장 .. 괜찮아. 고마워. – Megatron

5

이것은 나쁜 상황입니다. 하나는 일반적으로 가상 소멸자가없는 클래스에서 파생되기를 원하지 않습니다. 잘못된 일을하는 것은 너무 쉽습니다.

개체의 "유형을 확인"하는 기본 제공 방법은 해당 유형에 dynamic_cast을 시도하고 성공했는지 확인하는 것입니다. 기본 클래스는 다형성이 아니기 때문에 (가상 멤버 함수가 없음)이 작업을 수행 할 수 없습니다.

몇 가지 옵션이 있습니다. 위해 더 나은의 악화로 : 상속 대신 사용 조성 또는 기본 클래스를 변경할 수있는 몇 가지 방법을 찾을 : 이상적으로

  • , 당신은 당신의 디자인을 변경해야합니다. 당신이이 ComplexInstruction이라는 사실에 의존 할 필요가 어디에 있든, 당신이 ComplexInstruction* 또는 ComplexInstruction&이 있는지 확인 :

  • 는 객체가 ComplexInstruction 사실의 트랙을 잃지 마십시오. ComplexInstruction 객체의 기본 하위 객체있는 모든 simple_instr 객체의

  • 보관할 트랙. 모든 ComplexInstruction 생성자에서 전역 목록에 simple_instr 기본 하위 개체에 대한 포인터를 저장하고 소멸자에서 목록에서 포인터를 제거합니다. 그런 다음 simple_instr이 목록에 있는지 확인하는 함수 bool IsComplexInstruction(const simple_instr*)을 제공 할 수 있습니다 (목록에있는 개념적 목록을 의미 함). std::vector 또는 std::set은 보유하고있는 객체 수에 따라 이상적입니다.

관련 문제