2009-11-11 5 views
0

저는 솔리테어 게임을 학습 연습으로 모델링하기 위해 C++ 클래스를 만들었습니다.const 참조 또는 boost :: shared_ptr을 사용해야합니까?

나는 SolitaireGame, CardStack (보드에 10 장의 카드 중 하나)과 카드를위한 수업을 가지고 있습니다. 나의 현재 모델은 SolitaireGame이 104 개의 카드 객체의 벡터를 소유하고 있다고 말합니다 - 나는 이것을 '신발'이라고 부릅니다. SolitaireGame은 또한 신발에 저장된 Card 객체의 주소 중 본질적으로 deque 인 10 개의 CardStacks를 추적합니다. 갑판과 손은 CardStack에서 상속받습니다. 나는 갑판에서 카드를 신발에 보관 된 원래 물건을 가리키는 포인터를 통해 손으로 캐스케이드로 전달합니다.

나는 this 질문에 대한 답변의 수에 따라 카드의 포인터를 전달해서는 안되지만 const 참조를 사용해야합니다. 그 이유는 벡터에 저장된 객체의 주소가 이동 될 수 있기 때문에 어디서나 주소를 저장하는 것이 더 바람직하지 않기 때문입니다. 나는 최근 boost :: sharedptr을보고 시작했다. 여기에 shared_ptr을 사용하여 사람들이 생각하는 것은 무엇입니까? 여기

클래스의 간략화 된 버전이다 :

class SolitaireGame 
{ 
    public: 
    SolitaireGame::SolitaireGame(int numsuits);  

    private:   
     vector<Card> _shoe; 
     Deck _deck; 
     Hand _hand; 
     CardStack _cols[NUM_COLUMNS]; 
     int _numsuits; 
     GameState gamestate; 
}; 

class CardStack 
{ 
    public: 
     CardStack(){ cout << "CardStack constructor" << endl; } 
     CardStack(const CardStack&); 
     CardStack(const deque<Card *> &d); 
     ~CardStack(){ } 

     virtual Card * PullCard(Face f); 
     virtual void PushCard(Card * c); 

     Card * CardAt(int i) const; 
     Card * Top() const; 

     deque<Card *>::iterator Begin() { return _cards.begin(); } 
     deque<Card *>::iterator End() { return _cards.end(); } 

     int Size() const; 
     CardStack& operator=(const CardStack& rhs); 

     friend std::ostream& operator<<(std::ostream &os, const CardStack &obj); 

private: 
     deque<Card *> _cards; 

는};

답변

5

벡터에 저장된 객체의 주소 이동이 가능하기 때문에 어디서나 주소를 저장할 수 있다는 이유가 있습니다.

동일한 이유로 포인터를 저장하는 것만 큼 나쁜 점이 있습니다. 벡터의 크기가 다른 오브젝트가 오브젝트에 대한 포인터를 보유하고있는 한 변경되지 않으면 안전해야합니다.

C++에서 프로그래밍 할 때는 항상 객체를 "소유"하는 사람을 결정해야합니다. 누가 더 이상 필요하지 않을 때 삭제할 책임이 있습니다. 자연 개체 소유자가없는 경우 참조 횟수 또는 가비지 수집을 사용하여 개체의 수명을 관리하는 boost::shared_ptr과 같은 스마트 포인터를 사용할 수 있습니다.

귀하의 경우에는 SolitaryGame 인스턴스가 모든 카드를 소유하고 있다는 것이 명백합니다. 또한 게임의 카드 수는 고정되어 있습니다. 따라서 게임 인스턴스에 종속적 인 객체에 카드 포인터를 쉽게 전달할 수 있습니다.

게임이 삭제되면 모든 카드가 삭제되고 남아있는 포인터는 유효하지 않지만이 시점에서 카드 포인터가있는 다른 개체도 삭제되어야합니다.

+0

답장을 보내 주셔서 감사합니다. 그래, 내가 어떻게 작동하는지 상상하는 방법이다. 하나의 게임은 삭제되지만 카드는 모두 지워지기 때문에 삭제된다. 카드가 동적으로 할당되지 않았기 때문에 내 코드가 합리적으로 안전하다는 것을 알았습니다. 그리고 신발에는 항상 변경되지 않는 104 개의 카드가 포함되어 있습니다. – BeeBand

+2

나는 이것에 완전히 동의한다 (+1). 내가 추가 할 수있는 유일한 것은 (const로) 참조 (아직 CardStack에 포인터로 저장)로 카드를 전달하는 것입니다. 내 정책은 NULL이 유효한 값 인 경우에만 함수 인수와 반환 값을 포인터로 사용합니다. 그렇지 않으면 참조를 사용합니다. 이렇게하면 카드를 사용하기 전에 카드가 유효한지 확인/주장 할 필요가 없으므로 NULL이 유효하지 않다는 것을 프로그래머에게 분명히 알릴 수 있습니다. –

0

컨테이너에 참조를 저장할 수 없으므로 공유 액세스를 원하면 포인터 만 사용할 수 있습니다. shared_ptr은 메모리를 관리하고 싶지 않기 때문에 다소 의미가없는 것처럼 보일 수 있습니다.

그러나 카드 클래스를 만들면 하나 또는 두 개의 정수 만 포함되며 불변입니다 (단, 양복과 값을 변경할 수있는 마법 카드가 아니면). 따라서 나는 카드의 사본만을 사용하고 싶다.

돌아 왔을 때 참조를 선호 할 수 있습니다 (반환 값에 대한 포인터를 저장하고 싶지 않은 경우에는 이상하게 보일 수 있습니다). 그러나 개인적으로 다시 한번 나는 가치있게 돌아올 것입니다.

2

예 당신이 당신의

vector<Card> _shoe; 

의 요소의 주소를 복용하고 당신이 설명처럼 확실히 문제가있을 수 있습니다 귀하의

deque<Card *> _cards; 

로 배치하는 경우. 벡터가 재 할당되어 벡터의 카드 요소 주소가 더 이상 유효하지 않게됩니다.

벡터 내용에 참조 (const 또는 기타)를 전달하면 포인터 주위를 전달할 때와 동일한 문제가 발생합니다. C++에서 참조는 실제로 얇게 가려진 포인터입니다. 별명으로서 사용 된 포인터와의 유일한 차이점은 "unseated"가 될 수없고, NULL이되며, 앨리어싱 된 유형과 구별 할 수 없다는 사실입니다 (포인터를 가질 수 없습니다. 카드 참조 벡터). 참조에는 특별한 참조 카운팅이나 다른 가비지 수집 언어로 얻은 내용이 없습니다. 따라서 벡터를 재 할당 할 때 누군가가 갑판에있는 카드에 대한 참조를 보유하고 있으면 포인터가하는 것처럼 쉽게 참조가 실패합니다.

벡터를 벡터의 boost :: shared_ptr 's 벡터로 바꾸면 문제를 해결할 수 있습니다. boost :: shared_ptr는 참조 카운트입니다. 즉, 기본 객체에 몇 개의 리퍼러가 있는지 추적합니다. 그리고 당신의 벡터는 객체 자체가 아닌 shared_ptrs의 벡터가 될 것입니다.따라서 벡터가 재 할당 될 때 재 할당하는 동안 새로운 참조자가 기본 객체에 일시적으로 추가되면 벡터는 shared_ptr을 재 할당 된 공간에있는 shared_ptr으로 대체합니다. 기본 객체는 움직이지 않습니다.

나는 한 걸음 더 나아가 모든 사람에게 shared_ptr을주지 않을 것을 권장합니다. 비 소유자에게 boost::weak_ptr's을 전달하십시오. boost :: weak_ptr은 기본 데이터에 대한 약한 참조입니다. weak_ptr은 필요할 때 누군가에게 shared_ptr을 얻기위한 핸들을 제공합니다. 기본 데이터의 참조 횟수에 참여하지 않습니다. 따라서 기본 데이터가 소유자에 의해 삭제 된 경우 먼저 확인할 수 있습니다. 그런 다음 삭제되지 않은 경우 shared_ptr (임시 참조 자 수에 참여)을 가져 와서 필요한 작업을 수행하십시오.

+0

답장을 보내 주셔서 감사합니다. 그래서, 올바르게 이해했다면, 여분의주의 사항 (신발이 꽤 안정적 임에도 불구하고)이 신발을 동적으로 할당 된 카드에 shared_ptrs의 벡터로 생성 할 수 있다고 제안하는 것입니까? 그리고이 카드에 액세스하려는 사람은 약한 포인터를 통해 그렇게해야합니다. 나는이 아이디어가 마음에 든다. 나는 그것이 나를위한 좋은 학습 운동이 될 것이라고 생각한다. – BeeBand

0

필자는 Ferdinand의 대답이 위와 같다고 생각하지만 boost :: shared_ptr의 사용에 대해 언급하고 싶습니다.

중량이 얼마나이 당신의 카드 객체입니까? 만약 그들이 충분히 작다면 (몇 int의 말) 다음 boost :: shared_ptr을 복사하는 것이 싸구려가 아니기 때문에 boost :: shared_ptr을 사용하는 것보다 카드 자체를 복사하는 것이 더 나을 것입니다 (참조 카운트의 쓰레드 동기화로 인해). 이는 고유 한 ID가 필요하지 않은 Card 객체를 전제로합니다. 스페이드의 10 개 카드 객체는 다른 것과 마찬가지로 훌륭합니다.

+0

당신은 하나의 '고유 한 정체성'을 상세히 설명 할 수 있습니까?예, 각 카드는 고유 한 개체이지만 4 개의 분리 된 10 개의 스페이드가있을 수 있습니다. 카드 개체 - 그러나 스페이드 개체 중 10 개는 다른 개체에 대해 알아야합니다. 카드 개체는 무겁지 않습니다. – BeeBand

관련 문제