【问题标题】:unique_ptr vs shared_ptr - design [closed]unique_ptr vs shared_ptr - 设计[关闭]
【发布时间】:2018-02-01 09:25:50
【问题描述】:

我正在写一个纸牌游戏。我有以下课程:Card、Deck、Player、Board、GameLogic。

Deck 包含一个由 unique_ptr Cards 组成的向量。然后从牌堆中抽出一张牌并传递给玩家。然后玩家选择一张牌并放...这里我有一个设计问题。因为到目前为止,拥有一个 unique_ptr 完全没问题。但后来我想将一张卡片传给 Board 和 GameLogic。

目前我将 unique_ptr 转换为 shared_ptr。但我觉得它既丑陋又不合逻辑。

我相信正确使用指针非常重要,应该告诉你变量的生命周期。我做错了什么,但不知道是什么......

您对如何解决有什么建议吗?

【问题讨论】:

  • 我通常的建议是不要将智能指针视为一些简单的自删除指针,而是从所有权的角度来看。一个“资源”会有多个所有者 (std::shared_ptr) 还是只有一个所有者 (std::unique_ptr)?也许智能指针在这里不正确,但您应该使用普通的 non-owning 指针?或者想出一个你不需要指针的设计?
  • 你为什么使用指针而不只是卡片值?
  • 你为什么有unique_ptr<Card>s? Card 是多态的(为什么)?您可以只拥有一个vector<Card> 并按值复制它们。
  • @Someprogrammerdude 一个所有者拥有一个共享指针,然后非所有者获得指向它的弱指针,这没什么错。当然,你可以用 Java 编写你最终得到的语法,但它会很好地工作。
  • @UKMonkey 你必须有特定的理由需要弱指针,因为它们很慢。但是当他们需要时,他们很棒!

标签: c++ c++11 design-patterns c++14 c++17


【解决方案1】:

按照你自己的话:

Deck 包含一个由 unique_ptr Cards 组成的向量。然后从中抽出一张牌 甲板并传递给玩家。然后玩家选择一张牌并放...这里 我有一个设计问题。因为到目前为止,拥有一个完全没问题的 唯一的_ptr。但后来我想把一张卡片传给董事会和 游戏逻辑。

可能你在构造函数中创建了每张卡片。虽然这是完全可行的,但我不明白这一点。假设总是有相同的、固定数量的卡片。我的意思是,您不会创建每种类型的新卡片或其他任何类型的卡片。

我只是将卡片组创建为一个简单的数组,然后将平均指针传递给玩家等等。当牌组被破坏时,卡片将被破坏。

class Deck {
public:
    const static int NumCards = ...;
...
private:
    Card cards[NumCards];
};

class Player {
public:
    void dealt(Card*[] cards);
};

C++ 的一个优点是让您可以选择将对象存储在堆中还是堆栈中。虽然前者更灵活(例如,当可用卡片的数量随时间变化时),但后者的效率要高得多,前提是您事先知道项目的数量,显然您在本例中就是这样做的。

希望这会有所帮助。

【讨论】:

  • 你可以看到我的其他 cmets 我需要 polimorphizm。但除此之外,我不喜欢这样的解决方案是我需要一些其他变量来管理从甲板上抽牌。当我有指针时,我可以从牌组中获得一张牌。
  • 我想您需要多态性,因为每种类型的卡都有一个类。这可以以不同的方式完成,简化为一个 Card 类,但无论如何。关于“另一个变量”,是的,您需要另一个集合才能将已发牌的牌存储在 Player 中。但是,这比将卡片从一个地方移动到另一个地方要好得多。
【解决方案2】:

要使用具有特定寿命的对象:

  • 优先选择值而不是指针。根据需要复制值或传递引用,但不要存储引用,因为引用的值可以被删除。
  • 当需要指针时,如果您可以识别一个所有者,则首选unique_pointer。根据需要传递引用,但不要存储它们。
  • 当有多个所有者或需要存储“引用”时使用shared_pointer。传递weak_pointers,可以根据需要存储和锁定。

请注意,您也可以提供一些 id 作为参考,以便稍后访问可能已删除的对象。如果您的 Card 只不过是一个 id(红心皇后),只需传递它的副本即可。

【讨论】:

  • 不,我的卡不止这些。有许多不同类型的卡。有的可以放在棋盘上,有的可以用来打架,有的可以格挡,有的可以释放……等等。有很多参数可以描述每一种卡牌。因此,我认为将它们全部归为一类并不是一个好主意。你呢?
【解决方案3】:

虽然我同意其他答案中的观点,即此处并非绝对需要指针,但如果您确实使用指针,最简单的方法是使用引用:

class Deck
{
private:
    std::vector<unique_ptr<Card>> cards;
public:
    const Card& get_card(unsigned index); // returns cards[index]
}

const 引用允许您引用它,而无需将其转换为 shared_ptr(并且没有代码的其他部分修改它的风险)。

【讨论】:

  • 好的。我得到了你的解决方案。我只是认为从牌组中取出一张牌是个好主意。就像模仿现实世界一样,如果你抽到一张牌,那么它就不再在牌组中了。我希望它能帮助我避免错误。当您复制一个值时,您必须记住将其从所有位置删除。
  • 啊,如果这是你想要的,你可以这样做! std::move unique_ptrDeckPlayer,那么它显然归玩家所有,不再出现在牌组中。之后你可以通过引用Board(使用上面的解决方案)来传递它!
  • 好的。但是随后玩家可以放一张牌(他不再持有它)。所以也许那时我应该将std::move 传递给某个管理对象并将原始指针传递给LogicBoard。你怎么看?
  • 嗯,听起来任何卡总是DeckPlayerBoard 的一部分。因此,在其中一个类中保留unique_ptr 并传递对Logic 的引用(或让Logic 读出其他类,取决于实现)是有意义的。
  • 除此之外,您还可以使用原始指针而不是引用——这里的主要区别是引用不能是null,这听起来像你想要的。
【解决方案4】:

如果你对共享指针和唯一指针都不满意,没有比普通指针更好的选择了。使用观察者模式,这在对象之间存在一对多依赖关系时很有帮助,这样当一个对象改变状态时,它的所有依赖关系都会自动得到通知和更新。

【讨论】:

    猜你喜欢
    • 2021-01-17
    • 2013-12-09
    • 1970-01-01
    • 2013-08-18
    • 2017-03-15
    • 2011-02-21
    • 1970-01-01
    • 2016-06-05
    • 1970-01-01
    相关资源
    最近更新 更多