2012-04-26 1 views
4

저는 Connect 4 게임을 디자인하고 있습니다. 지금까지 기본 클래스가 4 개 있습니다 :`this '가 0x0와 같은 이유로 내 프로그램이 중단되는 이유는 무엇입니까?

Colour - 색상 표현 (RGBA)을 담당합니다. 변환 연산자가 포함되어 있습니다.

Player - 게임의 플레이어를 나타냅니다. 각 Player에는 Colour과 이름이 있습니다.

Board - 재생 보드를 나타냅니다. 여기에는 치수뿐만 아니라 해당 치수의 Tile2 차원 벡터가 포함됩니다.

Tile - 중첩 된 클래스는 Board입니다. 보드의 한 칸을 나타냅니다. 각 TileColour이고 해당 타일의 소유자는 std::unique_ptr입니다. 소유자는 nullptr으로 시작하며 에서 Player으로 한 번 변경할 수 있습니다. 색상은 투명한 검은 색으로 시작됩니다.


Colour 클래스를 테스트했는데 정상적으로 작동하는 것 같습니다. 내 Player 클래스도 tip-top 모양입니다. 그러나 Board/Tile 클래스에 문제가 있습니다.

내 테스트는 두 명의 플레이어와 보드를 만드는 것으로 구성되었습니다. 이들은 정상적으로 실행됩니다. 다음으로, 각 타일에 대해 보드의 치수를 반복합니다. 그때 j, 당신이 그것을 인쇄 기대하는 방식으로 i와 행과 열을 통해

board.tile (j, i).claimBy (p2); 

루프가 간다를 호출합니다.

tile (j, i)은 내가 작업중인 타일을 가져옵니다. 그것은 예상대로 작동합니다. 크래시에 주요 이벤트의


체인 :

claimBy (p2)는 타일을 설정은 다음과 같이 구현되어 플레이어 (2)에 의해 주장 될 수 있습니다 :

bool Board::Tile::claimBy (const Player &owner) 
{ 
    if (!_owner) 
    { 
     *_owner = owner; 
     _colour = owner.colour(); 
     return true; 
    } 

    return false; 
} 

_ownerstd::unique_ptr<Player>입니다. 타일의 소유자가 이전에 설정되었는지 (즉, nullptr이 아닌지) 먼저 확인합니다. 그렇지 않으면 전달 된 값으로 내부에 Player을 설정 한 다음 타일의 색상을 업데이트하고 true을 반환합니다. 이전에 소유권을 주장한 타일은 false을 반환합니다.

디버거가 끝나면 *_owner = owner; 줄에서 충돌이 발생합니다. 스텝을 밟으면 struct Player (내 선언은 Player 클래스)입니다. 암시 적 복사 생성자가됩니다 (클래스는 Colour _colourstd::string _name 만 기억하십시오).

스테핑을 다시 수행하면 Colour::operator=이됩니다 (복사 생성자가 호출하는 것이 좋습니다).정의는 다음과 같습니다.

Colour &Colour::operator= (const Colour &rhs) 
{ 
    if (*this != rhs) 
    { 
     _red = rhs.red(); 
     _green = rhs.green(); 
     _blue = rhs.blue(); 
     _alpha = rhs.alpha(); 
    } 

    return *this; 
} 

경로는 *this != rhs으로 바뀝니다.

return red() == rhs.red() 
    && green() == rhs.green() 
    && blue() == rhs.blue() 
    && alpha() == rhs.alpha(); 

여기 red() == rhs.red() 첫 번째 비교 그냥 return _red;입니다 red()이있는이 단지입니다 operator==에 역 호출이다. 이것은 프로그램이 충돌하는 지점입니다. 디버거에 this (this->_red)이 0x0이라고 나와 있습니다.

왜 이런 일이 일어나고 있는지 알 수 없습니다. 내 생각 엔 스마트 포인터를 잘못 사용하고있는 것 같습니다. 전에는 실제로 사용하지 않았지만 정상적인 포인터와 매우 유사해야하며 포인터가 nullptr 인 경우 release은 아무 것도 수행하지 않을 것이라고 생각했습니다.

this이 0x0 일 수 있습니다.

편집 :
나는 각각의 생성자에서 그렇게으로 확인 모든 NONE 투명 블랙 없다 멤버 이니셜 라이저 (예를 들어 Board::Tile::Tile() : _colour (Colours::NONE), _owner (nullptr){})에, 초기화입니다.

나는 디버깅 값을 인쇄하는 데 그다지 사용하지 않았기 때문에 디버거도 능숙하지 못합니다.

+0

사이드 노트 : 타일이 보드가되는 것이 맞습니까? – Cameron

+0

@Cameron, is-a 관계는 상속과 관련이 있습니다. 'Tile' 클래스의 나의 목표는'Board'에 완전히 포함시키는 것이 었습니다. 'tile' 함수는 원래'const' 리턴 이었지만, 방금 테스트를 위해 변경했습니다. 타일은 'Board'외부에서 액세스 할 수 있지만 작업 한 바로는 새 타일을 만들 수 없습니다. 물론 'Board'는 타일 벡터를 통해 비 const 액세스를 제공합니다 (제공하는 관계가 있습니다). 즉, 보드 객체에는 타일의 2D 벡터가 있습니다. – chris

+0

디자인에 관련없는 의견 : 보드를 조작하는 게임 논리가 RGBA 색상에 관심을 가져서는 안됩니다. 타일을 제어하는 ​​플레이어를 참조하고 그리기 코드가 어떤 플레이어인지에 따라 색상을 선택하게하십시오. – Wyzard

답변

3

*_owner = owner; 

의미 라인 "는 owner 객체의 복사본을 만들고, 그 _owner 포인트 장소에 보관합니다." 문제는 _owner이 아직 아무 것도 가리 키지 않았다는 것입니다. 여전히 null입니다. 당신이 정말로 플레이어 컨트롤, 당신은

_owner.reset(new Player(owner)); 

그러나 할 필요하려는 각 타일의 Player 객체의 사본를 확인하려면

Player 객체의 복사본을 만드는 것은 이상한 일이 할 것. 대신 shared_ptr을 사용해보세요. owner_owner을 모두 shared_ptr 초로 지정하고 일반적인 방식으로 다른 하나를 할당하면됩니다.

+0

나는 그것을 살펴볼 것입니다. 고마워요. 저는이 인수가 실제'Player'가되고 싶습니다만,'nullptr' 또는 하나의'Player'가 될 것이기 때문에'unique_ptr'을 생각하고있었습니다. 나는 틀렸다고 생각해. – chris

+0

'unique_ptr'의 "unique"는 한번'Player'를 가리키면 다른 것을 가리킬 수 없다는 것을 의미하지는 않습니다. 즉,'Player'를 가리키면 다른 플레이어가 * Player *를 가리 키지 않는다고 약속하기 때문에'unique_ptr'가 범위를 벗어 났을 때'Player'를 삭제하는 것이 안전합니다. 'shared_ptr'는 같은 플레이어에 대한 포인터를 여러 개 가질 수 있습니다 (예 : 플레이어가 제어하는 ​​각 타일에 하나씩). 그리고 모든 포인터가 범위를 벗어날 때까지 플레이어는 삭제되지 않습니다. – Wyzard

+0

'reset'은 트릭을 한 것으로 보입니다 :) 그것에 대해 생각했지만, 사실은 각 플레이어가 초기화되고 그 이후로 변경된 사항이 없습니다. 다음 레이어는 차례, 승리 등을 처리하는 "인터페이스"입니다.이 동작으로 인해 플레이어의 색상을 읽기 위해 복사본을 만들어 저장하는 것은 물론, 그것은 소유입니다. 나는 똑똑한 포인터에 대해 읽었으므로 그 차이점을 알았지 만 그 결정에 대한 나의 논리에는 결함이있는 것으로 생각된다. – chris

1

기본값으로 시작하는 std::unique_ptr<Player>으로 시작합니다. 말하자면, 일부 클린업 의미론을 갖는 NULL 포인터와 동일합니다. 그런 다음 명령문 *_owner=owner;에 역 참조를 지정하여 할당 할 수 있습니다.

따라서 문 *_owner=owner;은 기본적으로 암시 적 할당 연산자를 호출하는 ((Player*)NULL)->operator=(owner);과 같습니다. 이것이 첫 번째 일은 ((Player*)NULL)->_colour=owner._colour;과 같습니다. this==NULL을 찾는 것은 놀라운 일이 아닙니다. 실제로, 그것은 예상됩니다.

수정 내용은 실제로 발생시키고 자하는 것에 따라 다릅니다. 각 Board::Tileowner의 완전히 새로운 사본을 제공해야합니까? 그런 다음 _owner.reset(new Player(owner))라고 말하고 싶습니다. 각 타일이 기존 플레이어에 대한 참조를 보유하기를 원하십니까? 플레이어 오브젝트 owner이 Board :: Tile 오브젝트보다 오래 지속될 것이라고 보증 할 수 있습니까?그런 다음 원시 포인터를 원한다 : (Board :: Tile의 선언에서) Player const *_owner; (구현 중) _owner=&owner;.

관련 문제