假设我有一个我想通过API公开的[acme]对象流。我有两个选择,回调和迭代器。
// API #1
// This function takes a user-defined callback
// and invokes it for each object in the stream.
template<typename CallbackFunctor>
void ProcessAcmeStream(CallbackFunctor &callback);
// API #2
// Provides the iterator class AcmeStreamIterator.
AcmeStreamIterator my_stream_begin = AcmeStreamIterator::begin();
AcmeStreamIterator my_stream_end = AcmeStreamIterator::end();
API#1从用户手中获取程序的控制流,并且在整个流消耗之前不会返回(暂时忘记异常)。
API#2保留用户手中的控制流,允许用户自己前进流。
API#1感觉更多更高级别,允许用户立即跳转到业务逻辑(回调函子)。另一方面,API#2感觉更灵活,允许用户进行较低级别的控制。
从设计角度来看,我应该选择哪一个?我还没有看到更多的利弊吗?未来有哪些支持/维护问题?
答案 0 :(得分:9)
迭代器方法更灵活,回调版本可以通过现有算法轻松实现第一个:
std::for_each( MyStream::begin(), MyStream::end(), callback );
答案 1 :(得分:6)
IMO,第二个显然优越。虽然我可以(有点)理解你的感觉它是较低的水平,但我认为这是不正确的。第一个定义了它自己的“更高级别”的特定概念 - 但它与C ++标准库的其余部分不相符,最终相对难以使用。特别是,它要求如果用户想要的东西等同于标准算法,则必须从头开始重新实现,而不是使用现有代码。
第二个完全适合库的其余部分(假设您正确实现了迭代器),并为用户提供了通过标准算法在更高级别处理数据的机会(和/或者遵循标准模式的新的非标准算法。
答案 2 :(得分:5)
回调函数的一个 回调优势 是API的用户不会搞乱迭代。可以很容易地比较错误的迭代器,或使用错误的比较操作或以其他方式失败。回调API阻止了这一点。
使用回调轻松完成取消枚举,BTW:只需让回调返回bool
并继续,只要它返回true
即可。
答案 3 :(得分:4)
C ++标准库习惯用法是提供迭代器。如果您提供迭代器,那么ProcessAcmeStream
是std::for_each
的简单包装器。也许值得写作的麻烦,也许不是,但它并没有完全将你的调用者提升到一个全新的可用性世界,它是一个新的名称,用于将标准库函数应用到你的迭代器对。
在C ++ 0x中,如果你也通过std::begin
和std::end
使迭代器对可用,那么调用者可以使用基于范围的for,这将它们带入业务逻辑,就像{{ {1}}确实可能更快。
所以我会说,如果有可能提供一个迭代器然后提供它 - 如果调用者想要以这种方式编程,C ++标准会对你进行控制反转。至少,对于控制就像它一样简单的情况。
答案 4 :(得分:2)
从设计的角度来看,我会说迭代器方法更好,因为它更简单,也更灵活;在没有lambdas的情况下制作回调函数真的很烦人。 (现在C ++ 0x将具有lambda表达式,但这可能不那么令人担忧,但即便如此,迭代器方法也更通用。)
回调的另一个问题是取消。你可以返回一个布尔值来表示你是否想要取消枚举,但是当控件不在我手中时我总是感到不安,因为你并不总是知道会发生什么。迭代器没有这个问题。
当然,总有一个问题是迭代器可以是随机访问而回调不是,因此它们也更具可扩展性。