0

我有一个名为 Particle 的类,它有一个 std::set 作为成员。该类如下所示:

class Particle {
private:
    std::set<vtkIdType> cells;
    std::set<vtkIdType>::iterator ipc;

public:

    Particle() {};

    enum state {EXISTS = -1, SUCCESS = 0, ERROR = 1};

    state addCell(const vtkIdType cell);

    int numCells() { return static_cast<int>(cells.size()); }

    vtkIdType getFirstCell() { return (*(ipc = this->cells.begin()));}
    vtkIdType getNextCell() { return *(++ipc); }
    vtkIdType hasNextCell() { ++ipc; if (ipc == this->cells.end()) return false; --ipc; return true; }

    std::string getOutput();
};

我对 非常不满意getFirstCell()getNextCell()尤其是hasNextCell()它们的存在是因为我不想暴露集合本身。我不得不使用这种方式++ipc--ipc因为if((ipc+1) == this->cells.end())给出了编译器错误,ipc+1 似乎是问题所在。

封装一个集合并访问它的好方法是什么?另外,有没有摆脱这个getFirstCell()功能的好方法?

提前致谢。

编辑:我发布的代码只是类结构的一个例子。“真实”类包含更多的集合和其他对这个问题不那么重要的数据(我假设)。

4

6 回答 6

4

ipc+1不起作用的原因是std::set它只支持双向迭代器,它支持operator++and operator--; 为了使用operator+,您需要使用随机访问迭代器。

我在您的设计中看到的一个问题是您的函数被命名为访问器(getSuchAndSuch),但它们也会修改对象的内部状态(ipc已修改)。这可能会导致混乱。

您可以尝试的一件事是使用一些返回迭代器的成员函数(例如 a beginend),并允许您的类的用户使用迭代器来访问内部集合,同时仍然封装集合实现。

您可以返回集合的迭代器类型,或者如果您想要更多的控制或封装,您可以实现自己的迭代器类来包装集合的迭代器。

于 2009-12-02T16:56:11.193 回答
4

我不确定您为什么不想公开集合本身,但如果是因为您想确保集合的内容不能在外部更改,class Particle只需返回const使集合“只读”的迭代器,例如

typedef std::set<vtkIdType>::const_iterator CellIterator;
CellIterator beginCell() const { return this->cells.begin(); }
CellIterator endCell() const { return this->cells.end(); }
于 2009-12-02T17:02:18.887 回答
2

为了防止暴露 set::iterator (不向用户承诺超过需要的内容),您可以创建一个包装器:

class Particle::iterator
{
public:
  iterator()
  {}
  iterator &operator++()
  {
    ++InternalIterator;
    return *this;
  }
  vtkIdType &operator*() const
  {
    return *InternalIterator;
  }
  ...//other functionality required by your iterator's contract in the same way
private:
  iterator(const std::set<vtkIdType> &internalIterator)
    :InternalIterator(internalIterator)
  {}
  std::set<vtkIdType>::iterator InternalIterator;
};

Particle::iterator Particle::GetBeginCell()
{
  return iterator(cells.begin());
}
Particle::iterator Particle::GetEndCell()
{
  return iterator(cells.end());
}

因此,您将摆脱内部迭代器(因为只能拥有一个迭代器是非常受限的),并且将能够在 Particle 的迭代器上使用来自 STL 的算法。

boost::iterator_facade 在这里也可能有用......

于 2009-12-02T17:12:41.673 回答
1

问题实际上是您要在这里完成的工作。现在,你的课程似乎(至少在我看来)弊大于利——它使处理集合内容变得更加困难而不是更容易。

我会看一下粒子,并弄清楚它是否可以提供一些有意义的东西,而不是某种存储/访问一堆单元格的方式。如果它真的只是一个简单的容器,那么使用类似的东西会好很多typedef std::set<cell> Particle;,因此最终用户可以在这个集合上使用算法等,就像他们可以使用其他任何东西一样。如果你真的可以封装一些有意义的东西,我只会编写一个类来封装它——即,如果你的Particle类可以包含一些关于粒子的“知识”,那么其他代码可以将粒子作为本身有意义的东西来工作。

现在,你Particle只是一个容器——而且它看起来也不是一个特别好的容器。除非你真的可以添加一些东西,否则你最好只使用已经存在的东西。

于 2009-12-02T17:23:39.780 回答
0

除了三个吸气剂之外,您显示的内容没有任何作用。通过将使用这些 getter 的操作作为 Particle 类的一部分来封装该集合,那么您将根本不需要这些 getter:瞧,封装了。

于 2009-12-02T16:54:48.650 回答
0

如果您想保留已有的通用实现,但只需消除getFirstCell(),您可以在构造函数中初始化 ipc 。如上所述,明智地使用const和明确区分访问器和修改器将澄清接口。此外,如果你要在你的类上实现迭代器,那么我建议addcell()返回一个引用新单元格的迭代器,而不是在遇到错误时抛出异常。

于 2009-12-02T17:08:43.307 回答