从函数返回unique_ptr的向量

24

我目前正在学习《Head First Object Oriented Analysis and Design》这本书,并同时熟悉C++11的一些特性(尤其是unique_ptr和move语义)。在书中,他们以一个策略棋盘游戏的设计作为示例。它有一个棋盘、方块和方块上的单位。代码是用Java编写的,我试图将其改写成C++。在Java中,tile类如下:

public class Tile
{
    private List units;

    public Tile()
    {
        units = new LinkedList();
    }

    protected void addUnit(Unit unit)
    {
        units.add(unit);
    }

    protected List getUnits()
    {
        return units;
    }
}

我最初使用 shared_ptr 在 C++ 中完成了同样的事情。它确实可以工作,但与我使用原始指针的第二个版本相比非常慢。然后我尝试了 unique_ptr:

class Tile
{

public:
  Tile();
  virtual ~Tile();

  void addUnit()
  {
    mUnits.push_back(std::unique_ptr<Unit>(new Unit());
  }

  std::vector<std::unique_ptr<Unit>> getUnits()
  {
    return mUnits;
  }

private:
  std::vector<std::unique_ptr<Unit>> mUnits;

};

编译器不喜欢getUnits函数。从我的理解来看,这是由于unique_ptr禁用了复制构造函数的原因。你如何返回向量列表?


2
返回引用:std::vector<std::unique_ptr<Unit>>& getUnits() - hmjd
3
返回一个 unique_ptr 的向量将意味着放弃它们,这样 Tile 就无法再访问它们了。这是您想要的吗? - Vaughn Cato
4
你真的需要返回向量吗?如果你的类封装了std::vector,那么你应该提供比这个访问器更好的对其数据的访问方式。这个例子清楚地说明了从Java到C++的机械性改写不是正确的方法,特别是因为你没有提供任何新功能。 - Bartek Banachewicz
2
不要忘记,Java中的“对象”是指针;即使看起来像是按值返回,结果也指向原始对象。在C++中的类比是返回指针或引用。 - Pete Becker
1
原始类的设计本来就很糟糕,希望那本书能提到这一点。为什么要封装一个容器,然后又完全访问它呢? - GManNickG
显示剩余4条评论
1个回答

23

C++中的返回值会产生复制 - 如果这正是您想要的,那么您不能使用unique_ptr,或者必须手动复制并使用std::move。然而,我认为您只是想提供对Unit的访问(出于任何原因...),因此只需返回一个引用:

std::vector<std::unique_ptr<Unit>> const& getUnits() const
{
  return mUnits;
}
(如果您不想向 vector 提供写入访问权限,则只需要使用 const。)
现在,仍然有一些问题:为什么~Tile需要 virtual?我看不出来有必要。
为什么要使用拥有指针来指向单位?在原始的 Java 代码中,您会获得对外部单元的引用,并仅存储一个引用-如果 Java 代码中的Tile被销毁,单位将不会被销毁(从我所看到的情况来看),只要它们在其他地方被引用。为了在C ++中实现完全相同的效果,你正确地使用了shared_ptr
当然,您也可以澄清谁是真正的单位所有者并相应地操作——如果不是Tile,则使用原始指针。另请参见“何时使用哪种指针?”

3
~Tile()是虚拟的,因为Java代码中有“protected”方法,这意味着意图是从此类派生其他类。 - Pete Becker
有趣的是...我尝试使用auto test = getUnits(),但是出现了一个错误:"使用已删除的函数'std::unique_ptr等等 :-( - darkman
6
@darkman:嗯... auto x = init; 会导致复制。使用 auto& x = init; - Xeo
为什么在Visual Studio 2013中这个不起作用?我仍然得到一个编译器错误,试图访问复制构造函数... - tomi.lee.jones
在 "auto& x = init;" 中,Xeo 在 auto 后使用 & 来指定 x 是对 init 的左值引用。请查看此处以获取更多详细信息:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html - Yingpei Zeng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接