vector

这篇具有很好参考价值的文章主要介绍了vector。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

vector容器:可以存储任意类型的元素,较之于string只能存储类型char。底层都是动态的顺序表
stl的容器都有一些同名的方法,实现功能都是类似的比如:一般都有size(),push_back(),empty()。。。。
连续的内存,故而支持随机访问,操作起来和在C语言中的数组差不多,当然vector< int >的对象有很多成员方法,使用起来更便捷。
vector
容器是模版实现的,所以在vector创建对象要给出模版参数 vector< int >
构造方法:

//简写版本,去掉了最后一个参数默认的分配器,以及函数头部的关键字
vector<T>();

vector<T> (size_type n, const T& val = T());

vector<T>(const vector& x);

简单使用

	vector<int> arr;//空int容器
	arr.resize(10);
	vector<int> v1(10);
	vector<int> v2(2, 10);//两个元素,并且元素的值=10
	//调试后可以看到vector会把所有元素初始化为0,如果不指定值
	for (int i = 0; i < v1.size(); i++);
	for (auto x : v2);//范围for
	for (auto& x : v2);//加上引用,可以对vector中的元素进行修改
	auto it = v1.begin();//使用迭代器
	while (it != v1.end())
		it++;
	it + 1;//迭代器可以像指针一样+整数,就像指针移动一样
	//int pos = it;//但不能当做下标

二维数组:

	int n, m;
	cin >> n >> m;
	vector<vector<int>> vi(n, vector<int>(m));//vector创建二维数组arr[n][m]
	//这样就实现了C/C++语言无法创建动态二维数组

vector< T > ( size_t n, const T& val = T() );
参考这个构造函数去理解为什么这样做也是合理的
此时 T:vector< int >
n:n
val:vector< int >(m)存储的值是一维数组,
const vector< int >& val = vector< int >(m);
这是一个匿名对象赋给了val

	v1.push_back(10);//数组添加元素
	v1[1] = 99;//其实操作就跟数组差不多
	//数组的操作很多都会涉及到排序
	sort(v1.begin(), v2.end());
	sort(v1.begin(), v2.end(), greater<int>());
	//第三个参数是可以使函数对象,函数指针
	template <class T> struct greater : binary_function <T,T,bool> {
	bool operator() (const T& x, const T& y) const {return x>y;}
	};
	仿函数重载函数调用运算符()
	在functional头文件中还有一些类似的模版:
	less,less_equal,
	greater_equal,
	equal_to,not_equal_to
	因为是模版所以使用时要给出模版参数
	*/

	bool myfunction (int i,int j) { return (i<j); }

	struct myclass {
	bool operator() (int i,int j) { return (i<j);}
	} myobject;

用vector存储自定义类型airplane

#include<iostream>
using namespace std;
#include<vector>
class airplane
{
	int need;
	int have;
	int come;
};

int main()
{
	int n;
	cin >> n;
	auto arr = new airplane[n];
	vector<airplane> arr2(5);
}

vector也是1.5被扩容(不同编译器实现不同)

#include<iostream>
#include<vector>

using namespace std;
int main()
{
	vector<int> v;
	int cap = v.capacity();
	cout << cap<<" ";
	for (int i = 0; i < 100; i++)
	{
		v.push_back(i);
		if (cap != v.capacity())
		{
			cap = v.capacity();
			cout << cap << " ";
		}
	}
	//容量变化:0 1 2 3 4 6 9 13 19 28 42 63 94 141
	return 0;
}

迭代器:
类似于指针 || 对指针的封装
对于vector来说,迭代器就是 typedef T* iterator
获取迭代器 auto it = v.begin();
迭代器失效:迭代器越界了,指向的地址原本数据转移了
其实这是从指针的角度理解,实际迭代器既然是封装,那么就不会想指针那么干,指针比较底层直接
vector
预防:在所有可能会导致迭代器失效的操作之后,需要使用迭代器时给迭代器重新赋值

vector不同于string,其中的insert,erase操作的参数只能传递迭代器
vector

为什么要这样设计呢?

我理解的是,因为string类确定要存储的就是char,而vector容器类是根据模版参数来实例化容器类的,无法确定其类型,所以要使用对应的迭代器,迭代器是带有类型的指针,也只能这样处理。

模拟实现vector:

#pragma once

//模拟实现vector
#include<iostream>

namespace gyx
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
	private:
		iterator start, finish, endofstorage;
		//这些迭代器作为底层操作的指针
	public:
		vector()
			:start(nullptr)
			, finish(nullptr)
			, endofstorage(nullptr)
		{}
		vector(int n, const T& val = T())
		{
			start = new T[n];
			for (size_t i = 0; i < n; ++i)
				start[i] = val;
			
			finish = start + n;
			endofstorage = finish;
		}



		template<class Iterator>
		vector(Iterator first, Iterator last)
		{
			auto it = first;

			size_t n = 0;
			while (it != last)
			{
				++it;
				n++;
			}

			start = new T[n];
			while (first != last)
			{
				*finish = *first;
				++first;
				++finish;
			}
		}

		vector(const vector<T>& v)//拷贝构造
		{
			start = new T[v.size()];
			for (size_t i = 0; i < v.size(); ++i)
				start[i] = v[i];

			finish = start + n;
			endofstorage = finish;
		}

		vector<T>& operator=(vector<T> v)
		{
			this->swap(v);
			return *v;
		}

		~vector()
		{
			if (start)
			{
				delete[] start;
				start = finish = endofstorage = nullptr;
			}
		}
		iterator begin(){ return start; }
		iterator end(){ return finish; }

		size_t size()const{ return finish - start; }
		size_t capacity()const{ return endofstorage - start; }
		bool empty()const{ return start == finish; }

		void reserve(size_t newcapacity)//扩容或者缩,一般都是扩
		{
			size_t oldcap = capacity();
			if (newcapacity > oldcap)
			{
				T* temp = new T[newcapacity];

				if (start)
				{
					for (size_t i = 0; i < size(); ++i)
						temp[i] = start[i];

					delete[] start;
				}
				size_t sz = size();
				start = temp;
				finish = start + sz;
				endofstorage = start + newcapacity;
			}
		}

		void resize(size_t newsize, const T& val = T())
		{
			size_t oldsize = size();
			if (newsize < oldsize)
				finish = start + newsize;

			else
			{
				if (newsize>capacity())
					reserve(newsize);

				for (size_t i = oldsize; i < newsize; ++i)
					start[i] = val;

				finish = start + newsize;

			}
		}

		T& front(){ return *start; }
		const T& front()const{ return *start; }
		T& back(){ return *(finish - 1); }
		const T& back()const{ return *(finish - 1); }

		T& operator[](size_t index)
		{
			assert(index < size());
			return start[index];
		}
		const T& operator[](size_t index)const
		{
			assert(index < size());
			return start[index];
		}

		T& at(size_t index)
		{
			// assert(index < size());
			if (index >= size())
			{
				throw out_of_range("vector at method: index out_of_range");
			}

			return start[index];
		}

		const T& at(size_t index)const
		{
			if (index >= size())
			{
				throw out_of_range("vector at method: index out_of_range");
			}

			return start[index];
		}

		iterator erase(iterator first, iterator last)
		{
			auto copy_first = first;
			auto copy_last = last;
			size_t last - first;
			while (copy_last != end())
			{
				*copy_first = *copy_last;
				++copy_first;
				++copy_last;
			}
			finish -= n;
			return first;
		}


		void clear()
		{
			erase(begin(), end());
		}

		void swap(vector<T>& v)
		{
			std::swap(start, v.start);
			std::swap(finish, v, finish);
			std::swap(endofstorage, v.endofstorage);
		}

		void push_back(const T& val)
		{
			if (finish == endofstorage)
			{
				reserve(capacity() * 2);
			}

			*finish = val;
			++finish;
		}

		void pop_back()
		{
			if (empty())
				return;

			--finish;
		}

		iterator insert(iterator pos, const T& val)
		{
			if (pos<begin() || pos>end())//位置不合法
				return end();

			if (finish == endofstorage)
			{
				size_t len = pos - start;
				reserve(capacity() * 2+3);
				pos = start + len;//扩容后呀更新pos,否则会有迭代器失效的可能
			}

			auto it = finish - 1;
			while (it >= pos)
			{
				*(it + 1) = *it;
				it--;
			}
			*pos = val;//这句有问题吗?扩容之后pos位置的迭代器就失效了,这样赋值是有问题的
			finish++;

			return pos;
		}

	};
}

vector
实现原理使用是三个迭代器(指针),来管理这段连续内存,注意在扩容后原先传入的迭代器失效问题。例如:在insert中,之前传入 的pos位置,在reserve扩容后很有可能申请了另一片连续的空间,那么之前传入的pos位置就失效了,要更新,所以在代码中的设计是用len保存步长(距离start的长度,来动态定位pos),如果没有reserve前后的那两行代码,测试下面代码就会报错

#include"MYvector.h"
using namespace gyx;
int main()
{
	vector<int> arr;
	for (int i = 0; i < 100; i++)
	{
		arr.insert(arr.begin(), i);
		std::cout << i << "     " << arr.begin();
	}
	return 0;
}

对于传入的自定义类型,要有无参构造,或者全缺省构造,因为在new申请空间时T* temp = new T[newcapacity];
只能调用无参构造,对于每一个存储的对象,在单独去用赋值运算符传递一个新的单独构造的对象。

拷贝元素:memcpy是浅拷贝
如果vector中放置的元素中涉及到资源管理时候,采用memecpy的浅拷贝是会出问题

memcpy(temp, start, sizeof(T) * size());

将容器当中的数据一个个拷贝过来时不能使用memcpy函数,当vector存储的数据是内置类型或无需进行深拷贝的自定义类型时,使用memcpy函数是没什么问题的,但当vector存储的数据是需要进行深拷贝的自定义类型时,使用memcpy函数的弊端就体现出来了。例如,当vector存储的数据是string类的时候。文章来源地址https://www.toymoban.com/news/detail-437357.html

到了这里,关于vector的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 创建了一个名为nums_list的vector容器,其中存储了一系列的pair<int, int>

    在上述代码中,我们首先创建了一个整数数组 nums ,其中包含了一些整数。然后,我们创建了一个空的 nums_list 容器。接下来,我们使用 for 循环遍历整数数组 nums ,并将每个数组元素和其对应的索引作为一个 pair 对象加入到 nums_list 容器中。最后,我们遍历 nums_list 容器,并

    2024年02月07日
    浏览(44)
  • 【C++】STL 算法 - transform 变换算法 ① ( transform 函数原型 | 将 一个 或 两个 输入容器 中的元素 变换后 存储到 输出容器 中 )

    std::transform 是 STL 标准模板库 中的一个算法 , 该算法的作用是 用于对 容器 或 指定迭代器范围 的 每个元素 进行 指定的 \\\" 转换操作 \\\" , 并将 \\\" 转换结果 \\\" 存储到另一个容器中 ; std::transform 算法 接受 一个 或 两个输入范围 , 以及一个输出范围 , 并 根据提供的 一元函数对象 或

    2024年01月16日
    浏览(62)
  • vector容器 常用函数

    vector是STL(标准模板库)中最常见的容器,它是一种顺序容器,支持随机访问。可以用sort对其进行排序,底层数据结构是数组,可以随机访问元素。 vector是一块连续分配的内存,从数据安排的角度来讲,和数组极其相似,不同的地方就是:数组是静态分配空间,一旦分配了空间

    2024年02月21日
    浏览(26)
  • C++---vector容器

    是STL容器中的一种常用的容器,由于其大小(size)可变,常用于数组大小不可知的情况下来替代数组。vector容器与数组十分相似,被称为动态数组。时间复杂度为O(1)。 数组数据通常存储在栈中,vector数据通常存储在堆中。 动态扩展不是在原空间后加入空间,而是寻找更大空

    2024年04月14日
    浏览(42)
  • STL : vector 矢量容器

    目录 Vector Capacity Elements access Modifiers Allocator Non-member Notice overloads Template specializations #includevector    using namespace std; 矢量容器 :单向开口的连续内存空间,底层封装:数组; 预留内存空间不够时,可以动态扩展(容器满了则新建容器同时进行拷贝); 顺序序列:容器元素按

    2023年04月15日
    浏览(42)
  • Vector容器的详细介绍

    1.1 功能:   -vector 数据结构和数组非常相似,也称单端数组 1.2 vector与普通数组区别:   - 不同之处在于数组是静态空间,而vector可以动态拓展 1.3 动态拓展:   并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间。   vector容

    2024年01月23日
    浏览(40)
  • C++中的vector容器

      vector是封装动态数组的顺序容器。   就像数组一样,vector也采用的连续存储空间来存储元素。这也就意味着我们可以通过下标来获取vector的元素,和数组一样高效。但是又不像数组,vector的大小是可以动态改变的,且它的大小会被容器自动处理。   本质上,vector使

    2023年04月24日
    浏览(50)
  • C++提高编程——STL:string容器、vector容器

    本专栏记录C++学习过程包括C++基础以及数据结构和算法,其中第一部分计划时间一个月,主要跟着黑马视频教程,学习路线如下, 不定时更新,欢迎关注 。 当前章节处于: ---------第1阶段-C++基础入门 ---------第2阶段实战-通讯录管理系统, ---------第3阶段-C++核心编程, -----

    2024年01月23日
    浏览(48)
  • 《C++ primer》练习3.20:输出vector相邻元素的和&输出vector头尾对象的和

    最近看《C++ Primer》,有这样一个题目 读入一组整数并把它们存入一个vector对象,将每对相邻整数的和输出出来。 这里要注意输入的奇数个和偶数个的数的区别。偶数个整数的话刚好数全部用完,奇数个整数最后一个数空出来,也输出出来,后面没有数了(再使用后面的索引

    2024年02月09日
    浏览(32)
  • Vector容器(黑马程序员)

    视频与文档链接 功能: vector数据结构和 数组非常相似 ,也称为 单端数组 普通数组一旦分配内存,容量就被固定了,无法扩展。 vector与普通数组区别: 不同之处在于数组是静态空间,而vector可以 动态扩展 动态扩展: 并不是在原空间之后续接新空间,而是找更大的内存空

    2024年02月08日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包