抱歉这很长,但我无法弄清楚如何缩短它并仍然显示我问题的重要特征。
我问这个,即使我想出了如何做我想要的事情,但它需要以不与标准迭代器模式匹配的方式实现迭代器。我想问一下如何以更标准或更好的方式做到这一点的任何建议。
我有一个抽象的基类。它的任何子类都有一些对象集合。我想在基类中放入一些需要迭代集合中所有对象的公共代码。集合中的对象都将来自另一个抽象基类,但它们将是子类对象。可能存在来自多个子类的多个对象集合,并且集合可以是不同的集合类型。我真正想做的是这样的事情:(注意:这段代码不会编译。)
class CollectionObjectBase
{
public:
virtual int someInterface(int x) = 0;
};
class MyObjectBase
{
public:
class iterator
{
public:
virtual CollectionObjectBase& operator*() = 0;
virtual iterator& operator++() = 0;
virtual bool operator!=(iterator& other) = 0;
};
virtual iterator begin() = 0;
virtual iterator end() = 0;
int processCollections()
{
iterator it;
int sum = 0;
for(it = begin(); it != end(); ++it)
{
sum += (*it).someInterface(7);
}
return sum;
}
};
然后我想要一些CollectionObjectBase和MyObjectBase的子类,如下所示:
class CollectionObject1 : public CollectionObjectBase { ... }
class CollectionObject2 : public CollectionObjectBase { ... }
class CollectionObject3 : public CollectionObjectBase { ... }
class MyObjectA : public MyObjectBase
{
public:
std::map<int, CollectionObject1> someThings;
std::vector< CollectionObject2> otherThings;
class iterator : public MyObjectBase::iterator
{
public:
std::map<int, CollectionObject1>::iterator it1;
std::vector< CollectionObject2>::iterator it2;
// Iterate first over it1 and then over it2.
...
};
virtual MyObjectBase::iterator begin()
{
return iterator(someThings.begin(), otherThings.begin());
}
virtual MyObjectBase::iterator end()
{
return iterator(someThings.end(), otherThings.end());
}
};
class MyObjectB : public MyObjectBase
{
public:
std::vector<CollectionObject1> someThings;
std::set< CollectionObject3> otherThings;
class iterator : public MyObjectBase::iterator
{
public:
std::vector<CollectionObject1>::iterator it1;
std::set< CollectionObject3>::iterator it2;
// Iterate first over it1 and then over it2.
...
};
virtual MyObjectBase::iterator begin()
{
return iterator(someThings.begin(), otherThings.begin());
}
virtual MyObjectBase::iterator end()
{
return iterator(someThings.end(), otherThings.end());
}
};
上述代码存在一些大问题。首先,MyObjectBase :: begin()和MyObjectBase :: end()不能返回MyObjectBase :: iterator,因为MyObjectBase :: iterator是一个抽象类,你不会想要,因为你真的想要一个MyObjectBase :: iterator的子类,它知道如何迭代MyObjectBase子类中的集合。您需要返回对MyObjectBase :: iterator的引用。
第二个问题来自MyObjectA :: iterator :: operator!=()。你想做这样的事情:
virtual bool operator!=(iterator& other)
{
return it1 != other.it1 || it2 != other.it2;
}
但要成为MyObjectBase :: iterator :: operator!=()的重载,它必须采用MyObjectBase :: iterator&amp;类型的参数。并且它没有成员it1和it2。我最好的想法我想解决这个问题是用函数atEnd()替换operator!=(),因为在这段代码中它唯一被用来检测迭代器是否在最后,但它确实是非标准的。
最后,MyObjectA :: begin()和MyObjectA :: end()无法返回临时值。请记住,我们必须返回对指向MyObjectA :: iterator的MyObjectBase :: iterator的引用,而不是复制构造MyObjectBase :: iterator。我最好的想法我想解决这个问题就是用新的方式分配迭代器,当我这样做时,我需要返回一个指针,而不是一个引用,以便调用者可以删除它。所以这是我的最终代码。它工作并执行迭代器的功能,但它是非标准的。任何人都可以想到一种方法来使用符合标准模式的迭代器吗?
#include <map>
#include <vector>
class CollectionObjectBase
{
public:
virtual int someInterface(int x) = 0;
};
class MyObjectBase
{
public:
class iterator
{
public:
virtual CollectionObjectBase& operator*() = 0;
virtual iterator& operator++() = 0;
virtual bool atEnd() = 0;
};
virtual iterator* begin() = 0;
int processCollections()
{
iterator* it;
int sum = 0;
for (it = begin(); !it->atEnd(); ++it)
{
sum += (**it).someInterface(7);
}
delete it;
return sum;
}
};
class CollectionObject1 : public CollectionObjectBase
{
public:
virtual int someInterface(int x)
{
return x + 1;
}
};
class CollectionObject2 : public CollectionObjectBase
{
public:
virtual int someInterface(int x)
{
return x + 2;
}
};
class MyObjectA : public MyObjectBase
{
public:
std::map<int, CollectionObject1> someThings;
std::vector< CollectionObject2> otherThings;
class iterator : public MyObjectBase::iterator
{
public:
std::map<int, CollectionObject1>::iterator it1;
std::map<int, CollectionObject1>::iterator it1End;
std::vector< CollectionObject2>::iterator it2;
std::vector< CollectionObject2>::iterator it2End;
iterator(std::map<int, CollectionObject1>::iterator it1Init, std::map<int, CollectionObject1>::iterator it1EndInit,
std::vector< CollectionObject2>::iterator it2Init, std::vector< CollectionObject2>::iterator it2EndInit) :
it1(it1Init),
it1End(it1EndInit),
it2(it2Init),
it2End(it2EndInit)
{
// Initialization handled by initialization list.
}
virtual CollectionObjectBase& operator*()
{
if (it1 != it1End)
{
return (*it1).second;
}
else
{
return *it2;
}
}
virtual iterator& operator++()
{
if (it1 != it1End)
{
++it1;
}
else
{
++it2;
}
return *this;
}
virtual bool atEnd()
{
return it1 == it1End && it2 == it2End;
}
};
virtual MyObjectBase::iterator* begin()
{
return new iterator(someThings.begin(), someThings.end(), otherThings.begin(), otherThings.end());
}
};
有一个无用的问题&#39;回答。考虑Impl和Base的这些子类:
class MyImpl : public Base::Iterator::Impl {
public:
MyImpl(std::vector<CollectionObjectSub>::iterator itInit) : it(itInit) {}
CollectionObjectBase& operator*() { return *it; }
void operator++() { ++it; }
// bool operator==(Base::Iterator::Impl const &other) const { it == other.it; }
bool operator==(MyImpl const &other) const { it == other.it; }
private:
std::vector<CollectionObjectSub>::iterator it;
};
class Sub : public Base {
public:
Iterator& begin() { return Iterator(new MyImpl(collection.begin())); }
private:
std::vector<CollectionObjectSub> collection;
};
Sub包含一个向量,MyImpl包含一个向量迭代器。我想用impl_指向MyImpl来实例化一个Base :: Iterator。如果MyImpl :: operator ==采用MyImpl&amp;那么它不会超载Base :: Iterator :: Impl :: operator ==,但如果需要Base :: Iterator :: Impl&amp;那么即使它实际上是一个MyImpl,我也无法访问向量迭代器。
似乎我需要进行动态强制转换来获取MyImpl,这需要RTTI。我听说如果可能的话,避免使用RTTI。还有另一种方式吗?
答案 0 :(得分:1)
因此,您希望按值返回迭代器,但其动态类型会根据您调用的begin
/ end
的覆盖而有所不同?没问题 - 只需添加另一层间接。
class Base {
public:
class Iterator {
public:
struct Impl {
virtual ~Impl() {}
virtual CollectionObjectBase& operator*() = 0;
virtual void operator++() = 0;
virtual bool operator==(Iterator::Impl const &other) const = 0;
};
Iterator() {}
explicit Iterator(std::unique_ptr<Impl> &&i) : impl_(std::move(i)) {}
Iterator(Iterator &&other) = default;
CollectionObjectBase& operator*() { return **impl_; }
Iterator& operator++() { ++*impl_; return *this; }
bool operator==(Iterator const &other) const {
return (!impl_ && !other.impl_) || (*impl_ == *other.impl_);
}
private:
std::unique_ptr<Impl> impl_;
};
// ...
这使用指向impl (pimpl)的习惯用法为Base
的每个具体子类赋予其Base::Iterator::Impl
的具体子类,但仍允许你传递{ {1}}使用移动语义来转移值,以转移动态对象的所有权。
NB。我使两个默认构造的(nullptr)迭代器比较相等,因此您不需要为Base::Iterator
创建一个真实实例。