2016-09-06 55 views
0

我有一個類idx_aware,進入一個容器container,它圍繞着std::vector。當該類被添加到container時,container在其內部存儲器存儲器中設置指向自身的指針idx_aware以及idx_aware索引。C++ OOP:Class知道它在容器中的索引 - 防止覆蓋?

索引不會改變,直到container被銷燬或idx_aware被刪除; idx_aware需要知道它的容器及其索引,因爲它有一些方法需要這兩個工作。

現在,這引入了以下問題:當我得到一個非const引用包含在containeridx_aware類,我可以分配給它的另一個idx_aware類,它可以有不同的索引。目的是分配所有的字段並保持索引原樣。

#include <vector> 
#include <limits> 
#include <iostream> 

class container; 


// Stores a std::size_t field, which can be set only by subclasses. 
class with_idx { 
    std::size_t _i; 
public: 
    with_idx() : _i(std::numeric_limits<std::size_t>::max()) {} 
    operator std::size_t() const { return _i; } 
protected: 
    void set_idx(std::size_t i) { _i = i; } 
}; 


// Knows its index and its container 
class idx_aware : public with_idx { 
    container const *_container; 
    int _some_field1; 
    float _some_field2; 
public: 
    void foo() { 
     // Do stuff using _container and _i 
    } 
private: 
    friend class container; 
}; 


// Wraps around a std::vector 
class container { 
    std::vector<idx_aware> _data; 
public: 

    idx_aware &operator[](std::size_t idx) { 
     // Need non-const access to call foo 
     return _data[idx]; 
    } 

    idx_aware const &operator[](std::size_t idx) const { 
     return _data[idx]; 
    } 

    std::size_t add(idx_aware const &item) { 
     // Here it could potentially reuse a freed position 
     std::size_t free_slot = _data.size(); 
     // Ensure _data is big enough to contain free_slot 
     if (_data.size() <= free_slot) { 
      _data.resize(free_slot + 1); 
     } 
     // Assign 
     _data[free_slot] = item; 
     _data[free_slot].set_idx(free_slot); 
     _data[free_slot]._container = this; 
     return free_slot; 
    } 

}; 

int main() { 
    container c; 
    idx_aware an_item; 
    std::size_t i = c.add(an_item); 

    std::cout << c[i] << std::endl; // Prints 0 

    idx_aware another_item; // Created from somewhere else 

    // I want to set all the data in idx_aware, but the 
    // index should stay the same! 

    c[i] = another_item; 

    std::cout << c[i] << std::endl; // Prints numeric_limits<size_t>::max() 

    // Now container[i] is broken because it doesn't know anymore its index. 

    return 0; 
} 

一個可能的解決方法是在防止分配和複製操作來覆蓋_i屬性,這樣使得當set_idx被調用時,設置一個標誌,以改變with_idx

class with_idx { 
    std::size_t _i; 
    bool _readonly; 
public: 
    with_idx() : _i(std::numeric_limits<std::size_t>::max()), _readonly(false) {} 
    with_idx(with_idx const &other) : _i(other._i), _readonly(false) {} 
    with_idx &operator=(with_idx const &other) { 
     if (!_readonly) { 
      _i = other._i; 
     } 
     return *this; 
    } 
    operator std::size_t() const { return _i; } 
protected: 
    void set_idx(std::size_t i) { 
     _i = i; 
     if (i != std::numeric_limits<std::size_t>::max()) { 
      // This has been set by someone with the right to do so, 
      // prevent overwriting 
      _readonly = true; 
     } else { 
      // Removed from the container, allow overwriting 
      _readonly = false; 
     } 
    } 
}; 

這會導致在分配後返回索引不變的idx_aware類的引用。

idx_aware &not_in_container1 = /* ... */; 
idx_aware &not_in_container2 = /* ... */; 
idx_aware &in_container = /* ... */; 

not_in_container1 = in_container = not_in_container2; 
// std::size_t(not_in_container_1) != std::size_t(not_in_container_2) 
  • 是否有能夠以更好的方式這種情況建模設計模式?我的搜索沒有成功。
  • 以這種方式覆蓋賦值運算符是否會產生其他不良後果?我在前面的例子中指出的限制看起來不太「糟糕」。
  • 有沒有更簡單的解決方案?我想過寫一些代理對象來代替idx_aware &返回類型operator[]

經驗是說,當C++不這樣做,你打算什麼,你很可能是濫用OOP ...

+1

我根本就不允許通過賦值操作符來改變索引。 – MikeMB

+1

雖然這樣做可能有很好的理由,但我建議(重新)評估是否需要索引和容器地址的with_idx和idx_aware中的功能在可能的情況下移動到公共接口。容器的物體必須知道有關容器的事實有點可疑。 –

回答

0

羅伯特的評論暗示我這個解決方案。
爲什麼包含的對象知道它的容器?能夠執行諸如foo之類的操作並提供其他方式需要訪問容器的速記方法。

讓我們從包含的對象中去除這個功能;包含的對象只是數據有效載荷。相反,我們讓operator[]不返回包含的對象,而是返回某種迭代器,包含所包含對象的包裝器,它知道容器和索引,一旦解除引用就返回實際包含的對象。

class was_idx_aware { 
    int _some_field1; 
    float _some_field2; 
}; 

class container { 
    std::vector<idx_aware> _data; 
public: 
    class idx_aware_wrapper { 
     container const *_container; 
     std::size_t _idx; 
    public: 
     idx_aware_wrapper(container const &c, std::size_t i) 
      : _container(&c) 
      , _idx(i) 
     {} 

     was_idx_aware const &operator*() const { 
      return _container->_data[_idx]; 
     } 

     was_idx_aware &operator*() { 
      return _container->_data[_idx]; 
     } 

     void foo() { 
      // Do stuff using _container and _idx. 
     } 
    }; 


    idx_aware_wrapper operator[](std::size_t i) { 
     return idx_aware_wrapper(*this, i); 
    } 

    /* .... */ 
}; 

這使得was_idx_aware快速訪問任何數據,而包裝類可以與所有需要與容器相互作用的方法來增強。無需存儲並保持索引最新或覆蓋賦值運算符。