我有一个名为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()
函数?
提前致谢。
编辑:我发布的代码只是类结构的一个例子。 “真实”类包含更多集合和其他对此问题不重要的数据(我假设)。
答案 0 :(得分:4)
ipc+1
不起作用的原因是std::set
仅支持双向迭代器,它支持operator++
和operator--
;为了使用operator+
,您需要使用随机访问迭代器。
我在设计中看到的一个问题是你的函数被命名为accessors(getSuchAndSuch),但它们也修改了对象的内部状态(ipc
被修改)。这可能会导致混淆。
您可以尝试的一件事是使用一些返回迭代器的成员函数(例如,begin
和end
),并允许您的类的用户使用迭代器来访问内部集合,同时仍然封装了集合实现。
你可以返回set的迭代器类型,或者如果你想要更多的控制或封装,你可以实现自己的迭代器类来包装set的迭代器。
答案 1 :(得分: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(); }
答案 2 :(得分: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());
}
因此,您将摆脱内部迭代器(因为它只能有一个迭代器是非常有限的)并且能够在粒子的迭代器上使用STL中的算法。
另外boost :: iterator_facade在这里可能很有用......
答案 3 :(得分:1)
问题实际上是你要在这里完成的。现在,你的班级似乎(至少对我来说)弊大于利 - 这使得处理集合的内容变得更加困难而不是更容易。
我会看看Particle,并弄清楚它是否能提供超出存储/访问一堆单元格的某些方面的有意义的东西。如果它真的只是一个简单的容器,那么你可以用typedef std::set<cell> Particle;
之类的东西做得更好,所以最终用户可以像使用其他任何东西一样在这个集合上使用算法。我只编写一个类来封装,如果你真的可以封装有意义的东西 - 也就是说你的Particle
类可以体现一些关于粒子的“知识”,所以其他代码可以使用粒子作为一个有意义的东西本身。
现在,你的Particle
只不过是一个容器 - 而且它看起来也不是一个特别好的容器。除非你能真正添加一些东西,否则你可能最好只使用已有的东西。
答案 4 :(得分:0)
答案 5 :(得分:0)
如果您想保留已有的常规实现,但只是删除getFirstCell()
,则可以在构造函数中初始化ipc。如上所述,明智地使用const
并明确区分访问者和变更者将澄清界面。另外,如果你要在类上实现迭代器,那么我建议addcell()
返回引用新单元格的迭代器,而不是在遇到错误时抛出异常。