2010-12-17 4 views
6

일부 코드가 Windows로 이식되고 난처한 상태입니다. 포인터를 포인터에 복사하기 위해 실행시 자동으로 실행되는 일부 코드가 있으며, 포인터가 null이 아닌 경우 포인터를 포인터로 삭제하기 위해 종료시 다시 실행되는 코드가 있습니다.포인터가없는 경우 포인터에서 포인터로 충돌이 발생합니다.

나는 동작 더 클래스 계층 구조는 다음과 같습니다

int main() 
{ 
    // Pointer to a Pointer, current crash. 
    InterfaceClass** ptrptr; 
    ConcreteTwo* object = new ConcreteTwo(); 
    ptrptr = (InterfaceClass**)(&object); // cast is required for some reason. 
    delete *ptrptr; // Crash here. 

    // Single pointer, works fine. 
    InterfaceClass* ptrptr; 
    ConcreteTwo* object = new ConcreteTwo(); 
    ptrptr = object; 
    delete ptrptr; 

    // There are other cases where there are only 3 classes in the hierarchy. 
    // This also works fine. 
    InterfaceClass** ptrptr; 
    ConcreteOne* object = new ConcreteOne(); 
    ptrptr = (InterfaceClass**)(&object); 
    delete *ptrptr; 

    return 0; 
} 

을 재현하는 샘플 프로그램을 만들었습니다. 기본 클래스는 일부 순수 가상 함수가있는 인터페이스이며 많은 객체가 둘 이상의 위치에서 잠재적으로 상속받을 수있는 방식으로 프로그램 전체에 여러 클래스에 포함됩니다. 이 때문에 구체적인 구현은 "public virtual InterfaceClass"로 확장해야합니다. 이 예에서 "가상"을 삭제하면 충돌이 해결됩니다.

class InterfaceClass { 
public: 
    virtual ~InterfaceClass() {}; 
    InterfaceClass() {} 
}; 

class ConcreteClass : public virtual InterfaceClass { 
public: 
    ConcreteClass() { } 
    virtual ~ConcreteClass() {} 
}; 

class ConcreteOne : public ConcreteClass 
{ 
public: 
    ConcreteOne(void) {} 
    virtual ~ConcreteOne(void) {} 
}; 

class ConcreteTwo : public ConcreteOne 
{ 
public: 
    ConcreteTwo(void) {} 
    virtual ~ConcreteTwo(void) {} 
}; 

답변

5

포인터의 유형이 가리키는 유형과 관련이 거의 없다는 사실을 잘 알고 있습니까? 즉, T1에서 T1이 T2로부터 상속을 받으면 T1 *도 T2 *를 상속 받았다고합니다. 그것은 틀린 것입니다. 자, 어떻게 당신의 현재 상황에 적용됩니까?

InterfaceClass** ptrptr; 
    ConcreteTwo* object = new ConcreteTwo(); 
    ptrptr = (InterfaceClass**)(&object); // cast is required for some reason. 

다음은 C 스타일 캐스팅의 주요 문제점입니다. 좋아요, 그래서 그것은 약간의 수평 공간을 절약 할 수 있습니다. 그러나 여러분은 어떤 종류의 캐스트를 방금했는지 알 수 있습니까? 그것은 당신이 생각하는 것이 아닙니다. reintpret_cast을 ConcreteTwo * 유형에서 관련없는 유형 InterfaceClass *로 실제로 수행했습니다! 이제 포인터 주소는 당신이 말하는 타입과 아무런 관련이 없습니다.

그런 다음 재 해석 된 포인터 유형을 삭제에 던지면 곧바로 자신의 괄약어를 위반하게됩니다.

3

음, 컴파일러는 당신이 캐스팅 할 수없는

... 당신이 그것을 당신의 방법을하기로 결정, 당신을 경고 : ConcreteTwo

ptrptr = (InterfaceClass**)(&object); 

object 때문에 포인트를한다 InterfaceClass과 같지 않습니다. InterfaceClass 하위 오브젝트 ConcreteTwo은 다른 주소에 있습니다. *ptrptrInterfaceClass의 인스턴스에 대한 포인터가 아닙니다.

delete에 전달하는 포인터는 에 대한 포인터이지만 컴파일러에서는 InterfaceClass에 대한 포인터라고합니다. delete은 그것이 실제로는 InterfaceClass이라고 가정하므로 충돌이 발생합니다.

1

나는 문제가 주조 라인에 있다고 생각합니다. BTW, 삽입 한 캐스트를 제거하면 컴파일러에서 정확히 무엇이 문제인지 알려줍니다.

당신이 정말로, 난 강력하게 무리를 줄 방법을,이 작업을 수행하려면 먼저 임시 만들어야합니다

ConcreteTwo* object = new ConcreteTwo(); 
InterfaceClass* ptr = object; 

다음 해당 주소를 타고 ptrptr 변수에 할당 할 수 있습니다

InterfaceClass** ptrptr = &ptr; 

지금 당신은 안전하게 삭제할 수 있습니다

delete *ptrptr; 

ptrptrptr 이전의 범위를 벗어나면 삭제가 다시 중단 될 수 있습니다.

나머지는 노아가 코드가 작동하지 않는 이유를 설명합니다.

관련 문제