2009-12-02 57 views
0

我有一個名爲Particle的類,它有一個std :: set作爲成員。這個類看起來是這樣的:如何正確封裝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

您可以將'hasNextCell'實現爲'iterator i = ipc; return ++ i!= cells.end();'以避免在查詢期間改變狀態。就個人而言,我會與詹姆斯的回答一起,並展示「開始」和「結束」。 – 2009-12-02 18:04:19

回答

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(); } 
+0

謝謝,我會試試這個。不幸的是,你是第二個提出這個建議的人,所以只有一個贊成:) – DaClown 2009-12-02 17:06:42

+0

沒問題,只要你注意到使用常量迭代器而不是正常的迭代器來解決你的具體問題;) – catchmeifyoutry 2009-12-02 17:13:13

+1

'begin()'和'end ()'如果從類上下文中清楚了什麼是迭代器所表示的,則更加通俗。 – 2009-12-02 17:13:41

0

你顯示的內容除了三個獲得者之外什麼也沒有做。通過使這些getters成爲Particle類的一部分的操作來封裝集合,那麼根本就不需要getter:封裝。

+0

我在我的問題中提出的代碼不完整,其中有更多的集合。最後只有一個數據容器,它包含單元格,點,標量索引......並且有很多這些粒子存儲在向量中。因此a不能將處理函數封裝在粒子中。 – DaClown 2009-12-02 17:04:00

4

ipc+1不起作用的原因是std::set只支持雙向迭代器,它支持operator++operator--;爲了使用operator+,您需要使用隨機訪問迭代器。

我在設計中看到的一個問題是您的函數被命名爲訪問器(getSuchAndSuch),但它們也修改對象的內部狀態(已修改ipc)。這可能會導致混淆。

你可以嘗試的一件事是使用一些返回迭代器的成員函數(例如,一個beginend),並允許你的類的用戶使用迭代器訪問內部集合,同時仍然封裝設置實施。

你可以返回集合的迭代器類型,或者如果你想要更多的控制或封裝,你可以實現你自己的迭代器類來包裝集合的迭代器。

+0

+1爲「獲取」功能設置狀態註釋。 – wheaties 2009-12-02 17:04:11

+1

其中一個「暴露容器」問題(首先我發現,誠實;):http://stackoverflow.com/questions/1484052/should-i-expose-iterators-and-adaptor-methods-or-a-whole- container-in-c – 2009-12-02 17:06:57

+0

我會隨着暴露的開始和結束。但我不知道名爲getNext ...的函數在語義上是不是可以改變內部狀態的。下一個函數還會如何到達下一個元素。順便說一句,我借用Java迭代器的這種語法,因爲我沒有想法。 – DaClown 2009-12-02 17:11:57

0

如果你想留住你已經有了整體實施,但只是消除getFirstCell(),你可以在構造函數中初始化IPC。如上所述,明智地使用const並明確區分訪問者和變異者將闡明界面。另外,如果你想在你的類上實現迭代器,那麼我會建議addcell()返回一個引用新單元格的迭代器,並在遇到錯誤時拋出異常。

+0

感謝您的回覆。 addCell不返回引用,因爲這裏使用集合的全部要點是其中元素的唯一性。我只需添加一個單元格並檢查該集合的大小是否已更改,以確定是否將某個元素添加到集合中。但是,當然,再加上另一個回覆以及通過開始和結束函數替換getter,如果它已經存在,我可以返回迭代器和set.end()。我會研究這個。 – DaClown 2009-12-02 17:23:07

2

爲了防止暴露組:迭代(不承諾用戶超過需要的話),你可以創建一個包裝:

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的算法。

也助推:: iterator_facade可以在這裏有用...

1

的問題是,你要在這裏完成真正的東西。現在,你的班級似乎(至少對我來說)做的不僅僅是好事 - 它使得集合中的內容變得更加困難而不是簡單。

我會看看Particle,並確定它是否可以提供一些有意義的方法來存儲/訪問一堆單元格。如果它真的只是一個簡單的容器,那麼你可以使用像typedef std::set<cell> Particle;這樣的更好的方法,所以最終用戶可以像使用其他任何方法一樣在這個集合上使用算法等。我只寫一個類來封裝,如果你真的可以封裝一些有意義的東西 - 也就是說,如果你的類可以體現一些有關粒子的「知識」,所以其他代碼可以將粒子用作本身有意義的東西。

現在,你的Particle不過是一個容器 - 它看起來也不是一個特別好的容器。除非你真的可以添加一些東西,否則只需使用已有的東西就可以更好。

+0

是的,它是其他數據中提到的數據結構。我將來的文章將包括我所有的代碼,將它切割爲必需品似乎更容易混淆,然後提高可讀性。 – DaClown 2009-12-02 17:41:01