首页 > 编程语言 >C++—vector的介绍及使用 && vector的模拟实现

C++—vector的介绍及使用 && vector的模拟实现

时间:2024-03-31 13:04:03浏览次数:31  
标签:back C++ v1 v2 vector && push size

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

文章目录

前言

一、vector的介绍及使用

1.1 vector的介绍

1.2 vector的使用

1.2.1 vector的定义

1.2.2 vector iterator 的使用

1.2.3 vector 空间增长问题

1.2.4 vector 增删查改

1.2.5 vector 迭代器失效问题。(重点)

1.2.6 vector 在OJ中的使用。

二、vector的模拟实现

总结



前言

世上有两种耀眼的光芒,一种是正在升起的太阳,一种是正在努力学习编程的你!一个爱学编程的人。各位看官,我衷心的希望这篇博客能对你们有所帮助,同时也希望各位看官能对我的文章给与点评,希望我们能够携手共同促进进步,在编程的道路上越走越远!


提示:以下是本篇文章正文内容,下面案例可供参考

一、vector的介绍及使用

1.1 vector的介绍

vector的文档介绍

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

1.2 vector的使用

vector学习时一定要学会查看文档:vector的文档介绍,vector在实际中非常的重要,在实际中我们熟悉常 见的接口就可以,下面列出了哪些接口是要重点掌握的。

1.2.1 vector的定义

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

vector构造函数的演示

1.2.2 vector iterator 的使用

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

vector的迭代器使用代码的演示

1.2.3 vector 空间增长问题

容量空间接口说明
size获取数据个数
capacity获取容量大小
empty判断是否为空
resize(重点)改变vector的size
reserve(重点)改变vector的capacity
  • capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。 这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。
  • reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
  • resize在开空间的同时还会进行初始化,影响size。
// 测试vector的默认扩容机制
void TestVectorExpand()
 {
 size_t sz;
 vector<int> v;
 sz = v.capacity();
 cout << "making v grow:\n";
 for (int i = 0; i < 100; ++i) 
{
 v.push_back(i);
 if (sz != v.capacity()) 
{
 sz = v.capacity();
 cout << "capacity changed: " << sz << '\n';
 }
 }
 }
 vs:运行结果:vs下使用的STL基本是按照1.5倍方式扩容
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++运行结果:linux下使用的STL基本是按照2倍方式扩容
 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
// 如果已经确定vector中要存储元素大概个数,可以提前将空间设置足够
// 就可以避免边插入边扩容导致效率低下的问题了
void TestVectorExpandOP()
{
	vector<int> v;
	size_t sz = v.capacity();
	v.reserve(100);
	// 提前将容量设置好,可以避免一遍插入一遍扩容
	cout << "making bar grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}

vector容量接口使用代码演示

1.2.4 vector 增删查改

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

vector插入和删除代码的演示

1.2.5 vector 迭代器失效问题。(重点)

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

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

  • 1.会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、 push_back等。
#include <iostream>
using namespace std;
#include <vector>

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 <iostream>
using namespace std;
#include <vector>

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 <iostream>
using namespace std;
#include <vector>

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;
}

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;
}
  • 3. 注意:Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。
// 1. 扩容之后,迭代器已经失效了,程序虽然可以运行,但是运行结果已经不对了
int main()
{
	vector<int> v{ 1,2,3,4,5 };
	for (size_t i = 0; i < v.size(); ++i)
		cout << v[i] << " ";
	cout << endl;
	auto it = v.begin();
	cout << "扩容之前,vector的容量为: " << v.capacity() << endl;
	// 通过reserve将底层空间设置为100,目的是为了让vector的迭代器失效    
	v.reserve(100);
	cout << "扩容之后,vector的容量为: " << v.capacity() << endl;
	// 经过上述reserve之后,it迭代器肯定会失效,在vs下程序就直接崩溃了,但是linux下不会
   // 虽然可能运行,但是输出的结果是不对的
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}
//程序输出:
//1 2 3 4 5
//扩容之前,vector的容量为: 5
//扩容之后,vector的容量为 : 100
//0 2 3 4 5 409 1 2 3 4 5

// 2. erase删除任意位置代码后,linux下迭代器并没有失效
// 因为空间还是原来的空间,后序元素往前搬移了,it的位置还是有效的
#include <vector>
#include <algorithm>
int main()
{
	vector<int> v{ 1,2,3,4,5 };
	vector<int>::iterator it = find(v.begin(), v.end(), 3);
	v.erase(it);
	cout << *it << endl;
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}
//程序可以正常运行,并打印:
//4
//4 5

// 3: erase删除的迭代器如果是最后一个元素,删除之后it已经超过end
// 此时迭代器是无效的,++it导致程序崩溃
int main()
{
	vector<int> v{ 1,2,3,4,5 };
	// vector<int> v{1,2,3,4,5,6};
	auto it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
			v.erase(it);
		++it;
	}
	for (auto e : v)
		cout << e << " ";
	cout << endl;
	return 0;
}
========================================================
// 使用第一组数据时,程序可以运行
[sly@VM - 0 - 3 - centos 20220114]$ g++ testVector.cpp - std = c++11
[sly@VM - 0 - 3 - centos 20220114]$ . / a.out
1 3 5
======================================================== =
// 使用第二组数据时,程序最终会崩溃
[sly@VM - 0 - 3 - centos 20220114]$ vim testVector.cpp
[sly@VM - 0 - 3 - centos 20220114]$ g++ testVector.cpp - std = c++11
[sly@VM - 0 - 3 - centos 20220114]$ . / a.out
Segmentation fault

从上述三个例子中可以看到:SGI STL中,迭代器失效后,代码并不一定会崩溃,但是运行结果肯定不对,如果it不在begin和end范围内,肯定会崩溃的。

  • 4. 与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效
#include <string>
void TestString()
{
	string s("hello");
	auto it = s.begin();
	// 放开之后代码会崩溃,因为resize到20会string会进行扩容
	// 扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了
	// 后序打印时,再访问it指向的空间程序就会崩溃
	//s.resize(20, '!');
	while (it != s.end())
	{
		cout << *it;
		++it;
	}
	cout << endl;
	it = s.begin();
	while (it != s.end())
	{
		it = s.erase(it);
		// 按照下面方式写,运行时程序会崩溃,因为erase(it)之后
	   // it位置的迭代器就失效了
	   // s.erase(it);  
		++it;
	}
}

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

1.2.6 vector 在OJ中的使用。

1.只出现一次的数字i

class Solution {
public:
	int singleNumber(vector<int>& nums) {
		int value = 0;
		for (auto e : v)
		{ 
			value ^= e; 
		}
		return value;
	}
};

2.杨辉三角

// 涉及resize / operator[]
// 核心思想:找出杨辉三角的规律,发现每一行头尾都是1,中间第[j]个数等于上一行[j-1]+[j]
class Solution {
public:
	vector<vector<int>> generate(int numRows) {
		vector<vector<int>> vv(numRows, vector<int>);
		//vv.resize(numRows, vector<int>);//vector<int>是类型T,可以忽略不写
		for (size_t i = 0; i < vv.size(); ++i)
		{
			vv[i].resize(i + 1, 0);//开空间,并将空间都初始化为0
			vv.[i][0] = vv.[i][vv[i].size() - 1] = 1;

		}
		for (size_t i = 0; i < vv.size(); ++i)
		{
			for (size_t j = 1; j < vv[i].size(); ++j)
			{
				if (vv[i][j] == 0)
				{
					vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
				}
			}
		}
		return vv;
	}
};

二、vector的模拟实现

vector.h
#pragma once
#include<assert.h>
namespace bit
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;

		iterator begin()
		{
			// 传值返回,生成的是返回对象的拷贝,拷贝的变量是临时变量
			return _start;//下标为0的位置
		}

		iterator end()
		{
			return _finish;//最后一个数据的下一个位置
		}

		const_iterator begin() const
		{
			return _start;
		}

		const_iterator end() const
		{
			return _finish;
		}

		vector()
		{}

		// v2(v1)
		vector(const vector<T>& v)
		{
			reserve(v.capacity());
			for (auto& e : v)
			{
				push_back(e);
			}
		}

		// initializer_list方法的构造函数(构造+拷贝构造—>优化,直接构造)
		// vector<int> v1 = { 1,2,3,4,5,6,7,8,9,10 };
		vector(initializer_list<T> il)
		{
			// initializer_list的对象里面有一个size()的成员函数(两个指针相减)
			reserve(il.size());
			// initializer_list有迭代器,是原生指针
			for (auto& e : il)
			{
				push_back(e);
			}
		}

		// 迭代器区间构造
		// 类模板的成员函数可以是函数模板:好处是:可以是其它容器的迭代器
		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		// size_type 就是 size_t;value_type 就是 T
		// 第二个参数,不能给0,因为T不一定是int,所以要用一个T()类型的匿名对象;
		// 比如:string,就用string构造一个匿名对象;
		// 如果T是内置类型:内置类型为了兼容,有了模板之后,会进行升级(int就是0,double就是0.0,指针就是空指针)
		vector(size_t n, const T& val = T())
		{
			// 构造函数:n个val进行初始化
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}
		// 这个构造函数是上面构造函数的重载版本
		vector(int n, const T& val = T())
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		void swap(vector<T>& v)
		{
			// 我们期望的是调用库里面类模板的swap()函数,但是由于就近原则,它会现在局部去找,再从外部去找,
			// 局部找,就找到了这个函数,但是它的参数不匹配,所以要加std::
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endofstorage, v._endofstorage);
		}

		// v1 = v3 所有深拷贝的类都可以用现代写法 v是v3的拷贝,把V3的数据拷贝到V中
		vector<T>& operator=(vector<T> v)
		{
			// V1想要V的数据
			swap(v);
			return *this;
		}


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

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

		T& operator[](size_t pos)
		{
			assert(pos < size());

			return _start[pos];
		}


		const T& operator[](size_t pos) const
		{
			assert(pos < size());

			return _start[pos];
		}

		size_t capacity() const
		{
			return _endofstorage - _start;//空间的大小
		}

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				size_t old_size = size();//提前保存一下size的数据个数
				//memcpy(tmp, _start, size() * sizeof(T));
				for (size_t i = 0; i < old_size; i++)
				{
					tmp[i] = _start[i];// 赋值是深拷贝
				}
				delete[] _start;

				_start = tmp;
				_finish = tmp + old_size;
				_endofstorage = tmp + n;
			}
		}

		//resize的缺省值不能是0,因为T可能是int/double/char/string/vector等类型
		//所以给缺省值为无参的匿名对象,T()去调用T类型的构造函数
		void resize(size_t n, const T& val = T())
		{
			if (n > size())
			{
				reserve(n);
				// 插入
				while (_finish < _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
			else
			{
				// 删除
				_finish = _start + n;
			}
		}

		void push_back(const T& val)
		{
			/*if (_finish == _endofstorage)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}

			*_finish = val;
			++_finish;*/

			insert(end(), val);
		}

		void pop_back()
		{
			/*assert(!empty());

			--_finish;*/

			erase(--end());
		}

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

		void insert(iterator pos, const T& val)
		{
			assert(pos >= _start);
			assert(pos <= _finish);

			if (_finish == _endofstorage)
			{
				size_t len = pos - _start;//pos - 扩容前的_start;求pos相对于头部的相对位置
				reserve(capacity() == 0 ? 4 : capacity() * 2);

				// 如果扩容了要更新pos
				pos = _start + len;//_start:是扩容后的下标为0的地址
			}

			iterator it = _finish - 1;
			while (it >= pos)//it是指针,最小的指针是空指针,指针是一个一个字节内存单元的编号
			{
				*(it + 1) = *it;
				--it;
			}
			*pos = val;
			++_finish;
		}

		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);

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

			--_finish;

			return pos;
		}


	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _endofstorage = nullptr;
	};

	//template<typename T>
	// 函数模板
	template<class T>
	void print_vector(const vector<T>& v)
	{
		for (size_t i = 0; i < v.size(); i++)
		{
			cout << v[i] << " ";
		}
		cout << endl;

		// typename告诉编译器 vector<T>::const_iterator是一个类型
		//typename vector<T>::const_iterator it = v.begin();
		/*auto it = v.begin();
		while (it != v.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;*/
	}

	void test_vector1()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.push_back(4);
		v1.push_back(4);
		print_vector(v1);

		vector<double> v2;
		v2.push_back(1.1);
		v2.push_back(2.2);
		v2.push_back(3.1);
		print_vector(v2);

		v2.insert(v2.begin(), 11.11);
		print_vector(v2);

		v2.insert(v2.begin(), 11.11);
		print_vector(v2);

		v2.insert(v2.begin(), 11.11);
		print_vector(v2);

		v2.insert(v2.begin(), 11.11);
		print_vector(v2);

		v2.insert(v2.begin(), 11.11);
		print_vector(v2);

		v2.erase(v2.begin());
		print_vector(v2);

		v2.erase(v2.begin() + 4);
		print_vector(v2);

		/*for (size_t i = 0; i < v.size(); i++)
		{
			cout << v[i] << " ";
		}
		cout << endl;

		vector<int>::iterator it = v.begin();
		while (it != v.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;*/
	}

	void test_vector2()
	{
		//内置类型的默认构造函数,初始化:int = 0,double = 0.0,char = \0等
		int i = 1;
		int j = int();
		int k = int(2);

		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.push_back(4);
		v1.push_back(4);
		print_vector(v1);

		v1.resize(10);
		print_vector(v1);

		v1.resize(3);
		print_vector(v1);
	}

	void test_vector3()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.push_back(4);
		v1.push_back(4);
		print_vector(v1);

		vector<int> v2(v1);
		print_vector(v2);

		vector<int> v3;
		v3.push_back(10);
		v3.push_back(20);
		v3.push_back(30);

		v1 = v3;
		print_vector(v1);
		print_vector(v3);
	}

	void test_vector4()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		print_vector(v1);

		// 迭代器区间构造函数(用来初始化顺序表)
		// 这个函数的参数千万不能用++或--,因为begin()和end()返回的是值是放入临时对象中的,不能改变
		vector<int> v2(v1.begin() + 1, v1.end() - 1);
		print_vector(v2);

		string str("abcd");
		vector<int> v3(str.begin(), str.end());
		print_vector(v3);
	}

	void test_vector5()
	{
		vector<int> v1(10, 1);// 初始化10个1
		print_vector(v1);

		vector<int> v2(10u, 1);
		print_vector(v2);

		vector<char> v3(10, 'a');
		print_vector(v3);
	}

	void test_vector6()
	{
		auto x = { 1,2,3,4,5,6,7,8,9,10 };
		cout << typeid(x).name() << endl;
		cout << sizeof(x) << endl;

		initializer_list<int> y = { 1,2,3,4,5,6,7 };// 将{}中的数值给initializer_list<int>类型的对象y


		// 常量字符串为什么能初始化string?单参数的构造函数,隐式类型转换
		// "11111"构造一个临时对象,再将这个临时对象进行拷贝构造。
		string str = "11111"; // 构造 + 拷贝构造 -> 优化 直接构造
		// (加个&引用,就不会优化了:加&的话,只有构造,没有拷贝构造;
		// 但是这里编译不过去:类型转换会产生临时变量,引用的是常量字符串构造的临时对象,临时对象具有常性,所以要加const)
		const string& str1 = "11111"; // 构造临时对象,引用的是临时对象
		vector<string> v;
		v.push_back(str);// 有名对象
		v.push_back(string("22222"));// 匿名对象
		v.push_back("33333");// 可以不用push_back匿名对象,直接使用常量字符串
		// 为什么可以直接push_back常量字符串?看图


		int i = 1;
		// 不推荐 -- C++11
		//int j = { 1 };
		int k{ 1 };

		// 跟上面类似
		// 隐式类型转换+优化
		//vector<int> v1 = { 1,2,3,4,5,6,7,8,9,10 };// C++11的用法 用initializer_list初始化vector,也是隐式类型的转换
		vector<int> v1{ 1,2,3,4,5,6,7,8,9,10 };//  C++11里面可以把赋值符号去掉,但是不建议这样使用
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		// 直接构造  它和上面的隐式类型转换方法的结果都是一样的
		vector<int> v2({ 10,20,30,40 });
		for (auto e : v2)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector7()
	{
		vector<string> v;
		v.push_back("11111");
		v.push_back("22222");
		v.push_back("33333");
		v.push_back("44444");
		v.push_back("55555");

		for (auto& e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector8()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.push_back(5);
		v1.push_back(6);
		v1.push_back(7);
		v1.push_back(8);
		print_vector(v1);

		// insert以后,it就失效了,不要使用了
		vector<int>::iterator it = v1.begin() + 3;
		v1.insert(it, 40);

		print_vector(v1);
		// 如果还是想访问it的位置,那就重新更新一下it的位置
		it = v1.begin() + 3;
		cout << *it << endl;
	}
	void test_vector9()
	{
		//std::vector<int> v1;
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.push_back(4);
		v1.push_back(5);
		//v1.push_back(4);

		// 删除偶数 -- 迭代器失效以后,不要直接使用,如果要使用按规则重新更新后使用
		//std::vector<int>::iterator it = v1.begin();
		vector<int>::iterator it = v1.begin();

		//cout << typeid(it).name() << endl;
		while (it != v1.end())
		{
			if (*it % 2 == 0)
			{
				it = v1.erase(it);
				// erase()函数会返回删除元素的下一个位置的迭代器,哪怕erase()函数会缩容,也会返回缩容后的空间的位置
			}
			else
			{
				++it;
			}
		}

		//print_vector(v1);
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;
	}
}
vector.cc
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<algorithm>//算法头文件
using namespace std;

#include<vector>//(int/double)顺序表

void test_vector1()
{
	/*vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);*/

	vector<int> v(10, 1);//开10个空间,初始化为1
	//遍历:下标 + []
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
	//遍历:迭代器
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	//遍历:范围for
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

void test_vector2()
{
	size_t sz;
	vector<int> v;
	v.reserve(100);
	sz = v.capacity();
	cout << "making v grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}

	cout << v.size() << endl;
	cout << v.capacity() << endl;
	v.reserve(10);//reserve不会缩容
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	cout << "--------------" << endl;

	cout << v.size() << endl;
	cout << v.capacity() << endl;
	v.resize(10);//只保留10个size,resize不会缩容
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.shrink_to_fit();//缩容
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	vector<int> a;
	a.resize(10, 1);//开10个空间,初始化为1
	for (auto e : a)
	{
		cout << e << " ";
	}
	cout << endl;
}

void test_vector3()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	//只要是迭代区间,都是左闭右开
	//vector<int>::iterator pos = find(v.begin(), v.end(), 3);
	auto pos = find(v.begin(), v.end(), 3);
	if (pos != v.end())//找不到,返回的是 v.end()
	{
		v.insert(pos, 30);
	}

	// 头插
	v.insert(v.begin(), 0);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	//在下标为2的位置插入0
	v.insert(v.begin() + 2, 0);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	// 迭代区间不一定是自己的迭代器,vector下的iterator必须是vector的,模板的迭代器就不一定是vector的
	// 迭代器区间可以是属于自己的迭代器,也可以是属于其他容器的迭代器
	// 模拟一下不是vector下的迭代器的迭代器区间
	string s("abcd");
	v.insert(v.begin(), s.begin(), s.end());
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

void test_vector()
{
	// 对象数组,数组中的每一个对象是 string
	vector<string> v;
	string s1("苹果");
	v.push_back(s1);//有名对象
	v.push_back(string("香蕉"));//匿名对象
	v.push_back("草莓");//隐式类型转换

	vector<vector<int>> vv;
}

#include"vector.h"

int main()
{
	//test_vector1();
	test_vector3();
	return 0;
}

对于迭代器而言:

  • vector——insert/erase都会失效;
  • list——insert不会失效,erase会失效。

总结

好了,本篇博客到这里就结束了,如果有更好的观点,请及时留言,我会认真观看并学习。
不积硅步,无以至千里;不积小流,无以成江海。

标签:back,C++,v1,v2,vector,&&,push,size
From: https://blog.csdn.net/2301_79585944/article/details/136770877

相关文章

  • 矩阵匹配【华为OD机试JAVA&Python&C++&JS题解】
    一.题目-矩阵匹配从一个NM(N<=M)的矩阵中选出N个数,任意两个数字不能在同一行或同一列,求选出来的N个数中第K大的数字的最小值是多少。输入描述:输入矩阵要求:1<=K<=N<=M<=150输入格式:NMKNM矩阵输出描述:N*M的矩阵中可以选出M!/N!种组合数组,每个组合数组中第K大的数中的......
  • 文本统计分析【华为OD机试JAVA&Python&C++&JS题解】
    一.题目-文本统计分析有一个文件,包含以一定规则写作的文本,请统计文件中包含的文本数量规则如下文本以";“分隔,最后一条可以没有”;",但空文本不能算语句,比如"COMMANDA;;"只能算一条语句.注意,无字符/空白字符/制表符都算作"空"文本文本可以跨行,比如下面,......
  • C++ 计数器小全
    1.简介  这篇文章介绍了C++的计数器们,包括:cnt(累加计数器),sum(求和计数器),mul(累乘计数器),last(上一项记录器),flag(状态计数器)  下文中,我就会详细说这6种计数器2.详解(正文)        2-1.cnt详解        cnt的中文名是累加......
  • 【C++】内存管理
    在学习完类与对象后,在学习C++中,接下来需要学习的是内存管理,在本篇博客中将会讲解C++的内存管理。 一.C/C++内存分布在学习内存管理前,我们先来了解一下C/C++的内存分布。 一般来说,内存可以分为四个区域:栈区、堆区、数据段、代码段。栈区:用于存放非静态的局部变量,函......
  • C++与C语言
    C++之所以是C++,和面向过程的C语言相比,它加了一个类,还有一个是模板。 引入C语言这种面向过程的编译语言可以将待解的问题分解成若干个子问题,面向对象程序设计则是建立在结构化程序设计方法的基础上,完全避免了结构化程序设计方法中所存在的问题。程序=数据结构+算法(结构化......
  • 数据结构 —— 线性表的链式存储(链表)(C++)
    目录单链表(有头结点)定义初始化判空销毁清空求表长取值查找插入删除创建头部创建尾部创建本文相关知识:以链式存储结构来实现线性表(C++)如有错误请指正~~谢谢~后面更新循环链表和双向链表单链表(有头结点)以带头结点的单链表为例,操作更加简便!定义首先,为了增强程序的可读性,做出以......
  • 杨辉三角形(c++实现)
    题目下面的图形是著名的杨辉三角形:如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:1,1,1,1,2,1,1,3,3,1,1,4,6,4,1,…给定一个正整数N,请你输出数列中第一次出现N是在第几个数?输入输入一个整数N。输出输出一个整数代......
  • UE4 c++ 通过枚举寻找DataTable中的数据
    DataTable中的数据DataTable中每一行数据是一个结构体在C++代码中定义结构体,然后可以在蓝图中可以创建以此结构体为单元的DataTable枚举变量定义一个头文件来存储枚举变量,然后可以在要使用的文件中利用MyEnumPtr=FindObject<UEnum>(ANY_PACKAGE,TEXT("EGridShapEnum"),tr......
  • C++入门(一)
    目录命名空间:为什么要提出命名空间?命名空间的定义:命名空间的使用:加命名空间名称及作用域限定符:使用using将命名空间中某个成员引入:使用usingnamespace命名空间名称引用:C++输入&输出:库的导入:使用说明:输入和输出:输入流:输出流:缺省参数:缺省参数的概念:缺省参数的......
  • 第1章 迈向现代C++
    芝士wa2024.3.30资源链接1.1被启用的特性不再允许字符串字面值常量赋值给一个char*。如果需要用字符串字面值常量赋值和初始化一个char*,应该使用constchar*或者autochar*str="helloworld!";//将出现弃用警告C++98异常说明、unexpected_handler,set_unexpec......