一个通用的常量迭代器(const_iterator)包装类
最近在写代码的时候遇到这样一个功能需求:在一个类里面使用了一个容器,现在希望将里面的数据用只读方式暴露给外部来读取。这个功能当然很好实现,只需要在这个类里面实现公有的begin()/end()方法,实现时直接调用容器的begin()/end()函数即可。可是过了不一会,新的问题冒出来了:因为我突然发现容器里面的数据是原始数据,还需要对每个数据作一次变换才能使用,而我一方面需要保留这些原始数据,又不想另开缓冲区来存储变换后的数据,那么就只能在访问的同时变换数据。那么,需要解决的技术难题就是:如何能在使用迭代器访问的同时透明的对数据内容进行一次变换?
请看下面的代码,这里我将问题简化为以下的类:
{
typedef ::std::vector<FLOAT> StorageType;
typedef StorageType::const_iterator const_iterator;
StorageType data_;
public:
const_iterator begin() const
{
return data_.begin();
}
const_iterator end() const
{
return data_.end();
}
// 其他省略
};
// 以下为调用代码,source是DataSource的一个实例
DataSource::const_iterator first = source.begin();
foo(*first); // 此时我想临时变换source.data_中的数据怎么办?
在容器本身上动手术并不明智,因为我的最终目的是要让这个DataSource能够“侦听”到数据访问的“消息”并在其中做手脚,把容器和具体类的实现邦定在一起终究会出问题的,所以我把目光放在了const_iterator这个迭代器本身上。
于是,我设计了一个包装类来解决这个问题,基本思想是:包装类实现迭代器的所有concept,并使用迭代器和Functor一起作为参数来构造这个包装类,使得operator *和operator ->的重载中可以使用Functor来临时修改容器中读出来的数据。
完整的代码直接给出来好了,这是一个最简单但可用的类:
class const_iterator_wrapper
{
typedef const_iterator_wrapper MyType;
typedef typename ContainerType::const_iterator IteratorType;
typedef typename ContainerType::value_type ValueType;
ValueType value_;
IteratorType it_;
Functor func_;
public:
const_iterator_wrapper(const IteratorType & it, const Functor & func = Functor())
: it_(it)
, func_(func)
, value_()
{
}
const ValueType & operator * ()
{
value_ = *it_;
func_(value_);
return value_;
}
const ValueType * operator -> ()
{
value_ = *it_;
func_(value_);
return &value_;
}
MyType & operator ++ ()
{
++it_;
return *this;
}
MyType operator ++ (int)
{
MyType old = *this;
++it_;
return old;
}
MyType & operator -- ()
{
--it_;
return *this;
}
MyType operator -- (int)
{
MyType old = *this;
--it_;
return old;
}
bool operator == (const MyType & me)
{
if (me.it_ == it_)
{
return true;
}
else
{
return false;
}
}
bool operator != (const MyType & me)
{
if (me.it_ != it_)
{
return true;
}
else
{
return false;
}
}
bool operator == (const IteratorType & it)
{
if (it == it_)
{
return true;
}
else
{
return false;
}
}
bool operator != (const IteratorType & it)
{
if (it != it_)
{
return true;
}
else
{
return false;
}
}
private:
const_iterator_wrapper()
{
}
};
在这里,其实有三个地方是很明显应该改变的,但我为了简化问题而没有在这里体现:第一个是没有实现所有的iterator的concept,包括+、-、+=、-=等运算符重载我懒的写了,反正我的应用里面用不到;第二个是重复调用Functor的问题,其实我应该提供更多的模板参数来判断当前的value_是否已经是转换过后的值——之所以要提供模板参数而不是每次都这样做,是因为有时候Functor执行的代价甚至于比检查value_是否已经是转换过后的值还要小,这是有可能的;第三个是没有将iterator的所有traits提取出来,也没有做必要ValueType类型推导,这些都需要boost::type_traits支持,而且写出来之后会让代码变得很难懂,算了……还有第四个是对Functor的concept的约束,我并没有检查,实际上,我要求Functor类一定要实现Functor::operator () (const ValueType&)才行,否则会报错。
使用方法则很直白,首先当然要写一个Functor——这个Functor可以与任何的类紧密藕合在一起,甚至于为了方便访问类内部的成员而被声明为友元类也行,因为这种Functor就是类内部私有的东西。先写个最简单的:
{
void operator () (int & i)
{
i += 4;
}
};
这样,使用这个包装类只需要写如下的代码:
程序代码typedef const_iterator_wrapper<TheContainer, Alerter> wrapper;
TheContainer container;
container.push_back(1);
container.push_back(3);
container.push_back(5);
container.push_back(6);
container.push_back(3);
container.push_back(9);
container.push_back(4);
const TheContainer & ref = container;
wrapper first = ref.begin();
wrapper last = ref.end();
for (; first != last; ++first)
{
std::cout << *first << “ ”;
}
可以看到,wrapper因为用了默认Functor构造,所以直接可以将容器的常量迭代器直接用隐式构造的方式初始化一个wrapper类。然后,wrapper的用法就和一般的迭代器别无二致了。
如果实现一个稍微复杂一点、带构造参数的Functor,当然就需要自己构造一个然后作为构造参数传给wrapper啦,其中一定要注意,wrapper在判断相等或不等的时候不会理睬Functor的差异,所以只要Functor有默认构造函数,那么end()迭代器还是可以用简洁方式来构造wrapper。
剩下的,就等着对这个类进一步的发展了。这只是一个简单的包装,不过还是能够实现很多令人兴奋的功能的,起码,它帮了我的大忙。
以上的代码在Visual C++.Net 2003上调试通过。

有话想说?请留下评论吧~~如果喜欢我的blog,欢迎订阅~~
评论
还没有任何评论。
留下评论