Должен ли я использовать ссылку const или boost :: shared_ptr? - PullRequest
0 голосов
/ 11 ноября 2009

Я создал несколько классов C ++ для моделирования пасьянса в качестве учебного упражнения.

У меня есть классы для SolitaireGame, CardStack (одна из 10 стопок карт на доске) и Card. Моя текущая модель утверждает, что SolitaireGame владеет вектором из 104 объектов Card, который я называю «башмак». SolitaireGame также отслеживает 10 CardStacks, которые по сути являются пятнами адресов объектов Card, хранящихся в обуви. Палуба и Рука наследуются от CardStack. Я передаю карты из колоды в руку в каскад с помощью указателей на оригинальные предметы, хранящиеся в обуви.

Согласно ряду ответов, которые я получил на этот вопрос, я не должен передавать указатели вокруг карты, а должен использовать ссылки на константы. Причина в том, что объекты, хранящиеся в векторах, могут перемещать свои адреса, поэтому хранить их адреса где угодно - нет-нет. Я недавно начал смотреть на boost :: sharedptr. Что люди думают об использовании shared_ptr для Card здесь?

Вот упрощенные версии классов:

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;

};

Ответы [ 4 ]

5 голосов
/ 11 ноября 2009

Причина в том, что объекты, хранящиеся в векторах, могут перемещать свои адреса, поэтому хранить их адреса где угодно - нет-нет.

Хранение (постоянных) ссылок так же плохо, как и сохранение указателей по той же причине. Если размер вектора не меняется, пока другие объекты содержат указатели на объекты в нем, вы должны быть в безопасности.

При программировании на C ++ вы всегда должны решить, кому «принадлежит» объект, например, кто несет ответственность за его удаление, когда он больше не нужен. Если нет владельца естественного объекта, вы можете прибегнуть к умным указателям, таким как boost::shared_ptr, которые используют подсчет ссылок или сборщик мусора для управления временем жизни объекта.

В вашем случае совершенно очевидно, что экземпляр SolitaryGame владеет всеми картами. Причем количество карт в игре фиксированное. Поэтому вы можете легко передавать указатели ваших карт на объекты, которые зависят от игрового экземпляра.

После удаления игры все карты будут удалены, а оставшиеся указатели станут недействительными, но в это время другие объекты, содержащие указатели карт, также должны быть удалены.

2 голосов
/ 11 ноября 2009

Да, если вы берете адрес элементов вашего

vector<Card> _shoe;

и размещение их в вашем

deque<Card *> _cards;

Определенно может быть проблема, как вы описываете. Ваш вектор мог перераспределиться, в результате чего адрес элементов карты вектора больше не действителен.

Передача ссылок (постоянных или иных) на содержимое вашего вектора будет иметь те же проблемы, что и передача указателей. В C ++ ссылка действительно является тонко завуалированным указателем. Единственное отличие от указателя заключается в том, как он используется (в качестве псевдонима), в том, что он не может быть «незаполнен», в том, что он равен NULL, и в том, что он не отличается от псевдонима (вы не можете вектор карточных ссылок). Ссылка не имеет никакого специального подсчета ссылок или чего-либо другого, что вы получаете с другими языками сборки мусора. Поэтому, когда ваш вектор перераспределяется, и если кто-то держит ссылку на любую из карт в колодах, эти ссылки потерпят неудачу так же легко, как и указатель.

Замена вашего вектора на вектор карт Boost :: shared_ptr может решить ваши проблемы. Boost :: shared_ptr подсчитывается по ссылке. Это означает, что он отслеживает, сколько существует ссылок на базовый объект. И ваш вектор будет вектором shared_ptrs, а не самого объекта. Поэтому, когда вектор перераспределяется, вы просто временно добавляете новый реферер к базовому объекту во время перераспределения, а затем вектор заменяет shared_ptr на shared_ptr, живущий в перераспределенном пространстве. Базовый объект не двигается.

Я бы сделал еще один шаг и рекомендовал бы не давать всем shared_ptr. Передайте boost :: weak_ptr не владельцам. Boost :: weak_ptr - слабая ссылка на базовые данные. Слабый_птр дает кому-то ручку для получения shared_ptr при необходимости. Он не участвует в подсчете ссылок базовых данных. Таким образом, вы можете сначала проверить, были ли основные данные удалены владельцем. Затем, если он не был удален, получите shared_ptr (временно участвующий в подсчете рефереров) и выполните необходимое действие.

0 голосов
/ 11 ноября 2009

Я думаю, что ответ Фердинанда выше - это путь, но я хотел прокомментировать использование boost :: shared_ptr в этом случае.

Как тяжелый объекты вашей Карты? Если они достаточно малы (скажем, несколько int), то, вероятно, было бы лучше просто скопировать сами карты, чем использовать boost :: shared_ptr, поскольку копирование boost :: shared_ptr недешево (из-за синхронизации потоков по счетчику ссылок). Это основано на объектах вашей Карты, которым не нужно иметь уникальные идентификаторы, например, одна десятка пикового объекта Карты так же хороша, как и любой другой.

0 голосов
/ 11 ноября 2009

Вы не можете хранить ссылки в контейнере, поэтому, если вам нужен общий доступ, вы можете использовать только указатели. shared_ptr может показаться бессмысленным, потому что вы не хотите управлять памятью.

Однако, если бы я создал класс Card, он содержал бы только одно или два целых числа и был бы неизменным (если только это не магические карты, которые могут изменить свою масть и ценность). Поэтому я бы использовал только копии карточек.

Когда вы возвращаетесь, вы можете предпочесть ссылку (если вы не хотите хранить указатели на возвращаемые значения, что в конечном итоге выглядело бы довольно странно). Но лично я снова вернусь по значению.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...