(Java)Collection in C++没有类似的概念。
我能理解其中的原因,但我想知道是否有任何方法可以优雅地 假 。
我已经实施了许多自定义Collection
它们都Iterator
正常工作,类似于std::vector
,std::unordered_set
等。
他们是MyArray<T>
,MyBinaryTree<T>
和MySet<T>
。
在这里,我将展示一个工作代码,显示我想要伪造它的位置。
我们说我有2个级别的程序:库和用户
只做一件事 - User
命令Library
来吃掉桶中的所有Orange*
。
Library.h
class Library{
public: static void eatAll(const MyArray<Orange*>& bucket);
};
Library.cpp
#include "Orange.h"
void Library::eatAll(const MyArray<Orange*>& bucket){
for(auto orange:bucket){
orange->eaten();
}
}
User.h
MyArray<Orange*> bucket;
Library::eatAll(bucket);
没关系。
现在,我希望Library::eatAll
也支持MyBinaryTree<Orange*>
,我有一些不太理想的方法,如下所示。
MyBinaryTree<T>
和MyArray<Orange*>
(及其迭代器)继承自新的类Collection<T>
(和CollectionIterator<T>
)。 Library::eatAll(const Collection<T>&)
缺点:来自&#34;虚拟&#34;的性能损失Collection<T>
中的一些函数。
//Library.h
template<class T> void eatAll(const T&t ){
for(auto orange : t){
orange->eaten();
}
}
eatAll
成为模板功能 缺点: eatAll
的实施必须在标题中
我必须在#include orange.h
中Library.h
有时,我真的想转发声明。
//Library.h
template<class T> void eatAll(const T&t ){
for(auto orange : t){
eatAnOrange(orange)
}
}
private: void eatAnOrange(Orange* orange){
//below implementation is inside Library.cpp
orange->eaten();
}
eatAnOrange
缺点:
squeezeAll()
,我可能需要创建很多中间函数,例如squeezeAnOrange()
。 通过隐式构造函数在3个集合类中创建转换器 缺点:创建新的集合实例会带来性能。
//Here is what it will do, internally (roughly speaking)
MyBinaryTree<Orange*> bucket;
Library::eatAll(MyArray<Orange*>(bucket));
我相信我的解决方案不够优雅 是否有任何解决方案不会遭受上述劣势?
修改
目前的答案都比我的方法更优雅(谢谢!),但仍有缺点: -
- Oliv在#include "orange.h"
中需要User.h
- 理查德·霍奇斯的虚拟函数调用。
答案 0 :(得分:7)
在C ++中,使用迭代器设计模式遍历集合。整个STL是围绕这个概念设计的。它可能符合您的需求:
您可以将eatAll
定义为接受两个迭代器的函数:
template<class Iterator,class Sentinel>
void eatAll(Iterator it, Sentinel s){
for (;it!=s;++it)
it->eaten();
}
或范围,如算法界面:
template<class Range>
void eatAll(Range& r){
for (auto& v:r)
v.eaten();
}
您必须将二叉树定义为范围(它必须实现begin()
和end()
)。希望树木是一种可以线性化的图形。然后,所有智能工作都将进入迭代器实现!
答案 1 :(得分:2)
如果你想让它真正具有多态性,那么我们必须处理两件事:
容器的实际类型
取消引用地图的结果是一对包含键和值引用的事实。
我的观点是,对此的答案不是从容器派生,这是限制,而是创建一个多态“值迭代器”,它模拟所有迭代器并正确提取它们的值。
然后我们可以编写这样的代码:
vector:
Orange
Orange
map:
Orange
Orange
Orange
要得到这个:
#include <typeinfo>
#include <memory>
#include <iostream>
#include <vector>
#include <map>
#include <iterator>
// define an orange
struct Orange {
};
// a meta-function to get the type of the value of some iterated value_type
template<class ValueType> struct type_of_value
{
using type = ValueType;
};
// specialise it for maps and unordered maps
template<class K, class V> struct type_of_value<std::pair<K, V>>
{
using type = V;
};
template<class ValueType> using type_of_value_t = typename type_of_value<ValueType>::type;
// function to extract a value from an instance of a value_type
template<class ValueType> struct value_extractor
{
template<class V>
auto& operator()(V&& v) const {
return v;
}
};
// specialised for maps
template<class K, class V> struct value_extractor<std::pair<K, V>>
{
template<class Arg>
auto& operator()(Arg&& v) const {
return std::get<1>(v);
}
};
template<class Iter>
auto extract_value(Iter const& iter) -> auto&
{
using value_type = typename std::iterator_traits<Iter>::value_type;
auto e = value_extractor<value_type> {};
return e(*iter);
}
// a polymorphic (forward only at the moment) iterator
// which delivers the value (in the case of maps) or the element (every other container)
template<class ValueType>
struct PolymorphicValueIterator {
using value_type = type_of_value_t<ValueType>;
private:
struct iterator_details {
std::type_info const &type;
void *address;
};
struct concept {
virtual std::unique_ptr<concept> clone() const = 0;
virtual value_type& invoke_deref() const = 0;
virtual void invoke_next(std::size_t distance = 1) = 0;
virtual iterator_details get_details() = 0;
virtual bool is_equal(const iterator_details &other) const = 0;
virtual ~concept() = default;
};
template<class Iter>
struct model final : concept {
model(Iter iter)
: iter_(iter)
{}
std::unique_ptr<concept> clone() const override
{
return std::make_unique<model>(iter_);
}
virtual value_type& invoke_deref() const override {
return extract_value(iter_);
}
void invoke_next(std::size_t distance = 1) override
{
iter_ = std::next(iter_, distance);
}
iterator_details get_details() override {
return {
typeid(Iter),
std::addressof(iter_)
};
}
bool is_equal(const iterator_details &other) const override {
if (typeid(Iter) != other.type) {
return false;
}
auto pother = reinterpret_cast<Iter const*>(other.address);
Iter const& iother = *pother;
return iter_ == iother;
}
Iter iter_;
};
std::unique_ptr<concept> concept_ptr_;
public:
bool operator==(PolymorphicValueIterator const &r) const {
return concept_ptr_->is_equal(r.concept_ptr_->get_details());
}
bool operator!=(PolymorphicValueIterator const &r) const {
return not concept_ptr_->is_equal(r.concept_ptr_->get_details());
}
PolymorphicValueIterator &operator++() {
concept_ptr_->invoke_next(1);
return *this;
}
value_type& operator*() const {
return concept_ptr_->invoke_deref();
}
template<class Iter>
PolymorphicValueIterator(Iter iter)
{
concept_ptr_ = std::make_unique<model<Iter>>(iter);
}
PolymorphicValueIterator(PolymorphicValueIterator const& r)
: concept_ptr_(r.concept_ptr_->clone())
{}
PolymorphicValueIterator& operator=(PolymorphicValueIterator const& r)
{
concept_ptr_ = r.concept_ptr_->clone();
return *this;
}
};
template<class Iter>
auto makePolymorphicValueIterator(Iter iter)
{
using iter_value_type = typename std::iterator_traits<Iter>::value_type;
using value_type = type_of_value_t<iter_value_type>;
return PolymorphicValueIterator<value_type>(iter);
}
// a test
void do_orange_things(PolymorphicValueIterator<Orange> first, PolymorphicValueIterator<Orange> last)
{
while(first != last) {
std::cout << "Orange\n";
++first;
}
}
int main()
{
std::vector<Orange> vo {
Orange(), Orange()
};
std::map<int, Orange> mio {
{ 1, Orange() },
{ 2, Orange() },
{ 3, Orange() }
};
std::cout << "vector:\n";
auto first = makePolymorphicValueIterator(vo.begin());
auto last = makePolymorphicValueIterator(vo.end());
do_orange_things(first, last);
std::cout << "\nmap:\n";
first = makePolymorphicValueIterator(mio.begin());
last = makePolymorphicValueIterator(mio.end());
do_orange_things(first, last);
}
这是一个最小的,完整的实现:
{{1}}