前言
打怪升级:第52天 |
---|
一、STL初始
- 什么是STL
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。 - STL的版本
原始版本
Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。 HP 版本–所有STL实现版本的始祖。
P. J. 版本
由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。
RW版本
由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。
SGI版本
由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。
如果大家了解过侯捷老师写的那本《STL源码剖析》就会知道,这本书参考的原码也是来自 SGI版本 – stl30,
下方,我也会仿照SGI中的代码风格来模拟实现vector和list容器的迭代器。
STL 提供了六大组件,彼此组合套用协同工作。这六大组件分别是:
二、六大组件之迭代器
迭代器初始
迭代器作为算法与容器之间的沟通枢纽,在stl中有着外交官的作用,由于每一个容器的数据结构不同,所以它们都有自己专属的一套迭代器,在SGI版本中,迭代器就是原生指针或者对原生指针的封装,并且对原生指针的封装也是为了模拟指针的功能,从而统一对容器的访问方式,极大的降低学习使用的成本。
那么下面,就让我们开始操作吧~!
迭代器的模拟实现
(1)victor
vector的底层是数组,因此支持随机访问,vector的正向迭代器可以直接使用原生指针;
vector的反向迭代器需要对原生指针进行封装,让它的行为可以和正向迭代器保持一致。
正向迭代器
示例:
namespace kz // 放到命名空间中,防止与库里的vector冲突
{
template<class T> // 模板
class vector
{
typedef T* iterator; // 正向迭代器直接使用原生指针 -- typedef是为了保持各个容器的迭代器命名相同
typedef const T* const_iterator; // 需要单独有一份const版本 -- 因为常量对象不能使用非常量成员函数
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator cbegin() const
{
return _start;
}
const_iterator cend() const
{
return _finish;
}
};
}
void Test_iterator1()
{
kz::vector<int>arr(10);
for (int i = 0; i < 10; ++i)
arr[i] = i;
kz::vector<int>::iterator it = arr.begin();
// for (; it < arr.end(); ++it) // vector中迭代器比较可以使用 < ,因为vector底层是数组,是一块连续的空间
for (; it != arr.end(); ++it) // 但是其他迭代器如list就不可以,因此为了所有迭代器保持一致,最好使用 !=
cout << *it << " ";
cout << endl << endl;
}
struct A
{
int _a1;
int _a2;
};
void Test_iterator2()
{
kz::vector<A>arr(6);
for (int i = 0; i < 6; ++i)
arr[i] = { i, i + 10 };
kz::vector<A>::iterator it = arr.begin();
for (; it != arr.end(); ++it)
cout << (*it)._a1 << " " << it->_a2 << endl;
}
反向迭代器1
vector的反向迭代器要和正向迭代器保持一致那就需要模拟正向迭代器的全部功能:++,–,比较大小,解引用,下标访问,以及如果数据元素是自定义类型我们还需要提供重载的箭头运算符: ->
template<class T>
struct Reverse_iterator
{
typedef Reverse_iterator<T> self;
T* _it;
Reverse_iterator(T* t = nullptr)
:_it(t)
{}
self& operator++()
{
--_it;
return *this;
}
self operator++(int)
{
self tmp = *this;
--_it;
return tmp;
}
self& operator--()
{
++_it;
return *this;
}
self operator--(int)
{
self tmp = *this;
++_it;
return tmp;
}
bool operator>(const self& t)const
{
return _it > t._it;
}
bool operator>=(const self& t)const
{
return _it > t._it || _it == t._it;
}
bool operator<(const self& t)const
{
return _it < t._it;
}
bool operator<=(const self& t)const
{
return _it < t._it || _it == t._it;
}
bool operator==(const self& t)const
{
return _it == t._it;
}
bool operator!=(const self& t)const
{
return _it != t._it;
}
T& operator[](int n)
{
return _it[-n];
}
T& operator*()
{
return *_it;
}
T* operator->()
{
return _it;
}
};
namespace kz // 放到命名空间中,防止与库里的vector冲突
{
template<class T> // 模板
class vector
{
typedef T* iterator; // 正向迭代器直接使用原生指针 -- typedef是为了保持各个容器的迭代器命名相同
typedef const T* const_iterator; // 需要单独有一份const版本 -- 因为常量对象不能使用非常量成员函数
typedef Reverse_iterator<T> reverse_iterator;
iterator rbegin()
{
return _finish-1;
}
iterator rend()
{
return _start-1;
}
const_iterator crbegin() const
{
return _finish-1;
}
const_iterator crend() const
{
return _start-1;
}
};
}
const int CNT = 10;
void Test_iterator3()
{
kz::vector<int>arr(CNT);
for (int i = 0; i < CNT; ++i)
arr[i] = i;
kz::vector<int>::reverse_iterator rit = arr.rbegin(); // 反向打印
while (rit != arr.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl << endl;
}
反向迭代器2
我们现在反向迭代器写好了,但是好像少了些什么:const迭代器好像还没有设计 – 非const对象的元素是可以修改的,而const迭代器不行,所以在对迭代器的解引用方面需要进行限制区分,
那么我们该如何实现呢?
- 我们可以和前面写begin(), 和const begin();那样将迭代器再拷贝一份并且改为const,这种做法好像没有问题,嗯~,其实这样确实没有问题,这也是我们这些“普通人”很容易想到的方法,可以解决问题,这里就不做演示;
- 不过下面,我们还是要来看一看大佬们是如何设计的,即使那不是我们现在可以达到的思想境界,我们也可以来瞻仰膜拜一番“斗宗强者”的真正实力:
template<class T, class Ref, class Ptr>
struct Reverse_iterator
{
typedef Reverse_iterator<T, Ref, Ptr> self;
T* _it;
Reverse_iterator(T* t = nullptr)
:_it(t)
{}
self& operator++()
{
--_it;
return *this;
}
self operator++(int)
{
self tmp = *this;
--_it;
return tmp;
}
self& operator--()
{
++_it;
return *this;
}
self operator--(int)
{
self tmp = *this;
++_it;
return tmp;
}
bool operator>(const self& t)const
{
return _it > t._it;
}
bool operator>=(const self& t)const
{
return _it > t._it || _it == t._it;
}
bool operator<(const self& t)const
{
return _it < t._it;
}
bool operator<=(const self& t)const
{
return _it < t._it || _it == t._it;
}
bool operator==(const self& t)const
{
return _it == t._it;
}
bool operator!=(const self& t)const
{
return _it != t._it;
}
Ref operator[](int n)
{
return _it[-n];
}
Ref operator*()
{
return *_it;
}
Ptr operator->()
{
return _it;
}
};
namespace kz
{
template<class T>
class vector
{
typedef Reverse_iterator<T, T&, T*> reverse_iterator; // 在容器中对反向迭代器的使用
typedef Reverse_iterator<T, const T&, const T*> const_reverse_iterator;
reverse_iterator rbegin()
{
return reverse_iterator(_finish - 1);
}
reverse_iterator rend()
{
return reverse_iterator(_start - 1);
}
const_reverse_iterator crbegin() const
{
return const_reverse_iterator(_finish - 1);
}
const_reverse_iterator crend() const
{
return const_reverse_iterator( _start - 1);
}
};
}
const int CNT = 10;
void Test_iterator3()
{
kz::vector<int>arr(CNT);
for (int i = 0; i < CNT; ++i)
arr[i] = i;
//kz::vector<int>::const_reverse_iterator rit = arr.crbegin();
auto crit = arr.crbegin();
while (crit != arr.crend())
{
cout << *crit << " ";
++crit;
}
cout << endl << endl;
}
与第一种方法相比,这样可以减少代码的长度,降低了维护成本,管理更加方便(这里代码少,如果代码有几千上万行效果就会更加明显)。
反向迭代器3
如果仅仅对vector来说,第二种的写法已经是非常优雅了,不过,如果放眼所有的stl容器,每一个拥有反向迭代器的容器我们都需要重写一份反向迭代器的代码,虽然各个代码都不一样,但是还是显得冗余,
我们的大佬们对反向迭代器的实现并不满意,为了化简实现方法,简短代码,大佬们抓耳挠腮、废寝忘食、头悬梁 …(bushi),最终通过对正向迭代器的复用,来实现反向迭代器的功能,当这种方法问世之后,大家惊为天人,纷纷为他们的灵光乍现点赞(家里门槛都被踩塌了啊·)。
那么我们就不废话,请看代码:
/ reverse
// 适配器 -- 复用
template<class Iterator, class Ref, class Ptr>
struct Reverse_iterator
{
typedef Reverse_iterator<Iterator, Ref, Ptr> self;
Iterator _it; // _it 是一个正向爹迭代器
Reverse_iterator(Iterator t)
:_it(t)
{}
self& operator++()
{
--_it; // 反向迭代器++,就是正向迭代器的--
return *this;
}
self operator++(int)
{
self tmp = *this;
--_it;
return tmp;
}
self& operator--()
{
++_it;
return *this;
}
self operator--(int)
{
self tmp = *this;
++_it;
return tmp;
}
Ref operator*()
{
return *(_it - 1);
}
Ptr operator->()
{
return _it;
}
Ref operator[](int i)
{
return _it[-i - 1];
}
bool operator==(const self& t)
{
return t._it == _it;
}
bool operator!=(const self& t)
{
return t._it != _it;
}
// 和list有些不同的是:vector的底层是数组,在内存中为一块连续的空间,因此可以进行>,<的比较
bool operator>(const self& t)
{
return t._it > _it;
}
bool operator>=(const self& t)
{
return t._it >= _it;
}
bool operator<(const self& t)
{
return t._it < _it;
}
bool operator<=(const self& t)
{
return t._it <= _it;
}
};
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
const_reverse_iterator crbegin() const
{
return const_reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
const_reverse_iterator crend() const
{
return const_reverse_iterator(begin());
}
至此,反向迭代器的实现就到了到了终点,而上面的之中方法也是我们现在依旧在使用的。
(2)list
list的底层是一个带头双向循环链表,不支持迭代器,当然也不会支持> , < 等比较函数,
下面我们就直接上手操作:
正向迭代器
template<class T, class Ref, class Ptr>
struct Reverse_iterator
{
typedef Reverse_iterator<T, Ref, Ptr> self;
typedef list_node<T> node;
node* _node; // T依然是节点指针
Reverse_iterator(node* n = node())
:_node(n)
{}
self& operator++() // ++指向下一个节点
{
_node = _node->_prev;
return *this;
}
self operator++(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
self& operator--()
{
_node = _node->_next;
return *this;
}
self operator--(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
bool operator==(const self& t)
{
return _node == t._node;
}
bool operator!=(const self& t)
{
return _node != t._node;
}
Ref operator*() //
{
return _node->_val;
}
Ptr operator->()
{
return &_node->_val;
}
};
const int CNT = 10;
void Test_list1()
{
kz::list<int> l1;
for (int i = 0; i < CNT; ++i)
l1.push_back(i);
kz::list<int>::iterator it = l1.begin();
while (it != l1.end())
{
cout << *it << " ";
++it;
}
cout << endl << endl;
}
反向迭代器
vector中的反向迭代器复用了正向iterator,也就是说只要有正向迭代器的容器都可以使用该反向迭代器模板,
而所有的模板都是拥有正向迭代器的,因此,只要这个容器可以使用反向迭代器那么就都可以使用同一份reverse_iterator模板,
在stl_30(stl的SGI版本中一个)是将反向迭代器单独封装了一个头文件,所有需要的容器只需包头文件即可,不需要再单独写一份反向迭代器的代码。
const int CNT = 10;
void Test_list2()
{
kz::list<int> l1;
for (int i = 0; i < CNT; ++i)
l1.push_back(i);
kz::list<int>::reverse_iterator it = l1.rbegin();
while (it != l1.rend())
{
cout << *it << " ";
++it;
}
cout << endl << endl;
}
文章来源:https://www.toymoban.com/news/detail-434582.html
总结
以上就是我们迭代器的讲解以及模拟实现的全部内容,各个容器的正向迭代器各不相同,不过只要实现了正向迭代器,反向迭代器的实现就都可以使用同一个模板来实例化,从这里我们也可以看出我们的前辈们是多么的实力雄厚、深不可测,当我们还在为单个容器的反向迭代器头疼的时候,大佬们的眼光就已经放在了所有容器了。
reverse_iterator的最终实现请查看 上方的《反向迭代器3》;
感谢大家的来访,希望可以给各位带来启发与帮助。文章来源地址https://www.toymoban.com/news/detail-434582.html
到了这里,关于【c++迭代器模拟实现】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!