一、string字符串对象的迭代器iterator实现
1、基本原理
当我们给一个对象赋值了一个字符串,例如:
String str1 = “hello world”;
当我们想遍历该字符串的时候,你有想过如何遍历吗?因为其是在底层放了一组char类型的字符,我们是看不见的。
由此,我们引出了容器的迭代器类型这个概念。
具体实现如下:
String::iterator it = str1.begin();
for (; it != str1.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
其原理如下图:
特点:
- 该类型属于容器类型的嵌套类
- 容器内部的元素是不可见的,迭**代器可以透明的访问容器内部元素的值。**通俗来讲,不管容器底层是什么数据结构,怎么推进。例如:泛型算法参数接收的都是迭代器,他是一个全局函数是给所有函数用的,
有一套方式能够统一的遍历所有的容器的元素 - 他的功能是:提供一种统一的方式,来透明的遍历容器
因此,我们需要自己实现迭代器里面的相关运算符。
2、运算符的实现
(1)给String字符串类型提供迭代器的实现
因为他是一个内嵌类型,所以写在string类的public里面。
迭代器还需要提供 * 运算符重载,访问迭代器所迭代元素的值,迭代器解引用访问的就是容器底层数据。
class iterator
{
public:
iterator(char *p = nullptr):_p(p){ }
bool operator !=(const iterator& it)
{
return _p != it._p;//迭代器的不相等就是底层指针的不相等
}
void operator++()
{
++_p;
}
char& operator*() { return *_p; }
private:
char* _p;
};
(2)begin()方法
返回的是让其底层首元素的迭代器的表示
iterator begin() { return iterator(_pstr); }
(3)end()方法
返回的是容器末尾元素后继位置的迭代器的表示
iterator end() { return iterator(_pstr + length()); }
二、实现vector容器的迭代器
因为vector也是一种容器,所以我们还是可以使用上述迭代器,
(1)具体实现如下:
template <typename T>
struct Allocator
{
T* allocate(size_t size)//只负责内存开辟
{
return (T*)malloc(sizeof(T) * size);
}
void deallocate(void* p)//只负责内存释放
{
free(p);
}
void construct(T* p, const T& val)//已经开辟好的内存上,负责对象构造
{
new (p) T(val);//定位new,指定内存上构造val,T(val)拷贝构造
}
void destroy(T* p)//只负责对象析构
{
p->~T();//~T()代表了T类型的析构函数
}
};
template <typename T, typename Alloc = Allocator<T>>
class vector//向量容器
{
public:
vector(int size = 10)//构造
{
//_first = new T[size];
_first = _allocator.allocate(size);
_last = _first;
_end = _first + size;
}
~vector()//析构
{
//delete[]_first;
for (T* p = _first; p != _last; ++p)
{
_allocator.destroy(p);//把_first指针指向的数组的有效元素析构
}
_allocator.deallocate(_first);//释放堆上的数组内存
_first = _last = _end = nullptr;
}
vector(const vector<T>& rhs)//拷贝构造
{
int size = rhs._end - rhs._first;//空间大小
//_first = new T[size];
_first = _allocator.allocate(size);
int len = rhs._last - rhs._first;//有效元素
for (int i = 0; i < len; ++i)
{
//_first[i] = rhs._first[i];
_allocator.construct(_first + i, rhs._first[i]);
}
_last = _first + len;
_end = _first + size;
}
vector<T>& operator=(const vector<T>& rhs)//赋值运算符重载
{
if (this == &rhs)
{
return *this;
}
//delete[]_first;
for (T* p = _first; p != _last; ++p)
{
_allocator.destory(p);//把_first指针指向的数组的有效元素析构
}
_allocator.deallocate(_first);//释放堆上的数组内存
int size = rhs._end - rhs._first;//空间大小
_first = _allocator.allocate(size);
int len = rhs._last - rhs._first;//有效元素
for (int i = 0; i < len; ++i)
{
_allocator.construct(_first + i, rhs._first[i]);
}
_last = _first + len;
_end = _first + size;
return *this;
}
void push_back(const T& val)//尾插
{
if (full())
{
expand();
}
//*_last++ = val;
_allocator.construct(_last, val);//_last指针指向的内存构造一个值为val的对象
_last++;
}
void pop_back()//尾删
{
if (empty()) return;
//--_last;
//不仅要把_last指针--,还需要析构删除的元素
--_last;
_allocator.destroy(_last);
}
T back()const//返回容器末尾元素值
{
return *(_last - 1);
}
bool full()const
{
return _last == _end;
}
bool empty()const
{
return _first == _last;
}
int size()const//返回容器中元素个数
{
return _last - _first;
}
T& operator[](int index)
{
if (index < 0 || index >= size())
{
throw "OutOfRangeException";
}
return _first[index];
}
//迭代器一般实现成容器的嵌套类型
class iterator
{
public:
iterator(T* ptr = nullptr)
:_ptr(ptr) {}
bool operator!=(const iterator& it)const
{
return _ptr != it._ptr;
}
void operator++()
{
_ptr++;
}
T& operator*()
{
return *_ptr;
}
const T& operator*()const
{
return *_ptr;
}
private:
T* _ptr;
};
iterator begin()
{
return iterator(_first);
}
iterator end()
{
return iterator(_last);
}
private:
T* _first;//起始数组位置
T* _last;//指向最后一个有效元素后继位置
T* _end;//指向数组空间的后继位置
Alloc _allocator;//定义容器的空间配置器对象
void expand()//扩容
{
int size = _end - _first;
//T *ptmp = new T[2*size];
T* ptmp = _allocator.allocate(2 * size);
for (int i = 0; i < size; ++i)
{
_allocator.construct(ptmp + i, _first[i]);
//ptmp[i] = _first[i];
}
//delete[]_first;
for (T* p = _first; p != _last; ++p)
{
_allocator.destroy(p);
}
_allocator.deallocate(_first);
_first = ptmp;
_last = _first + size;
_end = _first + 2 * size;
}
};
(2)迭代器遍历的三种方式
方式一:因为vector是一种数组容器,连续空间可以利用下标访问,但是这种方式遍历只针对vector有用
int size = vec.size();
for (int i = 0; i < size; ++i)
{
cout << vec[i] << " ";
}
cout << endl;
方式二:迭代器通常访问方式
vector<int>::iterator it = vec.begin();
for (; it != vec.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
方式三:c++11中更简便的foreach的方式来遍历容器的内部元素的值.底层还是通过迭代器进行遍历的
for (int val : vec)
{
cout << val << " ";
}
cout << endl;