2009-03-02 6 views
1

저는 게임을하고 있는데 현재 입력을 처리하는 부분에 대해 작업하고 있습니다. 이 클래스에는 세 가지 클래스가 있으며 레벨을 시작하는 ProjectInstance 클래스가 있습니다. 입력을 처리 할 GameControllerGameController으로 결정되는 컨트롤의 영향을받는 PlayerEntity이 있습니다. 레벨을 시작하면 ProjectInstance가 GameController을 만들고 게임 루프 내부에서 호출되는 Step 메서드에서 EvaluateControls 메서드를 호출합니다. EvaluateControls 방법은 조금 다음과 같습니다포인터가 신비하게 NULL로 재설정됩니다.

void CGameController::EvaluateControls(CInputBindings *pib) { 
    // if no player yet 
    if (gc_ppePlayer == NULL) { 
     // create it 
     Handle<CPlayerEntityProperties> hep = memNew(CPlayerEntityProperties); 
     gc_ppePlayer = (CPlayerEntity *)hep->SpawnEntity(); 
     memDelete((CPlayerEntityProperties *)hep); 
     ASSERT(gc_ppePlayer != NULL); 
     return; 
    } 

    // handles controls here 
} 

이 기능을 제대로 호출하고 어설 트리거되지 않습니다. 그러나이 함수가 호출 될 때마다 gc_ppePlayer은 NULL로 설정됩니다. 보시다시피 범위를 벗어나는 지역 변수가 아닙니다. gc_ppePlayer이 NULL로 설정 될 수있는 유일한 장소는 생성자 또는 가능하면 소멸자에 있으며 둘 다 EvaluateControls을 호출하는 사이에 호출되지 않습니다. 디버깅 할 때 gc_ppePlayer은 반환 전에 정확하고 예상되는 값을받습니다. 한 번 더 F10 키를 누르고 커서가 닫는 중괄호에 있으면 값은 0xffffffff로 바뀝니다. 나는 여기서 잃어 버렸어. 어떻게 이런 일이 일어날 수 있니? 누군가?

답변

2

릴리스 또는 디버그 구성을 디버깅하고 있습니까? 릴리스 빌드 구성에서 디버거에 표시되는 내용이 항상 올바른 것은 아닙니다. 최적화가 이루어 지므로 시계 창에보고있는 것과 같이 기발한 값을 표시 할 수 있습니다.

실제로 ASSERT 트리거링이 표시됩니까? ASSERT는 일반적으로 릴리스 빌드에서 컴파일되므로 ASSERT가 응용 프로그램을 종료시키지 않는 이유는 릴리스 빌드를 디버깅하는 것입니다.

소프트웨어의 디버그 버전을 빌드 한 다음 gc_ppePlayer가 실제로 NULL인지 확인하는 것이 좋습니다. 실제로이 포인터가 무시되는 메모리 힙 손상을보고있는 것일 수 있습니다. 그러나 메모리 손상이라면 일반적으로 설명하는 것보다 훨씬 결정적이지 않습니다.

이와 같이 전역 포인터 값을 사용하는 것은 일반적으로 나쁜 습관으로 간주됩니다.진정으로 단일 객체이고 전역 적으로 액세스 할 수 있어야하는 경우 싱글 톤 클래스로 대체 할 수 있는지 확인하십시오.

+0

첫 번째 부분에 대한 지시 사항을 추가 할 것입니다, 그냥 내가 말하려고했는데,하지만 싱글 톤은 마찬가지로 글로벌 데이터 (안티 - 내 책의 패턴) 간단한 인스턴스화 및 참조/포인터가 더 모듈화되어 있습니다. –

+0

@ 로버트 - 싱글 톤은 오용되었을 때 안티 패턴이 될 수 있다고 생각합니다. 종종 그렇습니다. 객체가 실제로 시스템의 단일 객체이고 합법적으로 그 중 하나 이상일 수없는 경우, 싱글 톤 패턴이 가장 적합한 객체가 아닌가? 원시 포인터/참조보다 나은 ... – LeopardSkinPillBoxHat

+0

.. 적어도 당신은 그것에 대한 액세스를 추적 할 수 있기 때문에. – LeopardSkinPillBoxHat

5

해당 식의 값이 변경되면 (NULL 또는 NULL로) gc_ppePlayer == NULL에 감시 지점을 설정하면 디버거에서 정확한 위치를 알려줍니다.

어떻게 되나요? 끝나지 않은 문자열이나 mempcy 복사가 너무 작아서 메모리에 있는지 확인하십시오. 일반적으로 전역/스택 변수가 임의로 덮어 쓰여지는 문제의 원인입니다.

새로운,
  • 클릭 데이터 중단 점을 클릭

    1. 이동 중단 점 창
    2. 에 VS2005에서 감시 점 (brone에 의해 지시)를 추가합니다. 주소 상자에
    3. &gc_ppePlayer을 입력하고 다른 값만 남겨 둡니다.
    4. 그런 다음 실행하십시오. gc_ppePlayer 변경, 중단 이 타격을받을 것입니다

    . - 브론

  • +0

    흠, 내가 정확히해야합니까? 조사 식 대화 상자가 있지만 그게 무슨 뜻인지는 생각하지 않습니다. Visual Studio 2005를 사용하고 있습니다. – Aistina

    +0

    VS에서 경험이 없지만 해당 소금의 가치가있는 디버거는 감시 점 (essentiall)을 값의 변경에 따라 트리거되는 데이터에 중단 점으로 설정할 수 있어야합니다. 다음 질문은 VS2005에서 워치 포인트를 설정하는 방법입니다.) – hhafez

    +0

    @hhafez - VS2005에서 값이 변경되면이를 감지 할 수 있는지 몰랐습니다. 난 당신이 방금 여러 가지 주요 위치에 중단 점을 설정하고 값이 원하는 값으로 변경되었는지 확인해야한다고 생각했습니다 (조건부 중단 점을 사용하여이를 달성 할 수도 있습니다). – LeopardSkinPillBoxHat

    2

    첫 번째 생각은 memDelete()가 호출 될 때 SpawnEntity()가 "지워진"내부 멤버에 대한 포인터를 반환한다고 생각하는 것입니다. 포인터가 0xffffffff로 설정되면 분명하지 않지만 memDelete() 호출 중에 발생하면 ASSERT가 실행되지 않는 이유가 설명됩니다. 0xffffffff는 NULL과 다릅니다.

    전체 코드 기반을 다시 작성한 지 얼마나 되었습니까? 나는 지금과 같은 메모리 문제를 보아서 전체 솔루션을 간단히 재 구축함으로써 해결할 수 있습니다.

    함수의 끝 부분에서 단계 (F10) 대신 단계 (F11)를 시도 했습니까? 귀하의 예제가 로컬 변수를 보여주지는 않지만 간단하게하기 위해 일부만 남겨 두었을 것입니다. 그렇다면, F11은 소멸자에게 그 변수들 중 하나에 대해 단계적으로 들어가서 그 중 하나가 문제를 일으키는지를 볼 수있게합니다.

    +0

    +1. 이것은 가장 가능성있는 설명 인 것 같습니다. –

    +0

    이 함수의 유일한 vars는 반환 값 아래에 있으므로 소멸자가 호출되지 않습니다. 어쨌든, 내가 사용하고있는 SDK의 주석은 CreateEntity가 엔티티를 (에디터에서 내부적으로 사용하기 위해) 해당 속성에 연결하고, SpawnEntity가 재생 중에 엔티티를 생성하기 위해 seperator라고 말합니다. – Aistina

    +0

    memDelete() 호출을 일시적으로 제거하면 어떻게됩니까? 포인터가 그대로 유지됩니까? –

    0

    "핵심에 판소리가 있습니다."

    동적 초기화는 메모리의 여러 비트 (sic)를 덮어 씁니다.

    직접 또는 간접적으로 전역을 덮어 씁니다. 힙에 상대적인 전역 메모리는 어디에 있습니까?

    바이너리가 문제가 없어 질 때까지 동적으로 초기화 된 부분을 잘라냅니다. (한 번에 반씩 주석으로 반복)

    0

    현재 사용중인 플랫폼에 따라 무료 또는 유료로 제공되는 도구로 이러한 종류의 메모리 문제를 신속하게 파악할 수 있습니다. 내 머리 위로 떨어져

    :

    관련 문제