首页 > 解决方案 > 使用 unique_ptr 作为我自己的移动构造函数的替代方法是一种好习惯吗?

问题描述

我正在使用 C++ 制作纸牌游戏。它类似于纸牌,即有不同的堆叠和桩,卡从一个移动到另一个。

我正在使用std::vector各种桩,但std::vector<Card>我使用的是std::vector<std::unique_ptr<Card>>. 我这样做的理由是:

  1. 它确保所有权责任vector在于它当前所在的位置。
  2. 它确保我在堆叠和堆叠之间转移时不会意外拥有超过 1 个同一张卡副本,即强迫我使用std::move.
  3. 我不必为Card.

这是一种合理的使用方式unique_ptr吗?

编辑:忘了提,有不同类型的Cards 派生自基类。我是一个初学者,不想在各个级别使用不同的复制和移动构造函数。

标签: c++

解决方案


  1. 它确保所有权责任与它当前所在的向量有关。

这对我来说意义不大。Vector 拥有它的所有元素,因此唯一指针在这方面没有任何价值。

  1. 它确保我在堆栈和堆栈之间转移时不会意外拥有超过 1 个同一张卡的副本,即强制我使用 std::move 进行转移。

如果防止复制有用,那么另一种选择是使Card不可复制 - 或保持可Card复制并创建不可复制的包装器。在某种程度上,std::unique_ptr<Card>可以将其视为这样的包装器,但它不仅仅是一个包装器,并且对于该用例而言不必要地低效和复杂。一个简单的例子:

struct UniqueCard : Card {
    using Card::Card;

    UniqueCard(UniqueCard&&) = default;
    UniqueCard& operator=(UniqueCard&&) = default;

    UniqueCard(const UniqueCard&) = delete;
    UniqueCard& operator=(const UniqueCard&) = delete;
};

请注意,这种设计意味着卡片必须具有某种“空”状态,即它们将在移动后保留。这对应于堆栈中有一个空指针,从唯一指针移动时会出现这种情况。

我不相信这一定比只允许复制并将责任留给班级用户遵守游戏规则更好。

这是使用 unique_ptr 的合理方式吗?

单独防止复制并不是使用唯一指针的合理理由。

我建议尝试另一种设计:不是将卡片存储在向量中,而是用链表表示卡片堆栈。要从堆栈移动到另一个,请提取节点并拼接到另一个列表中。这样卡片可以是不可变的,不需要代表“空”卡片,而且你不会得到重复。


我忘了提到有不同类型的卡片派生自基类

您不能将派生类实例存储在包含基类对象的向量中。存储派生实例一个合理的用例std::vector<std::unique_ptr<Card>>,因为指针可以指向派生实例的基类子对象。

请注意,动态多态不一定是纸牌游戏的最佳解决方案。


推荐阅读