假设我有一个C ++迭代器,它不仅遍历数据结构,而且在取消引用时也对元素应用转换。
作为一个真实的例子,这里有一个迭代器遍历位图中的像素,将位图特定的像素格式转换为方便的结构:
class ConstPixelIterator {
public: struct Pixel {
float Red;
float Green;
float Blue;
float Alpha;
};
public: ConstPixelIterator(const Bitmap &bitmap);
// ...standard iterator functionality...
public: Pixel operator *() {
// Read from memory and convert pixel format-specific bytes into Pixel structure
}
};
现在,如果我想实现一个非const迭代器(即让用户修改像素),那么最好的方法是什么?
我考虑过的一些想法:
我可以将访问器方法放在Pixel
结构而不是普通字段中,并为其所有者提供电话回家的参考。 但这意味着如果用户更改了R,G,B和A,我会将像素转换为位图的像素格式4次,然后写入内存4次。
我可以从迭代器返回一个Pixel引用,并为其提供一个Update()
方法,如果像素被更改,则需要调用该方法。 这是非直观的,风险用户忘记致电Update
。
我总是可以按值返回Pixel
并提供一个特殊的赋值运算符。 是否破坏标准迭代器模式 - 在没有解除引用的情况下分配给迭代器应该移动迭代器,而不是更新它指向的元素
答案 0 :(得分:3)
我们在std::vector<bool>::iterator
中有一个现有示例 - 必须通过一些技巧来写入一个位。
一种解决方案是返回ProxyPixel
。它保留对原始像素的引用。您声明更新R,G,B,A可能会导致4次写入。这是真的,也是可以理解的。在第一次写入R之后,底层图像应该具有更新的R值。
或者您是否乐意接受最终更新?在这种情况下,您可以将回写延迟到ProxyPixel::~ProxyPixel
。是的,随着代理像素的更改,底层图像将暂时不同步,但效率会更高。合理的权衡。
答案 1 :(得分:0)
我可以在Pixel结构中放置访问器方法而不是普通字段,并为其所有者提供电话回家的参考。然而,这意味着如果用户改变了R,G,B和A,我会将像素转换为位图的像素格式4次并写入内存4次。
这推动了demeter的规律(像素不应该改变位图来设置它自己的值);你不应该这样做。
我可以从迭代器返回一个Pixel引用,并为它提供一个Update()方法,如果像素被更改,则需要调用该方法。这将是非直观的,并且风险用户忘记致电更新。
这是一个糟糕的界面,因为在不记住内容的情况下正确使用会很棘手。它也违反了demeter的布局(你不应该通过像素更新位图)。
我总是可以按值返回Pixel并提供一个特殊的赋值运算符。是否打破标准迭代器模式 - 在没有解除引用的情况下分配给迭代器应该移动迭代器,而不是更新它指向的元素
不要这样做;它违反了最不惊讶的原则。
考虑使像素独立于位图(即像素不知道位图是什么)。
您应该能够轻松地将(有效)值设置到像素中(使用字段或Update(R, G, B, A)
或类似的访问器)并让位图知道像素是什么以及如何从像素更新自身,其中一个(或多个):
void Bitmap::Update(int x, int y, const Pixel& p);
void Bitmap::Fill(const Pixel& p);
void Bitmap::SetRange(SomeRangeObject r, const Pixel& p);
这样,对像素的更新操作将需要以下步骤:
这不包括谁知道像素的位置(它们可能不应该是像素本身的一部分),但这是一个开始。
使用此实现,您的相互依赖性保持较低(更新位图像素的功能保留在位图中,而不是像素中),并且迭代模型很简单(与标准迭代器相同)。
答案 2 :(得分:0)
Pixel结构的成员可以实现为属性。
这可以让你写出像
这样的东西iter->r = 0;
它不允许您将像素结构发送到那些函数 期待它是'straigtforward'
在c ++中实现属性很简单,例如参见here
多次写入内存应该不是问题,因为它会缓存本地