我有一個類idx_aware
,進入一個容器container
,它圍繞着std::vector
。當該類被添加到container
時,container
在其內部存儲器存儲器中設置指向自身的指針idx_aware
以及idx_aware
索引。C++ OOP:Class知道它在容器中的索引 - 防止覆蓋?
索引不會改變,直到container
被銷燬或idx_aware
被刪除; idx_aware
需要知道它的容器及其索引,因爲它有一些方法需要這兩個工作。
現在,這引入了以下問題:當我得到一個非const引用包含在container
的idx_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 ¬_in_container1 = /* ... */;
idx_aware ¬_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 ...
我根本就不允許通過賦值操作符來改變索引。 – MikeMB
雖然這樣做可能有很好的理由,但我建議(重新)評估是否需要索引和容器地址的with_idx和idx_aware中的功能在可能的情況下移動到公共接口。容器的物體必須知道有關容器的事實有點可疑。 –