問題描述
我有一個自定義向量容器,它在內(nèi)部存儲一個線性數(shù)組項.昨晚,我試圖為我的類實現(xiàn)自定義迭代器,以便能夠?qū)⑺鼈兣c STL 算法一起使用.我取得了一些成功,你可以在這里看到:
I have a custom vector container that internally stores item a linear array. Last night, I was trying to implement custom iterators for my class to be able to use them with STL algorithms. I have had some success that you can see in here:
自定義迭代器的實例
這樣做時,我發(fā)現(xiàn)我只能將原始指針傳遞給 STL 算法,而且它們似乎工作正常.這是沒有任何迭代器的示例:
While doing so, I discovered I can merely pass raw pointers to STL algorithm and they just seem to work fine. Here's the example without any iterators:
#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;
}
沒有迭代器的實例
我的問題如下:
- 這是否總是適用于具有線性存儲的容器?例如,我知道這不適用于鏈表.
- 如果它們在這種情況下確實有效,我為什么還要經(jīng)歷實現(xiàn)迭代器的麻煩?我知道迭代器如何概括我的代碼等等,但是如果這個簡單的數(shù)組就是我所需要的一切,那么我就看不到重點了.
- 如果這種方法始終有效,我正在做的事情有哪些負面問題?一方面,我可以看到我正在破壞數(shù)據(jù)封裝.
- Does this always work for containers that have linear storage? I know that this would not work for linked-lists for example.
- If they do work in this situation, why should I ever go through the hassle of implementing iterators anyway? I know how iterators generalize my code and whatnot, but if this simple array is all I ever need then I don't see the point.
- What are the negative issues of what I'm doing if this approach would always work? For one thing, I can see I'm breaking data encapsulation.
推薦答案
基于運算符重載的迭代器的特性之一是指針已經(jīng)是隨機訪問迭代器.在 STL 的早期,這是一個巨大的設計勝利,因為它可以更輕松地將算法與現(xiàn)有代碼一起使用(并使程序員更熟悉界面).包裹一個數(shù)組是完全合理的,添加 typedef T* iterator;typedef const T* const_iterator
,從你的 begin()
和 &array[size]
返回 &array[0]
從您的 end()
開始,然后將您的容器與任何基于迭代器的算法一起使用.正如您已經(jīng)意識到的,這適用于任何元素在內(nèi)存中是連續(xù)的容器(例如數(shù)組).
One of the features of iterators being based on operator-overloading, is that pointers are already random-access iterators. This was a big design win in the early days of STL, as it made it easier to use algorithms with existing code (as well as making the interface more familiar to programmers). It's perfectly reasonable to wrap an array, add typedef T* iterator; typedef const T* const_iterator
, return &array[0]
from your begin()
and &array[size]
from your end()
, and then use your container with any iterator-based algorithm. As you already realise, this will work for any container where elements are contiguous in memory (such as an array).
如果滿足以下條件,您可能會實現(xiàn)真正的"迭代器:
You might implement 'real' iterators if:
- 您有一個不同形狀的容器(例如樹或列表);
- 您希望能夠在不使迭代器失效的情況下調(diào)整數(shù)組大小;
- 您想在迭代器使用中添加調(diào)試檢查,例如檢查迭代器是否在失效或容器被刪除后使用,或進行邊界檢查;
- 您想引入類型安全性,并確保人們不會意外地將任意
T*
分配給my_array::iterator
.
- You have a different-shaped container (such as a tree or list);
- You want to be able to resize the array without invalidating the iterators;
- You want to add debugging checks to your iterator use, for example to check if the iterator is used after being invalidated or after the container has been deleted, or to bounds-check;
- You want to introduce type-safety, and make sure people can't accidentally assign an arbitrary
T*
to amy_array::iterator
.
我想說僅憑最后一個優(yōu)勢就值得為其編寫一個簡單的包裝類.如果你不利用 C++ 的類型系統(tǒng)讓不同類型的東西有不同的類型,你不妨切換到 Javascript :-)
I'd say this last advantage alone is well worth writing a trivial wrapper class for. If you don't take advantage of C++'s type system by making different kinds of thing have different types, you might as well switch to Javascript :-)
這篇關于對于具有線性存儲的容器,可以使用原始指針代替帶有 STL 算法的迭代器嗎?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!