是否可以在C++中有一个转换迭代器

Is it Possible to Have a Transforming Iterator in C++?

本文关键字:有一个 转换 迭代器 C++ 是否      更新时间:2023-10-16

假设我有一个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
  }
};

现在,如果我想实现一个非常量迭代器(即让用户修改像素),最好的方法是什么?

我考虑过的一些想法:

  • 我可以将访问器方法放在Pixel结构中而不是普通字段中,并为其所有者提供对呼叫总部的引用。但是,这意味着如果用户更改了 R、G、B 和 A,我将像素转换为位图的像素格式 4 次,写入内存 4 次。

  • 我可以从迭代器返回一个 Pixel 引用,并为其提供一个Update()方法,如果像素发生更改,则需要调用该方法。这将是不直观的,并且有用户忘记拨打Update的风险。

  • 我总是可以按值返回Pixel并提供一个特殊的赋值运算符。确实破坏了标准的迭代器模式 - 在不取消引用的情况下分配给迭代器应该移动迭代器,而不是更新它指向的元素

我们在std::vector<bool>::iterator中有一个现有的例子 - 它必须拉一些技巧来写入一个位。

一种解决方案是返回ProxyPixel。它保留对原始像素的引用。您说过更新 R,G,B,A 可能会导致 4 次写入。这是真的,也是可以理解的。在第一次写入 R 之后,基础映像毕竟应该具有更新的 R 值。

或者你愿意接受最终的更新吗?在这种情况下,您可以延迟写回 ProxyPixel::~ProxyPixel 。是的,随着代理像素的更改,底层图像将暂时不同步,但效率会更高。合理的权衡。

我可以将访问器方法放在 Pixel 结构中而不是普通字段中,并为其所有者提供对呼叫总部的引用。但是,这意味着如果用户更改了 R、G、B 和 A,我将像素转换为位图的像素格式 4 次,写入内存 4 次。

这推动了德墨忒耳定律(像素不应更改位图以设置其自身的值);你不应该这样做。

我可以从迭代器返回一个 Pixel 引用,并为其提供一个 Update() 方法,如果像素发生更改,则需要调用该方法。这将是不直观的,并且有用户忘记调用更新的风险。

这是一个有点糟糕的界面,因为如果不记住东西,正确使用会很棘手。它还违反了德墨忒耳的布局(您不应该通过像素更新位图)。

我总是可以按值返回 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);

这样,对像素的更新操作将需要以下步骤:

  • 从位图中获取像素
  • 根据需要更新/转换像素
  • 从像素更新位图

这不包括谁会知道像素的位置(它们可能不应该是像素本身的一部分),但这是一个开始。

使用此实现,您的相互依赖关系保持较低(用于更新位图像素的功能保留在位图中,而不是在像素中),并且迭代模型是简单的(与标准迭代器相同)。

像素结构的成员可以作为属性实现。

这将允许您编写类似

iter->r = 0;

它不允许您将像素结构发送到以下函数:期待它"向前发展"

在 c++ 中实现属性很容易,请参阅此处的示例

多次写入内存应该不是问题,因为它将缓存本地

相关文章: