我应该使用const引用还是boost :: shared_ptr?

时间:2009-11-11 14:09:58

标签: c++ boost reference

我创建了一些C ++类来模拟Solitaire游戏作为学习练习。

我有SolitaireGame课程,CardStack(电路板上10堆卡片之一)和一张卡片。我现在的模型说SolitaireGame拥有一个104卡对象的矢量 - 我称之为'鞋'。 SolitaireGame还跟踪10张CardStack,它们基本上是存储在鞋子中的Card对象地址的deque。 Deck和Hand继承自CardStack。我通过指向Shoe中存储的原始对象的指针将牌从Deck传递到Hand to Cascade。

根据我收到的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;

};

4 个答案:

答案 0 :(得分:5)

  

原因是存储在向量中的对象可以移动其地址,因此将地址存储在任何地方都是禁忌。

存储(const)引用与存储指针一样糟糕,原因相同。如果只要其他对象持有指向其中对象的指针,向量的大小就不会改变,那么你应该是安全的。

用C ++编程时,你应该总是决定谁“拥有”一个对象,例如谁不负责在不再需要时将其删除。如果没有自然对象所有者,您可以使用像boost::shared_ptr这样的智能指针,它们使用引用计数或垃圾回收来管理对象的生命周期。

在您的情况下,很明显SolitaryGame实例拥有所有卡片。而且,游戏中的牌数是固定的。因此,您可以轻松地将卡片的指针传递给依赖于游戏实例的对象。

一旦游戏被删除,所有卡都将被删除,剩余的指针将无效,但此时,持有卡指针的其他对象也将被删除。

答案 1 :(得分:2)

是的,如果您要获取

元素的地址
vector<Card> _shoe;

并将它们放入您的

deque<Card *> _cards;

肯定会有问题,就像你描述的那样。您的向量可以重新分配,使向量的Card元素的地址不再有效。

将引用(const或其他)传递给向量的内容将产生与传递指针相同的问题。在C ++中,引用实际上是一个薄薄的指针。与指针的唯一区别在于它是如何使用(作为别名)它不能被“取消”的事实,即使其为NULL,以及它与别名类型无法区分的事实(你不能拥有矢量卡参考)。引用没有任何特殊引用计数或您使用其他垃圾收集语言获得的任何内容。因此,当您的向量重新分配时,如果任何人持有对甲板中任何卡的引用,那么这些引用将像指针一样容易失败。

使用boost :: shared_ptr of Cards向量替换向量可以解决您的问题。 boost :: shared_ptr是引用计数。这意味着它会跟踪底层对象存在多少引用者。而你的向量将是shared_ptrs的向量,而不是对象本身的向量。因此,当向量重新分配时,您只是在重新分配期间临时将新的引用者添加回底层对象,然后向量将shared_ptr替换为生活在重新分配的空间中的shared_ptr。基础对象不会移动。

我会更进一步,建议不要给每个人一个shared_ptr。将boost::weak_ptr's传递给非所有者。 boost :: weak_ptr是对底层数据的弱引用。 weak_ptr为某人提供了在需要时获取shared_ptr的句柄。它不参与基础数据的引用计数。因此,您可以首先检查所有者是否删除了基础数据。然后,如果没有删除,请获取shared_ptr(temporarilly参与引用计数)并执行所需的操作。

答案 2 :(得分:0)

您无法在容器中存储引用,因此如果您想要共享访问,则只能使用指针。 shared_ptr似乎有些无意义,因为你不希望管理内存。

但是,如果我制作了一个Card类,它只包含一个或两个整数,并且它将是不可变的(除非这些是可以改变它们的套装和值的魔术卡)。因此我只会使用卡片副本。

当你返回时,你可能更喜欢一个引用(除非你想存储指向返回值的指针,这最终看起来很奇怪)。但就个人而言,我只是按价值回归。

答案 3 :(得分:0)

我认为费迪南德上面的回答是要走的路,但我想在这个例子中评论一下boost :: shared_ptr的使用。

你的卡片对象怎么样?如果它们足够小(几个int说)那么复制卡本身可能比使用boost :: shared_ptr更好,因为复制boost :: shared_ptr并不便宜(由于引用计数上的线程同步)。这取决于您的Card对象不需要具有唯一标识,例如十分之一的黑桃卡对象和其他任何一样好。