C++vector

文章目录

  • vector的介绍及使用
    • vector的介绍
    • vector的使用
      • vector的定义
      • vector的遍历
      • vector iterator 的使用
    • vector 空间增长问题
    • vector 增删查改
    • vector 迭代器失效问题
    • vector 在OJ中的使用
    • vector深度剖析及模拟实现
      • 模拟实现vector
    • 使用memcpy拷贝问题
    • 对vector核心接口的测试

vector的介绍及使用

vector的介绍

vector的文档介绍

  1. vector是表示可变大小数组的序列容器。
  2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自
    动处理。
  3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小
    为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
  4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
  5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
  6. 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。

vector的使用

vector的定义

(constructor)构造函数声明 接口说明
vector()(重点) 无参构造
vector(size_type n, const value_type& val = value_type()) 构造并初始化n个val
vector (const vector& x); (重点) 拷贝构造
vector (InputIterator first, InputIterator last); 使用迭代器进行初始化构造
// constructing vectors
#include 
#include 

int main ()
{
    // 构造函数的使用
    std::vector<int> first; // 无参构造
    std::vector<int> second (4,100); // 初始化为4个整型数字100
    std::vector<int> third (second.begin(),second.end()); // 用second的迭代器初始化,类似于拷贝构造second
    //也可以用其他类的迭代器初始化
    string s("hello");
    vector<char> v(s.begin(), s.end());
    std::vector<int> fourth (third); // 拷贝构造
    
    
    // 下面涉及迭代器初始化的部分,我们学习完迭代器再来看这部分
    // the iterator constructor can also be used to construct from arrays:
    int myints[] = {16,2,77,29};
    std::vector<int> fifth (myints, myints + sizeof(myints) / sizeof(int) );
    std::cout << "The contents of fifth are:";
    
    for (std::vector<int>::iterator it = fifth.begin(); it != fifth.end(); ++it)
    std::cout << ' ' << *it;
    
    std::cout << '\n';
    return 0;
}

vector的遍历

1.size()+operator[]

2.迭代器

3.范围for

vector iterator 的使用

iterator的使用 接口说明
begin + end(重点) 获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置 的iterator/const_iterator
rbegin + rend 获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的 reverse_iterator

C++vector_第1张图片

#include 
#include 
using namespace std;

void PrintVector(const vector<int>& v)
{
    // const对象使用const迭代器进行遍历打印
    vector<int>::const_iterator it = v.begin();
    
    while (it != v.end())
    {
        cout << *it << " ";
        ++it;
    }
    
    cout << endl;
}

int main()
{
    // 使用push_back插入4个数据
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    
    // 使用迭代器进行遍历打印
    vector<int>::iterator it = v.begin();
    
    while (it != v.end())
    {
        cout << *it << " ";
        ++it;
    }
    
    cout << endl;
    
    // 使用迭代器进行修改
    it = v.begin();
    while (it != v.end())
    {
        *it *= 2;
        ++it;
    }
    
    // 使用反向迭代器进行遍历再打印
    vector<int>::reverse_iterator rit = v.rbegin();
    while (rit != v.rend())
    {
        cout << *rit << " ";
        ++rit;
    }
    cout << endl;
    
    PrintVector(v);
    return 0;
}

vector 空间增长问题

容量空间 接口说明
size 获取数据个数
capacity 获取容量大小
empty 判断是否为空
resize(重点) 改变vector的size
reserve (重点) 改变vector放入capacity
  • capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。
    这个问题经常会考察,不要固化的认为,顺序表增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。
  • reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问
    题。
  • resize在开空间的同时还会进行初始化,影响size

capacity

// vector::capacity
#include 
#include 
int main ()
{
    size_t sz;
    std::vector<int> foo;
    sz = foo.capacity();
    std::cout << "making foo grow:\n";
    
    for (int i=0; i<100; ++i) 
    {
        foo.push_back(i);
    	if (sz!=foo.capacity()) 
        {
        sz = foo.capacity();
        std::cout << "capacity changed: " << sz << '\n';
    	}
    }
}

vs:运行结果:
making foo grow:
capacity changed: 1
capacity changed: 2
capacity changed: 3
capacity changed: 4
capacity changed: 6
capacity changed: 9
capacity changed: 13
capacity changed: 19
capacity changed: 28
capacity changed: 42
capacity changed: 63
capacity changed: 94
capacity changed: 141
    
g++运行结果:
making foo grow:
capacity changed: 1
capacity changed: 2
capacity changed: 4
capacity changed: 8
capacity changed: 16
capacity changed: 32
capacity changed: 64
capacity changed: 128
    

reserve

// vector::reserve
#include 
#include 

int main ()
{
    size_t sz;
    std::vector<int> foo;
    sz = foo.capacity();
    std::cout << "making foo grow:\n";
    
    for (int i=0; i<100; ++i) 
    {
        foo.push_back(i);
        if (sz!=foo.capacity()) 
        {
            sz = foo.capacity();
            std::cout << "capacity changed: " << sz << '\n';
        }
        
    }
    
    std::vector<int> bar;
    sz = bar.capacity();
    
    bar.reserve(100); // this is the only difference with foo above
    std::cout << "making bar grow:\n";
    
    for (int i=0; i<100; ++i) 
    {
        bar.push_back(i);
        if (sz!=bar.capacity()) 
        {
            sz = bar.capacity();
            std::cout << "capacity changed: " << sz << '\n';
        }
        
    }
    
    return 0;
}

resize

// vector::resize
#include 
#include 

int main ()
{
    std::vector<int> myvector;
    
    // set some initial content:
    for (int i=1;i<10;i++)
    myvector.push_back(i);
    
    myvector.resize(5);
    myvector.resize(8,100);
    myvector.resize(12);
    
    std::cout << "myvector contains:";
    
    for (int i=0;i<myvector.size();i++)
    std::cout << ' ' << myvector[i];
    
    std::cout << '\n';
    return 0;
}

vector 增删查改

vector增删查改 接口说明
push_back 尾插
pop_back 尾删
find 查找。(注意这个是算法模块实现,不是vector的成员接口)
insert 在position之前插入val
erase 删除position位置的数据
swap 交换两个vector的数据空间
operator[] 像数组一样访问

push_back和pop_back的使用

// push_back/pop_back
#include 
#include 
using namespace std;

int main()
{
        int a[] = { 1, 2, 3, 4 };
        vector<int> v(a, a+sizeof(a)/sizeof(int));
        vector<int>::iterator it = v.begin();
        
    	//用迭代器遍历vector
        while (it != v.end()) 
        {
        	cout << *it << " ";
        	++it;
        }
        
        cout << endl;
    
    	//尾删
        v.pop_back();
        v.pop_back();
    
    	//尾插
    	v.push_back(5);
        
        it = v.begin();
        while (it != v.end()) 
        {
            cout << *it << " ";
            ++it;
        }
        
        cout << endl;
        return 0;
}

结果:

C++vector_第2张图片

find、insert、erase的使用

//insert,用迭代器插入
iterator insert (iterator position, const value_type& val);
//erase,用迭代器删除
iterator erase (iterator position);
//find
template <class InputIterator, class T>
    //用迭代器遍历查找,返回的是所找元素的迭代器位置
   InputIterator find (InputIterator first, InputIterator last, const T& val);
// find / insert / erase
#include 
#include //find的头文件,在算法头文件里
#include 
using namespace std;

int main()
{
    int a[] = { 1, 2, 3, 4 };
    vector<int> v(a, a + sizeof(a) / sizeof(int));
    
    // 使用find查找3所在位置的iterator
    vector<int>::iterator pos = find(v.begin(), v.end(), 3);
    
    // 在pos位置之前插入30
    v.insert(pos, 30);
    
    //用迭代器遍历vector
    vector<int>::iterator it = v.begin();
    while (it != v.end()) 
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
    
    //查找3的iterator
    pos = find(v.begin(), v.end(), 3);
    
    // 删除pos位置的数据
    v.erase(pos);
    
    //再次遍历
    it = v.begin();
    while (it != v.end()) 
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
    
    return 0;
}

结果:

C++vector_第3张图片

operator[]、swap的使用

// operator[]+index 和 C++11中vector的新式for+auto的遍历
// vector使用这两种遍历方式是比较便捷的。
#include 
#include 
using namespace std;

int main()
{
    int a[] = { 1, 2, 3, 4 };
    vector<int> v(a, a + sizeof(a) / sizeof(int));
    
    // 通过[]读写第0个位置。
    v[0] = 10;
    cout << v[0] << endl;
    
    // 通过[i]的方式遍历vector
    for (size_t i = 0; i < v.size(); ++i)
    cout << v[i] << " ";
        
    cout << endl;
    
    vector<int> swapv;
    //v和swapv交换
    swapv.swap(v);
    cout << "v data:";//v的内容
    for (size_t i = 0; i < v.size(); ++i)
    cout << v[i] << " ";
    
    cout << endl;
    
    cout << "swapv data:";//swapv的内容
    for (size_t i = 0; i < swapv.size(); ++i)
    cout << swapv[i] << " ";
    
    cout << endl;
    
    // C++11支持的新式范围for遍历
    for(auto x : v)
    cout<< x << " ";
    cout<<endl;
    return 0;
}

结果:

C++vector_第4张图片

C++98下,最好使用vector的swap,全局的swap会涉及深浅拷贝,开销较大

C++11则都一样

vector 迭代器失效问题

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T*。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器(程序可能会崩溃)

对于vector可能会导致其迭代器失效的操作有:

  1. 会引起其底层空间改变的操作,都有可能是迭代器失效,导致迭代器变成野指针。比如:resize、reserve、insert、assign、push_back等

    #include 
    using namespace std;
    #include 
    
    int main()
    {
        vector<int> v{1,2,3,4,5,6};
        auto it = v.begin();
        
        // 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容
        // v.resize(100, 8);
        
        // reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容量改变
        // v.reserve(100);
        
        // 插入元素期间,可能会引起扩容,而导致原空间被释放
        // v.insert(v.begin(), 0);
            // v.push_back(8);
        
        // 给vector重新赋值,可能会引起底层容量改变
        v.assign(100, 8);
        
        /*
        出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,
        而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的
        空间,而引起代码运行时崩溃。
        解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新
        赋值即可。
        */
        
        while(it != v.end())
        {
            cout<< *it << " " ;
            ++it;
        }
        cout<<endl;
        return 0;
    }
    
  2. 指定位置元素的删除操作–erase

    #include 
    using namespace std;
    #include 
    int main()
    {
        int a[] = { 1, 2, 3, 4 };
        vector<int> v(a, a + sizeof(a) / sizeof(int));
        
        // 使用find查找3所在位置的iterator
        vector<int>::iterator pos = find(v.begin(), v.end(), 3);
        
        // 删除pos位置的数据,导致pos迭代器失效。
        v.erase(pos);
        
        cout << *pos << endl; // 此处会导致非法访问
        return 0;
    }
    

    erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。

    以下代码的功能是删除vector中所有的偶数

    #include 
    using namespace std;
    #include 
    
    
    int main()
    {
        vector<int> v{ 1, 2, 3, 4 };
        auto it = v.begin();
        while (it != v.end())
        {
            if (*it % 2 == 0)
            v.erase(it);
            ++it;
        }
        return 0;
    }
    

    看一下结果:

    vs2019下:

    C++vector_第5张图片

    g++(Linux)下:

    image-20211022152529900

    可以看到两个编译器都对其报了错误,而且是与内存访问相关的错误,为什么呢?

    这里实际上就是一个迭代器失效的案例

    解释:

    vector中有元素{1,2,3,4},1不是偶数,所以不会删除,迭代器it向后走,指向2,2是偶数,所以删除2,此时迭代器还是向后走,我们的原意是想让it指向3,可是vector已经变成了{1,3,4},it指向的是第三个位置,所以指向了4,再删除4,vector变成了{1,3},it向后走,此时it已经不指向vector中的内容了,而v.end()指向的是3,所以it永远不会等于v.end(),也就是it会一直向后非法访问,直到访问到没有权限的位置,程序就异常结束了。

    而vs是在删除2后,++it时出错的,++it会出错是因为VS编译器会在++的时候对it进行检查,如果有非法访问的行为则直接抛异常

    我们在vecotr中加一个数字5,结果就不一样了:

    vs下:

    C++vector_第6张图片

    g++下:

    image-20211022153419990

    可以看到g++下的编译通过了,vs还是抛出了异常,因为这是一种巧合的情况,刚好不会发生非法的内存访问,当删除4之后,it++指向了5,5不是偶数,于是不会被删除,it再++,it就到了v.end()的位置,结束循环。

    而vs中仍然在同样的地方抛出了异常,是因为这种行为是不被允许的,可能会造成错误,于是vs就直接终止了程序。

    那么怎么解决这种问题呢?

    erase会返回删除位置的下一个位置的迭代器,我们用it接收这个值,更新it的位置,指向的刚好就是下一个元素的位置,就不会发生非法的访问了

    //代码int main()
    {
        vector<int> v{ 1, 2, 3, 4 };
        auto it = v.begin();
        while (it != v.end())
        {
            if (*it % 2 == 0)
            it = v.erase(it);
            else
            ++it;
        }
        
        return 0;
    }
    

总结:

迭代器失效解决办法:在使用前,对迭代器重新赋值即可

vector 在OJ中的使用

只出现一次的数字 - 力扣(LeetCode) (leetcode-cn.com)

杨辉三角 - 力扣(LeetCode) (leetcode-cn.com)

删除有序数组中的重复项 - 力扣(LeetCode) (leetcode-cn.com)

只出现一次的数字 II - 力扣(LeetCode) (leetcode-cn.com)

只出现一次的数字 III - 力扣(LeetCode) (leetcode-cn.com)

数组中出现次数超过一半的数字

电话号码的字母组合 - 力扣(LeetCode) (leetcode-cn.com)

连续子数组的最大和

vector深度剖析及模拟实现

C++vector_第7张图片

start、finish和end_of_storage是vector的三个核心成员变量,这三个成员变量的意义如上图所示。他们的类型是迭代器

template<class T>
typedef T value_type;
typedef value_type* iterator;
private:
iterator _start;
iterator _finish;
iterator _end_of_storage;

模拟实现vector

#include
#include
#include
#include

namespace ysj
{
	template <class T>
	class vector {
	public:

		typedef T value_type;
		typedef  value_type* iterator;
		typedef const value_type* const_iterator;

		//构造
		vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _endof_storage(nullptr)
		{}

		vector(int n, const T& val = T())
			:_start(nullptr)
			, _finish(nullptr)
			, _endof_storage(nullptr)
		{
			reserve(n);

			while (n--)
			push_back(val);
		}

		//迭代器构造
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
			:_start(nullptr)
			,_finish(nullptr)
			,_endof_storage(nullptr)
		{
			reserve(last - first);

			while (first != last)
			{
				push_back(*first);
				++first;
			}


		}

		//拷贝构造
		vector(const vector& v)
			:_start(nullptr)
			,_finish(nullptr)
			,_endof_storage(nullptr)
		{
			reserve(v.capacity());

			iterator it = v. begin();
			while (it != v.end())
			{
				push_back(*it);
				++it;
			}

		}

		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endof_storage, v._endof_storage);
		}

		//赋值重载

		//传统写法
		/*const vector& operator=(const vector& v)
		{
			delete[] _start;

			_start = new T[v.capacity()];
			memcpy(_start, v._start, v.size()*sizeof(T));
			_finish = _start + v.size();
			_endof_storage = _start + v.capacity();

			return *this;
		}*/
		
		//现代写法
		const vector<T>& operator=(vector<T> v)
		{
			swap(v);
			return *this;
		}

		size_t size()const
		{
			return _finish - _start;
		}

		size_t capacity()const
		{
			return _endof_storage - _start;
		}

		bool empty()
		{
			return _start == _finish;
		}

		//增容
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start)//当前vector不为空
				{
					for (size_t i = 0; i < size(); ++i)
					{
						tmp[i] = _start[i];
					}

					//不能用memcpy拷贝,memcpy是字节拷贝,属于浅拷贝,如果vector装的是string这样需要深拷贝的成员,就会生成野指针,也就是string的_str指向了一块被释放的空间(下面的_start被释放了)
					/*memcpy(tmp, _start, sz * sizeof(T));*/
					delete[] _start;
				}

				_start = tmp;
				_finish = _start + sz;//用提前保存的sz而不是size接口,如果原vector是空的话,size()就是一个很大的值,就会造成非法访问
				_endof_storage = _start + n;
			}
		}

		void resize(size_t n, T val = T())
		{
			//判断增容
			if (_finish == _endof_storage)
			{
				int newcapacity = 0 ? 4 : 2 * capacity();
				resize(newcapacity);
			}

			//n
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				//小于capacity
				if (n < capacity())
				{
					while (n >= size())
					{
						*_finish = val;
						_finish++;
					}
				}
				else//大于capacity
				{
					//增容
					reserve(n);
					while (n >= size())
					{
						*_finish = val;
						_finish++;

					}
				}
			}
		}


		//尾插
		void push_back(const T& x)
		{
			//判断增容
			while (_finish == _endof_storage)
			{
				reserve(capacity() == 0 ? 4 : 2 * capacity());
			}

			*_finish = x;
			++_finish;
		}

		//插入
		iterator insert(iterator pos, T val)
		{
			//判断增容
			if (_finish == _endof_storage)
			{
				//增容会有迭代器失效的隐患,所以要记录pos的相对位置
				int len = pos - _start;
				int newcapacity = capacity() == 0 ? 4 : 2 * capacity();
				reserve(newcapacity);
				
				pos = _start + len;
			}

			iterator it = _finish;
			
			while (it > pos)
			{
				*it = *(it - 1);
				--it;
			}

			*pos = val;
			_finish++;

			return pos + 1;

		}


		iterator erase(iterator pos)
		{
			assert(!empty());
			iterator it = pos;
			while (it < _finish - 1)
			{
				*it = *(it + 1);
				++it;
			}

			_finish--;

			return pos;
		}

		void pop_back()
		{
			assert(!empty());
			--_finish;
		}

		//迭代器
		iterator  begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		//const迭代器
		iterator  begin()const
		{
			return _start;
		}

		iterator end()const
		{
			return _finish;
		}


		//operator[]
		T& operator[](size_t pos)
		{
			assert(pos < size());
			return *(_start + pos);
		}

		void print()const
		{
			ysj::vector<T>::const_iterator it =  begin();

			while (it != end())
			{
				std::cout << *it << " ";
				++it;
			}

			std::cout << std::endl;
		}

		~vector()
		{
			delete[] _start;
			_start = _finish = _endof_storage = nullptr;
		}

	private:
		iterator _start;
		iterator _finish;
		iterator _endof_storage;
	};

	
}

使用memcpy拷贝问题

假设模拟实现的vector中的reserve接口中,使用memcpy进行的拷贝,以下代码会发生什么问题?

int main()
{
    ysj::vector<string> v;
    v.push_back("1111");
    v.push_back("2222");
    v.push_back("3333");
    return 0;
}

问题分析:

  1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中
  2. 如果拷贝的是自定义类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且
    自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。
C++vector_第8张图片 C++vector_第9张图片

C++vector_第10张图片

C++vector_第11张图片

结论:如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是
浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。

对vector核心接口的测试

using namespace std;

// constructing vectors
void TestVector1()
{
    // constructors used in the same order as described above:
    ysj::vector<int> first; // empty vector of ints
    ysj::vector<int> second(4, 100); // four ints with value 100

    ysj::vector<int> third(second. begin(), second.end()); // iterating through second

    ysj::vector<int> fourth(third); // a copy of third

    // the iterator constructor can also be used to construct from arrays:
    int myints[] = { 16, 2, 77, 29 };
    ysj::vector<int> fifth(myints, myints + sizeof(myints) / sizeof(int));
    std::cout << "The contents of fifth are:";
    for (ysj::vector<int>::iterator it = fifth. begin(); it != fifth.end(); ++it)
        std::cout << *it << " ";

    std::cout << endl;

    // 测试T是string时,拷贝问题
    ysj::vector<string> strV;
    strV.push_back("1111");
    strV.push_back("2222");
    strV.push_back("3333");
    strV.push_back("4444");
    for (size_t i = 0; i < strV.size(); ++i)
    {
        cout << strV[i] << " ";
    }

    cout << endl;
}

//vector iterator的使用
void PrintVector(const ysj::vector<int>& v)
{
    // 使用const迭代器进行遍历打印
    ysj::vector<int>::const_iterator it = v. begin();

    while (it != v.end())
    {
        cout << *it << " ";
        ++it;
    }

    cout << endl;
}

void TestVector2()
{
    // 使用push_back插入4个数据
    ysj::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    PrintVector(v);
    // 使用迭代器进行修改
    auto it = v. begin();
    while (it != v.end())
    {
        *it *= 2;
        ++it;
    }
    PrintVector(v);

    // 这里可以看出C++11支持iterator及接口,就支持范围for
    for (auto e : v)
        cout << e << " ";
}

// find / insert / erase
void TestVector3()
{
    int a[] = { 1, 2, 3, 4 };
    ysj::vector<int> v(a, a + sizeof(a) / sizeof(a[0]));

    // 使用find查找3所在位置的iterator
    auto pos = find(v. begin(), v.end(), 3);

    // 在pos位置之前插入30
    v.insert(pos, 30);
    PrintVector(v);

    // 删除pos位置的数据
    pos = find(v. begin(), v.end(), 3);
    v.erase(pos);
    PrintVector(v);
}

// iterator失效问题
void TestVector4()
{
    int a[] = { 1, 2, 3, 4 };
    ysj::vector<int> v(a, a + sizeof(a) / sizeof(a[0]));

    // 删除pos位置的数据,导致pos迭代器失效
    auto pos = find(v. begin(), v.end(), 3);
    v.erase(pos);
    cout << *pos << endl; // 此处会导致非法访问
    // 在pos位置插入数据,导致pos迭代器失效。
    // insert会导致迭代器失效,是因为insert可
    // 能会导致增容,增容后pos还指向原来的空间,而原来的空间已经释放了。
    pos = find(v. begin(), v.end(), 3);
    v.insert(pos, 30);
    cout << *pos << endl; // 此处会导致非法访问
    // 实现删除v中的所有偶数
    // 下面的程序会崩溃掉,如果是偶数,erase导致it失效
        // 对失效的迭代器进行++it,会导致程序崩溃

    auto it = v. begin();
    /*while (it != v.end())
    {
        if (*it % 2 == 0)
            v.erase(it);
        ++it;
    }*/
    // 以上程序要改成下面这样,erase会返回删除位置的下一个位置

    cout << "删除前:";
    PrintVector(v);

    it = v. begin();
    while (it != v.end())
    {
        if (*it % 2 == 0)
            it = v.erase(it);
        else
            ++it;
    }

    cout << "删除后:";
    PrintVector(v);
}

int main()
{
    //TestVector1();
    //TestVector2();
    //TestVector3();
    TestVector4();

    return 0;
}

你可能感兴趣的:(C++,数据结构,c++,容器,开发语言)