我有一个自定义矢量容器,在内部存储项目线性数组。昨晚,我试图为我的类实现自定义迭代器,以便能够将它们与STL算法一起使用。我在这里看到了一些成功:
Live example with custom iterators
在这样做时,我发现我只能将原始指针传递给STL算法,它们似乎工作得很好。这是没有任何迭代器的例子:
#include <cstddef>
#include <iostream>
#include <iterator>
#include <algorithm>
template<typename T>
class my_array{
T* data_;
std::size_t size_;
public:
my_array()
: data_(NULL), size_(0)
{}
my_array(std::size_t size)
: data_(new T[size]), size_(size)
{}
my_array(const my_array<T>& other){
size_ = other.size_;
data_ = new T[size_];
for (std::size_t i = 0; i<size_; i++)
data_[i] = other.data_[i];
}
my_array(const T* first, const T* last){
size_ = last - first;
data_ = new T[size_];
for (std::size_t i = 0; i<size_; i++)
data_[i] = first[i];
}
~my_array(){
delete [] data_;
}
const my_array<T>& operator=(const my_array<T>& other){
size_ = other.size_;
data_ = new T[size_];
for (std::size_t i = 0; i<size_; i++)
data_[i] = other.data_[i];
return other;
}
const T& operator[](std::size_t idx) const {return data_[idx];}
T& operator[](std::size_t& idx) {return data_[idx];}
std::size_t size(){return size_;}
T* begin(){return data_;}
T* end(){return data_+size_;}
};
template<typename T>
void print(T t) {
std::cout << t << std::endl;
}
int main(){
typedef float scalar_t;
scalar_t list [] = {1, 3, 5, 2, 4, 3, 5, 10, 10};
my_array<scalar_t> a(list, list+sizeof(list)/sizeof(scalar_t));
// works!
for (scalar_t* it = a.begin(), *end = a.end();
it != end; ++it)
std::cout << ' ' << *it;
std::cout << std::endl;
// works!
std::for_each(a.begin(), a.end(), print<scalar_t>);
std::cout << std::endl;
// works!
my_array<int> b(a.size());
std::copy(a.begin(), a.end(), b.begin());
// works!
scalar_t* end = std::remove(a.begin(), a.end(), 5);
std::for_each(a.begin(), end, print<scalar_t>);
std::cout << std::endl;
// works!
std::random_shuffle(a.begin(), end);
std::for_each(a.begin(), end, print<scalar_t>);
std::cout << std::endl;
// works!
std::cout << "Counts of 3 in array = " << std::count(a.begin(), end, 3) << std::endl << std::endl;
// works!
std::sort(a.begin(), end);
std::for_each(a.begin(), end, print<scalar_t>);
std::cout << std::endl;
// works!
if (!std::binary_search(a.begin(), a.end(), 5))
std::cout << "Removed!" << std::endl;
return 0;
}
Live example without iterators
我的问题如下:
答案 0 :(得分:16)
基于运算符重载的迭代器的一个特性是指针已经是随机访问迭代器。这是STL早期的一项重大设计胜利,因为它使得使用现有代码的算法变得更容易(以及使程序员更熟悉界面)。包装数组,添加typedef T* iterator; typedef const T* const_iterator
,从&array[0]
返回begin()
和从&array[size]
返回end()
,然后在任何迭代器中使用容器是完全合理的基于算法。正如您已经意识到的,这适用于任何元素在内存中连续的容器(例如数组)。
如果符合以下条件,您可以实现'真实'迭代器:
T*
意外地分配给my_array::iterator
。我说单独的最后一个优势非常值得写一个简单的包装类。如果你不通过使不同类型的东西有不同的类型来利用C ++的类型系统,你不妨切换到Javascript: - )
答案 1 :(得分:9)
答案 2 :(得分:4)
指针提供了随机访问迭代器所需的接口(取消引用,增量,加法,差异等),并且可以像迭代器一样对待。
T*
类型设置为迭代器类型,这可能不是一个重要问题。另外,一些算法可能会受益于使用迭代器类型标记的迭代器,而对于简单的指针类型则无法做到这一点。答案 3 :(得分:4)
这是否始终适用于具有线性存储的容器?
是的,迭代器概念的设计使得指针可以充当数组上的迭代器。
如果他们在这种情况下工作,我为什么要经历实施迭代器的麻烦?
在这种情况下,没有充分的理由来定义自己的迭代器类型,除非你想做像边界检查这样的事情,这是用简单的指针完成的。
一个小的好处是你可以为迭代器的特性包含嵌套的typedef,就像一些标准的迭代器类型那样;但是使用指针,无论如何都可以从std::iterator_traits<T*>
获得。
如果这种方法始终有效,我正在做什么的负面问题?首先,我可以看到我打破了数据封装。
要使接口与STL样式的容器更加一致,您应该为指针定义iterator
和const_iterator
类型(typedef
别名),并提供const
重载begin
和end
;对于C ++ 11兼容性,可能cbegin
和cend
。
您可能希望遵守各种其他要求;有关血淋淋的细节,请参阅C ++标准的第23.2节。但总的来说,让迭代器符合它们的要求更为重要,因为STL样式算法使用迭代器而不是容器,并且使用指针已经符合这些要求。