B站头图景深对焦效果

请在图片上左右移动鼠标查看效果

关于 迭代器 的看法

1. 如何获取一个迭代器(iterator)

2. 迭代器本身能够执行什么操作

3. 哪些表 ADT 方法需要迭代器作为参数


iterator begin():  返回一个适当的迭代器,表示容器中的第一项

iterator end(): 返回一个适当的迭代器,表示容器的尾端(终端)标记(即容器中最后一项之后的位置)

end  方法有些不同寻常,因为它返回一个“越界”的迭代器


迭代器可以用!=和==进行比较,

itr++ 和 ++itr :将迭代器推进到下一个位置。

前缀形式和后缀形式都可以使用的。

*itr :返回对存储在迭代器 itr 的位置上对象的引用。

所返回的引用可以允许修改,也可以不允许修改。

itr1 = itr2 :若 itr1 和 itr2 指向同一个位置则返回 true ,否则返回 false。

itr1 !=itr2 :若 itr1 和 itr2 指向不同的位置则返回 true ,否则返回 false。


 iterator insert (iterator pos,const object &x): 把 x 添加到表中由迭代器 pos 所给定的位置之前的位置上。

这是对 list (但不是对 vector )的常数时间的操作。返回值是指向被插入项的位置的一个迭代器。

iterator erase (iterator pos) : 删除由迭代器所给定的位置上的对象。这是对 list (但不是对 vector )的常数时间的操作。返回值是调用之前 pos 的后继元素所在的位置。

该操作使 pos 失效,现在它是多余的了,因为它正在指向的容器项已经被删除。

iterator erase (iterator start,iterator end): 删除从位置 start 开始直到(但不包括)位置 end ,为止的所有的项。

注意:整个表可以通过调用 c.erase(c.begin(),c.end()) 而被删除。


使用迭代器隔项删除(vector 或 list)中的项。

对 list 是高效的,但对 vector 则不是

复制代码
template <typename container>
void removeEverOtherItem(container & lst){
    auto itr = lst.begin();
    //itr 是一个container::iterator
    while(itr!=lst.end){
        itr = lst.erase(itr);
        if(itr!=lst.end())
            ++itr;
    }
}
复制代码

第4 行上 auto 的使用时C++11的特色,它使我们避免了更长的类型 Container::iterator 。

线性例程

因为运行时间是按照输入大小的相同增长因子增长的。


const_iterators

*itr 的结果不只是迭代器正在指向的项的值,而且还有该项本身。

这个特点使得迭代器非常强大,不过也带来了一些副作用,

假设我们想要把一个集合中的所有项改成一个特定的值。

下面例程对于 vector 和 list 都是高效的并且是以线性时间运行的,它是编写一般的,类型无关代码的完美示例。

template <typename container,typename object>
void change(container & c,const object & newvalue){
    typename container::iterator itr = c.begin();
    while(itr!=c.end())
        *itr++ = newvalue;
}

 

要想看清潜在的问题,假设 container c 是使用常量引用调用被传递到例程的。

这意味着我们不想对 c 做任何的改变,而编译器将通过禁止调用 c 的任何修改函数来保证这一点。

考虑下列代码,它打印由一些整数构成的 list 而且还试图对 list 进行隐蔽的修改:

void print(const list<int> & lst,ostream & out = cout){
    typename Container::iterator itr = lst.begin();
    while(itr!=lst.end()){
        out << *itr << endl;
        *itr = 0;
        ++itr;
    }
}

 

上面的代码太容易被绕开了。

STL  提供的解决方案是:

每一个集合容器不仅包含一个内嵌类型 iterator,而且还有一个内嵌类型的 const_iterator。在 iterator 和 const_iterator 之间的主要区别在于,

对于 cost_iterator ,operator * 返回一个常量引用,因而对于 const_iterator 的 *itr 不能出现在赋值语句的左边。

作为 C++11 中的自由函数,begin 和 end 的加入通过添加语言特色 auto 和 decltype 而成为可能。

 

template <typename container>
auto begin(container & c)-> decltype(c.begin()){
    return c.begin();
}
template <typename container>
auto begin(const container & c)->decltype(c.begin()){
    return c.begin();
}

 

 

begin 的返回类型经推导是 c.begin() 的类型。

打印任意容器

复制代码
template <typename container>
void print(const container &c,ostream &out = cout){
    if(c.empty())
        out << "(empty)";
        else{
            auto itr = begin(c);
            out << "[" << *itr++;
            while(itr!=end(c))
                out << "," << *itr++;
            out << "]" << endl;
        }
}
复制代码

 


 

 vector 的实现

 

复制代码
#include <algorithm>
template <typename object>
class vector
{
    public:
    explicit vector(int initsize = 0):thesize{initsize},
    thecapacity{initsize+spare_capacity}
    {
        objects = new object[thecapacity];
    }
    vector (const vector & rhs):thesize{rhs.thesize},
    thecapacity{ rhs.thecapacity },objects{ nullptr }
    {
        objects = new object[thecapacity];
        for (int k = 0; k < thesize;++k)
            object[k] = rhs.objects[k];
    }
    vector & operator=(const vector &rhs)
    {
        vector copy = rhs;
        std::swap(*this, copy);
        return *this;
    }
    ~vector(){
        delete[] objects;
    }
    vector(vector && rhs):thesize{rhs.thesize},
    thecapacity{rhs.capacity},object{rhs.objects}
    {
        rhs.objects = nullptr;
        rhs.thesize = 0;
        rhs.thecapacity = 0;
    }
    vector & operator=(vector && rhs)
    {
        std::swap(thesize, rhs.thesize);
        std::swap(thecapacity, rhs.thecapacity);
        std::swap(objects, rhs.objects);

        return *this;
    }

    void resize(int newsize)
    {
        if(newsize>thecapacity)
            reserve(newsize * 2);
        thesize = newsize;
    }
    void reserve(int newcapacity)
    {
        if(newcapacity<thesize)
            return;
        object *newarray = new object[newcapacity];
        for (int k = 0; k < thesize;++k)
            newarraay[k] = std::move(object[k]);

        thecapacity = newcapacity;
        std::swap(objects, newarray);
        delete[] newarray;
    }

    object & operate[](int index)
    {
        return objects[index];
    }
    const object & operator[](int index)const
    {
        return objects[index];
    }
    bool empty() const
    {
        reutrn size() == 0;
    }
    int capacity() const
    {
        return thecapacity;
    }
    void push_back(const object & x)
    {
        if(thesize==thecapacity)
            reserve(2 * thecapacity + 1);
        objects[thesize++] = x;
    }
    void push_back(object && x)
    {
        if(thesize == thecapacity)
            reserve(2 * thecapacity + 1);
        objects[thesize++] = std::move(x);
    }
    void pop_back()
    {
        --thesize;
    }
    const object & back () const
    {
        return object[thesize - 1];
    }
    typedef object *iterator;
    typedef const object *const_iterator;

    iterator begin()
    {
        return &objects[0];
    }
    const_iterator begin() const
    {
        return &objects[0];
    }
    iterator end()
    {
        return &objects[size()];
    }
    const_iterator end()const
    {
        return &objects[size()];
    }

    static const int spare_capacity = 16;

    private:
        int thesize;
        int thecapacity;
        object *objects;
};
复制代码

在 C++中,指针变量拥有我们对 iterator 期望的所有相同的运算符。

指针变量可以被复制和比较;* 运算符得到指针所指向的对象,而最特别的是,

当++用于指针变量时,指针变量则指向顺序存储的下一个对象:

如果指针正在指向数组内部,则增加指针的位置使它指向数组的下一元素。


 

list 的实现

4个类

1.list 类本身,它包含连接到表两端的链,表的大小,以及一些方法。

2.node 类,它很可能是一个私有的内嵌类。一个节点包含数据和指向前后两个节点的两个指针,以及一些适当的构造函数。

3.const_iterator 类,它抽象了位置的概念,而且是一个公有的内嵌类。该 const_iterator 存储一个指向“当前”节点的指针,并提供基本迭代器操作的实现,所有的操作,像=,==,!=和++等,均以重载运算符的形式出现。

4。iterator 类,它抽象了位置的概念,而且是一个公有的内嵌类,与3.有相同的功能,但 operator* 返回的是所指项的引用,而不是对该项的常量引用。一个重要的技术问题是,iterator可以用在任何需要const_iterator的例程中,但反之不真。换句话说,iterator 是一个 const_iterator。

复制代码
template <typename object>
class list
{
    private:
    struct node{
        object data;
        node *prev;
        node *next;
        
        node(const object & d = object{ },node *p=nullptr,
                               node *n = nullptr)
            :data{d},prev{p},next{n} { }
            node(object && d,node *p = nullptr,node *n = nullptr)
               :data{ std::move(d)},prev{p},next{n} { }
    };
    public:
    class const_iterator{
        public:
        const_iterator():current{ nullptr }
        { }
        const object & operator * ( )const{
            return retrieve();
        }
        const_iterator & operator++(){
            current = current->next;
            return *this;
        }

        const_iterator operator++ (int){
            const_iterator old = *this;
            ++(*this);
            return old;
        }

        bool operator == { const const_iterator & rhs }const
        {
            return current == rhs.current;
        }
        bool operator!=( const const_iterator & rhs) const{
            return !(*this == rhs);
        }

        protected:
            node *current;
            object & retrieve() const{
                return current->data;
            }
            const_iterator(node *p):current{p}
            {}
            friend class list<object>;
    };
    class iterator : public const_iterator{
        public:
        iterator()
        {}
        object & operator * ()
        {
            return const_iterator::retrieve();
        }
        const object & operator * () const{
            return const_iterator::operator*();
        }

        iterator & operator++(){
            this->current = this->current->next;
            return *this;
        }

        iterator & operator++(int){
            iterator old = *this;
            ++(*this);
            return old;
        }

        protected:
        iterator(node *p):const_iterator{p}
        {}
        friend class list<object>;
    };

    public:
    list(){
        init();
    }
    ~list(){
        clear();
        delete head;
        delete tail;
    }

    list(const list & rhs){
        init();
        for(auto & x:rhs)
            push_back(x);
    }

    list & operator = (const list & rhs){
        list copy = rhs;
        std::swap(*this, copy);
        return *this;
    }

    list(list && rhs)
    :thesize{rhs.thesize},head{rhs.head},tail{rhs.tail}
    {
        rhs.thesize = 0;
        rhs.head = nullptr;
        rhs.tail = nullptr;
    }

    list & operator =(list && rhs){
        std::swap(thesize, rhs.thesize);
        std::swap(head, rhs.head);
        std::swap(tail, rhs.tail);
        return *this;
    }

    iterator begin(){
        return {head->next};
    }
    const_iterator begin()const{
        return {head->next};
    }
    iterator end(){
        return {tail};
    }
    const_iterator end()const{
        return {tail};
    }

    init size()const{
        return thesize;
    }
    bool empty()const{
        reutrn size() == 0;
    }

    void clear(){
        while(!empty())
            pop_front();
    }

    object & front(){
        return *begin();
    }
    const object & front() const{
        return *begin();
    }
    object & back(){
        return *--end();
    }
    const object & back() const{
        return *--end();
    }
    void push_front(const object & x){
        insert(begin(), x);
    }
    void push_front(object && x){
        insert(begin(), std::move(x));
    }
    void push_back(const object &x){
        insert(end(), x);
    }
    void push_back(object && x){
        insert(end(), std::move(x));
    }
    void pop_front(){
        erase(begin());
    }
    void pop_back(){
        erase(--end());
    }

    iterator insert(iterator itr,const object & x){
        node *p = itr.current;
        thesize++;
        return {p->prev = p->prev->next = new node{x, p->prev, p}};
    }
    iterator insert(iterator itr,object && x){
        node *p = itr.current;
        thesize++;
        return {p->prev = p->prev->next = new node{std::move(x), p->prev, p}};
    }

    private:
        int thesize;
        node *head;
        node *tail;
        void init(){
            thesize = 0;
            head = new node;
            tail = new node;
            head->next = tail;
            tail->prev = head;
    }
};
复制代码

 

posted @   acmWang  阅读(64)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
返回顶端

2021-09-22 (星期三)
09:08:39 +08:00

点击右上角即可分享
微信分享提示