假设我有一个界面。接口(interface)具有开始和结束函数,因为派生类必须实现范围功能。用户只会使用接口(interface),不会知道派生类的实现。 我不能对所有派生类使用相同的迭代器(更具体地说,operator++() 是不同的)所以我必须创建一个抽象基迭代器类。
class BaseIterator
{
//...
public:
virtual Type operator*()
{
//Implementation
}
virtual bool operator!=(const BaseIterator&)
{
//Implementation
}
virtual BaseIterator& operator++() = 0;
}
//Interface
struct Interface
{
//other pure virtual functions
virtual BaseIterator& begin() = 0;
virtual BaseIterator& end() = 0;
}
在继承我正在使用的接口(interface)的具体类中,假设其中一个是 A 类,每个类都有自己的迭代器,它继承自 BaseIterator,并使用它来实现开始和结束函数。
class A : public Interface
{
//...
class AIterator : public BaseIterator
{
AIterator& operator++()
{
//...
}
}
public:
AIterator& begin()
{
//...
}
AIterator& end()
{
//...
}
}
其他派生类也类似。当我尝试将 for range 循环与多态类型一起使用时,就会出现问题。例如 (*)
Interface* c = Interface::makeA(); //assume for simplicity that there is static function in "Interface"
for(auto el : *c)
{
//do something with el
}
由于纯虚函数运算符++(),我得到一个无法实例化抽象类的错误。我认为发生这种情况的原因在于 for-range 循环的实现,它等效于以下内容:
auto && __range = range_expression ;
for (auto __begin = __range.begin(), __end = __range.end(); __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement;
}
我认为问题在于“auto__begin == __range.begin()”。 begin 返回对 BaseIterator 的引用,由于自动类型推导,它被删除,最终使 __begin 成为 BaseIterator 类型,这是抽象类,无法实例化。 我知道这种行为可以用 Java 实现。 我在这里错过了什么吗?如果没有,您将如何实现它但保留 (*) 中的功能?
最佳答案
请张贴一个完整的例子来说明 future 的问题。我猜你面临的错误类似于 ( https://wandbox.org/permlink/PKop4WMbpuygHoes )
prog.cc:73:19: error: cannot allocate an object of abstract type 'BaseIterator'
73 | for(auto&& i: a) {
| ^
prog.cc:3:7: note: because the following virtual functions are pure within 'BaseIterator':
3 | class BaseIterator
| ^~~~~~~~~~~~
prog.cc:19:27: note: 'virtual BaseIterator& BaseIterator::operator++()'
19 | virtual BaseIterator& operator++() = 0;
| ^~~~~~~~
prog.cc:73:19: error: cannot declare variable '__for_begin ' to be of abstract type 'BaseIterator'
73 | for(auto&& i: a) {
| ^
prog.cc:73:19: error: cannot allocate an object of abstract type 'BaseIterator'
prog.cc:73:19: error: cannot declare variable '__for_end ' to be of abstract type 'BaseIterator'
问题很明显,因为 for 循环试图将 begin()
的返回值分配给局部变量,但它不能。
C++ 不是 Java。基于范围的for循环要么调用begin
/end
成员函数,要么调用自由函数,因此不需要接口(interface)获取迭代器。您的类只需实现这些功能,基于范围的 for 就可以工作。除此之外,在基类中使用 operator!=
可以比较两个不同子类型的迭代器。要检查这一点,您需要使用 dynamic_cast
来检查类型。您还需要在实现中降低参数,这显然不是一个好的设计。
对于 C++ 中的这类问题,继承并不是那么突出,首选使用模板的通用代码,例如在STL中。由于迭代器类型是模板参数,所有算法都将使用兼容的迭代器而无需继承。
这是一个使用简单的 Range 类作为迭代器容器的示例: #包括
struct Range;
struct Iterator {
Range const* r;
int current;
Iterator(Range const* x, int c) : r(x), current(c) {}
Iterator& operator++() {
++current;
return *this;
}
int const& operator*() const {
return current;
}
};
bool operator==(Iterator const& x, Iterator const& y) {
return x.current == y.current;
}
bool operator!=(Iterator const& x, Iterator const& y) {
return !(x == y);
}
struct Range {
int min;
int max;
Iterator begin() const {
return Iterator(this, min);
}
Iterator end() const {
return Iterator(this, max+1);
}
};
int main() {
Range r{-5, 5};
for(auto&& i: r) {
std::cout << i << std::endl;
}
}
这是一个简化且不完整的示例,仅用于展示如何使其与基于范围的 for 循环一起使用。如果要实现符合标准库中迭代器的迭代器,则需要添加一堆成员函数和类型定义。应该有很多关于如何编写这些迭代器的信息。
PS:如果您决定对接口(interface)使用类似 Java 的方法,请不要忘记为您的接口(interface)提供一个(公共(public)或 protected )虚拟析构函数并将复制构造函数声明为 delete
以防止对象切片.
关于C++ 自定义迭代器和范围问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58347228/