我有一个类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中的功能在可能的情况下移动到公共接口。容器的物体必须知道有关容器的事实有点可疑。 –