智能指针、容器、迭代器的一些常见问题总结

最近在coding的时候在使用迭代器时遇到了一些的问题,都是因为自己理解不当造成的。总结几点分享给大家避免重复踩坑~

以下讨论均为序列式容器

  • 问题1:迭代器中的begin()和end()分别指向容器中的哪个元素?

请看如下代码:

#include <iostream>
#include <vector>
using namespace std;

int main(){
    
    
    vector<int>a(5,1); //创建一个有5个1的vector容器
    vector<int>::iterator p_a;
    for (p_a = a.begin(); p_a != a.end(); p_a++) {
    
      //使用迭代器遍历
        cout << *p_a  << " ";
    }
    return 0;
}

运行结果:
1 1 1 1 1

在使用begin(),end()返回迭代器的时候,总是错误的认为“begin()表示容器的首元素的位置,end()表示容器最后一个元素的位置”。如果真是如此,那应该不会输出最后一个元素。

但是从以上代码可以看出,迭代器遍历了整个容器,输出了所有元素。

在这里插入图片描述

在c++ primer中的解释为,end()成员返回指向容器“尾元素的下一个位置的迭代器”,这样的迭代器没有什么实际含义,仅是一个标记而已,表示我们已经处理完了容器的所有元素,也被称作尾后迭代器。即begin()指向迭代器的第一个元素,end()指向迭代器最后一个元素的下一个位置。

  • 问题2:iterator和const_iterator有哪些区别?

迭代器不同的声明方式决定了它的读写类型,c++primer中指出迭代器的标准库类型使用iterator和const_iterator来表示迭代器类型:

vector<int>::iterator it; //it能读写vector<int>的元素
vector<int>::const_iterator it2; //it2能读元素,不能写元素 

const_iterator类似于常量指针,能读区但不能修改它所指元素值。如果vector对象是一个常量,那么只能使用const_iterator。若不是,则都能使用。

  • 问题3:迭代器在哪些时候会失效?调用erase()会使当前迭代器失效吗?

c++primer指出,向容器中添加元素和从容器中删除元素的操作可能会使指向容器元素的指针、引用或迭代器失效。

向容器添加元素后:

  1. 如果容器使vector或者string,且存储空间被重新分配,则指针、引用和迭代器都会失效。如果存储空间未被重新分配,则插入位置之前元素的指针、引用和迭代器有效,但指向插入位置之后的指针、引用和迭代器失效。
  2. 对于deque,插入到除首尾位置之外的任何位置都会导致迭代器失效。若是首尾位置添加元素,仅迭代器失效,指针引用均不失效

若是从容器中删除元素,只想被删除元素的迭代器、指针和引用均失效:

  1. 对于容器vector或者string,只想被删除元素之前的迭代器、指针和引用有效。
  2. 对于deque,在首尾之外的任何位置删除元素,那么指向被删除元素外的其他元素的迭代器、指针和引用也会失效。如果删除deque的尾元素,则尾后迭代器也失效。

由于向迭代器添加、删除元素可能导致迭代器失效,必须保证每次改变容器的操作后都正确的重新定位迭代器

#include <iostream>
#include <vector>
using namespace std;

int main(){
    
    
    vector<int> a;
    vector<int>::iterator p_a;
    for (int i = 0; i < 10; i++) {
    
     //给容器赋值1-10
        a.push_back(i);
    }
    p_a = a.begin()+2; //迭代器指向元素2的位置
    a.erase(p_a);
    cout << *p_a<< endl;
    return 0;
}

运行结果
3

在调用erase()后,返回的迭代器已经指向序列中下一个元素,erase()后的迭代器全部失效,但erase()当前迭代器仍有效

  • 问题4:vector保存智能指针可以被析构吗?

这里用shared_ptr作为示例,每个shared_ptr对象在内部指向两个位置:

  1. 指向对象的指针;
  2. 指向用于控制引用计数的指针;

示例代码如下:

#include <iostream>
#include <vector>
#include <memory>
using namespace std;

int main(){
    
    
    {
    
    
        vector<shared_ptr<int>> a;
        {
    
    
            shared_ptr<int> c(new int);  //1号智能指针,作用域为花括号{}
            shared_ptr<int> d(c); //2号智能指针,作用域为花括号{}
            *c = 100;
            a.push_back(c); //3号智能指针,作用域为vector
            a.push_back(d); //4号智能指针,作用域为vector
            cout << c.use_count() << endl; //push_back()拷贝了一个智能指针,所以use_count()值为4
            cout << *a[0] << ' ' << *a[1] << endl; //输出vector3、4号智能指针中的值
        }
        cout << a[0].use_count() << endl;  //1、2号智能指针指针被析构,use_count()降为2
        a.pop_back();
        cout << a[0].use_count() << endl;  //vector中的4号智能指针被析构,use_count()降为1
    }
    return 0;
}

运行结果为:

4
100 100
2
1

具体的原理代码注释里写的很清楚了。 可以直接得出结论,push_back()函数拷贝的智能指针作用域为vector的生命周期。当vector被清空时,里面的智能指针被自动析构

  • 总结

1、end()指向容器中最后一个元素的下一个位置;

2、如果vector对象是一个常量,那么只能使用const_iterator。若不是,则都能使用;

3、在调用erase()时,erase()后的迭代器全部失效,但erase()当前迭代器仍有效;

4、push_back()函数拷贝的智能指针的作用域为vector。当vector被清空时,里面的智能指针被自动析构。此外,push_back()本身是一种拷贝行为,将使share_ptr计数+1。

猜你喜欢

转载自blog.csdn.net/zxxkkkk/article/details/110174284