c++ OOP:类知道它在容器中的索引-防止覆盖
C++ OOP: Class knows its index in the container - prevent overwrite?
我有一个类idx_aware
进入容器container
,它包裹着std::vector
。当这个类被添加到container
时,container
设置了一个指向idx_aware
的指针,以及idx_aware
在其内存中的索引。
除非container
被销毁或idx_aware
被删除,否则索引不会改变;idx_aware
需要知道它的容器和索引,因为它有一些方法需要两者都工作。
现在这引入了以下问题:当我得到container
中包含的idx_aware
类的非const引用时,我可以为它分配另一个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;
}
一个可能的解决方法是改变with_idx
,这样当set_idx
被调用时,设置一个标志,防止赋值和复制操作符覆盖_i
属性,像这样:
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)
- 是否有一种设计模式可以更好地模拟这种情况?我的搜索没有成功。
- 以这种方式重写赋值操作符还有其他不想要的结果吗?我在前面的例子中指出的限制看起来并不太"糟糕"。
- 是否有更简单的解决方案?我想写一些代理对象来代替
operator[]
的idx_aware &
返回类型。
经验告诉我们,当c++不能做你想做的事情时,你很可能误用了OOP…
Robert的评论给了我这个解决方案。
为什么被包含的对象知道它的容器?能够执行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
中的任何数据,并且包装器类可以使用需要与容器交互的所有方法进行扩展。
相关文章:
- 数组索引的值没有增加
- 芬威克树(BIT).找到具有给定累积频率的最小索引,单位为 O(logN)
- 使用ios:ate写入到流会覆盖现有文件
- 查找最接近的大于当前数字的数字的索引
- 在C++中调整向量中的索引
- 重载元组索引运算符-C++
- 给定一个向量,如何找到该向量的所有子集和的原始索引
- 为std::string的某个索引赋值
- 并行用于C++17中数组索引范围内的循环
- 我可以重新分配/覆盖std::字符串吗
- 跟随整数索引列表的自定义类迭代器
- 叮叮当当在修复时插入多个"覆盖"说明符
- 谷歌模拟和覆盖关键字
- 如何在for循环中包含两个索引值的测试条件
- D3D11-将混合权重和索引传递到顶点着色器
- 将转换字符键入 int 以用作向量C++的索引
- 在 C++ 中访问数组负索引处的内存不会返回垃圾
- TMap::Emplace() 在应用现有密钥时会覆盖吗?
- 将字符串中的索引覆盖为 int
- c++ OOP:类知道它在容器中的索引-防止覆盖