是否可以在C++中有一个转换迭代器
Is it Possible to Have a Transforming Iterator in C++?
假设我有一个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++ 中实现属性很容易,请参阅此处的示例
多次写入内存应该不是问题,因为它将缓存本地
- 有一个打印语句的函数是一种糟糕的编程实践吗
- VSCode-有一个红色下划线,但程序构建和运行正确,并且出现配音错误
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 有没有可能有一个只有ADL才能找到的非好友功能
- 我有一个对象,它将在整个程序的持续时间内实例化,但一个类成员不会,我应该动态分配它吗?
- 我的程序有一个保存配置文件的GUI,如何双击此配置文件以直接加载带有配置数据的GUI?
- 在学习数据结构之前对STL有一个了解是好的吗?
- 我在 .h 中有一个枚举类,并且在.cpp错误中有一个运算符重载:与"运算符<<不匹配
- 如何在 Gnuplot 中分别绘制 2 个文件数据?我有一个文件"sin.txt",另一个文件"cos.txt",我想将它们分别绘制在一个图表上
- 是否可以在C++中有一个"generic"模板参数,该参数可以是非类型模板参数或类型?
- 从 int 中剥离位时,编译器会警告一个转换,但不警告其他转换.有解决方法吗?
- 错误:在“(”标记之前进行预期的构造函数、析构函数或类型转换.即使我有一个构造函数
- 它不是编译.我正在调用一个通过引用调用的函数,但有一个错误,无法将双*转换为双倍
- OPENCV是否有一个软件包将3D点转换为2D自上而下的视图
- 如果我有一个固定大小的填充ascii数字的char数组,我知道它指的是一个无符号整数,那么将其转换为无符号整数的最快方法
- 是否可以在C++中有一个转换迭代器
- win32(windows.h)中是否有一个方便函数可以将lParam转换为POINT
- 有一个转换构造函数有什么意义?
- c++中是否有一个使用opencv的函数,它允许将图像转换为MATLAB中的im2double的双精度
- 当我对输出进行类型转换时,有一个有趣的结果