假设我有一个接口。接口具有begin和end函数,因为派生类必须实现for-range功能。用户将只使用接口,而不知道派生类的实现。我不能对所有的派生类使用相同的迭代器(更具体地说,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;
}在继承我使用的接口的具体类中,假设其中一个是类A,每个类都有自己的迭代器,该迭代器继承自BaseIterator,并使用它来实现begin和end函数。
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
}我得到一个错误,我无法实例化抽象类,因为是纯虚函数operator++()。我认为发生这种情况的原因是在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的引用,由于auto类型推导被移除,最终导致__begin类型为BaseIterator,这是抽象类,不能实例化。我知道这种行为可以在Java中实现。我是不是漏掉了什么?如果没有,您将如何实现此功能,但将功能保留在(*)中?
发布于 2019-10-12 05:26:26
请在将来发布一个完整的例子来说明这个问题。我猜您面临的错误类似于(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成员函数,要么调用自由函数,因此不需要接口来获取迭代器。您的类只需实现这些函数,基于范围的就可以工作。除此之外,在基类中使用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循环一起工作。如果您想实现符合标准库中迭代器的迭代器,则需要添加一组成员函数和typedefs。应该有大量关于如何编写这些迭代器的信息。
PS:如果您决定对接口使用类似Java语言的方法,请不要忘记为您的接口提供(公共的或受保护的)虚拟析构函数,并将复制构造函数声明为delete,以防止对象切片。
https://stackoverflow.com/questions/58347228
复制相似问题