【C++STL】vector的介绍和使用

文章目录

    • vector与数组的不同:
    • 初始化:
    • 访问vector中的数据
    • auto(底层用迭代器实现)
      • 自己设计的类型会导致反复析构
      • 解决办法,x使用引用
      • 如果不希望修改容器中的值:
      • 数组也可以被认为是一种容器
    • 总结
    • reserve()(只进行扩容,对象个数不改变)
      • 迭代器是失效问题:
    • resize():(对象的个数改变,可以进行容量的收缩)
    • assign()
    • 程序运行一段时间,只删除对象,不删除容量,当前对象的个数远远小于容量,如何降容量减下来?
    • push_back()
      • 迭代器失效:
    • insert()
    • 原位构建元素:emplace_back(),
  • 区别:两者的构建形式有什么区别?
  • 解决迭代器失效的方法:

vector与数组的不同:

  1. vector是表示可以改变大小的数组的序列容器。
  2. vector与数组一样,元素使用连续的存储空间,就可以使用常规指针,指向其元素,使用偏移量来访问存储空间中的元素。
  3. vector 与数组不同的是,vector的大小可以动态变化,容器会自动扩容存储空间。
    vector的特点:
  4. vector使用一个动态分配的连续存储空间来存储元素。在插入新元素时存储空间可能需要重新分配,以便增大大小,这意味着分配一个新存储空间要将所有元素移动到其中。就处理时间而言,这是一项相对昂贵的任务,因此,向量不会在每次向容器添加元素时重新分配。
  5. vector容器可以分配一些额外的存储空间以适应可能的增长,因此容器的实际容量可能大于严格需要的存储容量(即容器的大小)。。
  6. vector与array相比,向量消耗更多的内存,以换取管理存储和以高效方式动态增长的能力。
  7. 与其他动态序列容器(deques、 list和forward_list)相比,vector可以非常高效地访问其元素(就像数组一样)并相对高效地从其末尾添加或删除元素。对于在结尾以外的位置插入或删除元素的操作,其性能较差。

初始化:

int main()
{
	vector<int> ar1;
	vector<int> ar2 = { 12,23,34 };
	vector<int> ar3({ 12,23,34 });//初始化列表
	vector<int> ar4(10, 23);//初始化十个空间,将23拷贝进去
	vector<int> ar5 = ar2;
    vector<int> ar6(std::move(ar3));
}
  1. 调用构造函数初始化object的属性,创建对象,然后将这些对象,调用拷贝构造函数,拷贝到vector中,然后调用析构函数,析构掉拷贝
    过程中产生的4个无名对象。
  2. 等整个函数生存期到了,在析构vector中的4个对象。
int main()
{
 	vector<Object> vec = { Object(10),Object(20),Object(30),Object(40)};
	cout << vec.size() << endl;
	cout << vec.capacity() << endl;
}

【C++STL】vector的介绍和使用_第1张图片

访问vector中的数据

1.for循环,下标访问
2.at()访问
3.迭代器访问,相当于面向对象的指针
4.常性迭代器,无法改变值,只可取值
5.基于范围的for循环,使用引用的方式,因为如果是变量,每一步都会拷贝一个对象。

for (int i = 0; i < vec.size(); i++)
	{
		cout << vec[i].Value() << endl;
		           //obj.Value()
	}
	//for (int i = 0; i < vec.capacity(); i++)
	//{                    //代表的是容量,后面访问的均为空
	//	cout << vec.at(i).Value() << endl;
	//	         //vec.obj.value()
	//}
	vector<Object>::iterator it = vec.begin();
	for (; it != vec.end(); it++)
	{
		cout << it->Value() << " " << endl;
	//	it->Value() += 10;
    //	cout << (*it).Value() << " " << endl;
	}
	vector<Object>::const_iterator it2 = vec.begin();
	for (; it2 != vec.end(); it2++)
	{
		//常性迭代器只能调动常方法
	//	cout << it2->Value() << endl;
		cout << (*it2).Value() << endl;
		//it->Value() += 100;不能修改,只能读

	}    
	
	

auto(底层用迭代器实现)

自己设计的类型会导致反复析构

内置类型不影响

 //推演出x是object类型,因此需要遍历vector容器,需要每次都把当前节点的对象拷贝给x,如果采用引用,就可以直接操纵vector中的对象,完成相应操作。
for (auto x : vec)//x不是迭代器,vector容器,
{
	cout << x.Value() << " " << endl;
}
cout << endl;

如下结果,反复拷贝和析构
【C++STL】vector的介绍和使用_第2张图片

解决办法,x使用引用


 
for (auto& x : vec)//x不是迭代器,vector容器,
{
	cout << x.Value() << " " << endl;
}
cout << endl;

【C++STL】vector的介绍和使用_第3张图片
结果:
请添加图片描述

如果不希望修改容器中的值:

for (const auto& x : vec)

数组也可以被认为是一种容器

内置类型:不使用引用,直接将1拷贝给x
int arr[] = { 1,2,3,4,5 };
	for (auto x : arr)
	{       //直接打印
		cout << x << endl;//数组也可以被认为是一种容器
	}
	使用引用:取的是1的地址
	int arr[] = { 1,2,3,4,5 };
	for (auto &x : arr)
	{    //需要下地址,找到其值
		cout << x << endl;//数组也可以被认为是一种容器
	}

const修饰的是整个迭代器,修饰的是it3本身
所以其指向的值可以变,但是其本身不能变

总结

是否是使用引用和const,取决于使用目的。容器中的数据是否需要修改,是内置类型还是设计类型,等等。

const vector<Object>::iterator it3 = vec.begin();
	for (; it3 != vec.end(); it3++) //const修饰的是整个迭代器,修饰的是it3本身                  it3++   error
 	{                             //所以其指向的值可以变,但是其本身不能变
		cout << it3->Value() << endl;
		it3->Value() += 10;
	}

【C++STL】vector的介绍和使用_第4张图片

reserve()(只进行扩容,对象个数不改变)

void reserve(size_type new_cap);

增加vector的容量到大于或等于new_cap,若new_cap大于当前capacity(),则重新分配空间,将原来数据拷贝构造过去,如果小于capacity(),则不处理。

int main()
{
	vector<Object> vec = { Object(10),Object(20),Object(30),Object(40) };
	cout << vec.size() << endl;
	cout << vec.capacity() << endl;
	
	vec.reserve(10); //如果大于原本的容量。重新申请空间,并将原来容器中的对象,调用拷贝构造,拷贝到新的空间。
	cout << vec.size() << endl;
	cout << vec.capacity() << endl;
}

【C++STL】vector的介绍和使用_第5张图片
【C++STL】vector的介绍和使用_第6张图片

迭代器是失效问题:

如果在使用reserve()时, enw_cap大于capacity,需要重新申请空间,那么迭代器的指向原有空间会被析构,造成迭代器失效。

int main()
{
	vector<Object> vec = { Object(10),Object(20),Object(30),Object(40) };
	cout << vec.size() << endl;
	cout << vec.capacity() << endl;
	
	vector<Object>::iterator it = vec.begin();
	Object& obj = vec.back();
	vec.reserve(10); 
	cout << vec.size() << endl;
	cout << vec.capacity() << endl;
}

如下图:
【C++STL】vector的介绍和使用_第7张图片

resize():(对象的个数改变,可以进行容量的收缩)

如果resize()的参数大于原来的capacity,那么

  1. 会重新申请空间
  2. 再对申请的额外的空间调用默认构造,创建对象,初始化为0;
  3. 把源数据拷贝构造过来。
	vec.resize(10);
	vec.resize(2);//会将10个对象,后面8个对象全部析构掉

【C++STL】vector的介绍和使用_第8张图片
【C++STL】vector的介绍和使用_第9张图片

assign()

void assign(size_t count, _Ty());

这个函数和上面两个的不同之处是:

  1. 先构造一个目标对象;
  2. 析构之前的对象;
  3. 拷贝构造这个目标对象count次;
  4. 析构目标对象。
int main(void)
{
	vector<Object> vec = { Object(10), Object(20), Object(30),Object(40) };
	cout << vec.size() << endl;
	cout << vec.capacity() << endl;

	vec.assign(10, Object(100));//构建新对象,delete 原来的对象,,拷贝构造到vector容器中,拷贝10次
	cout << vec.size() << endl;
	cout << vec.capacity() << endl;
	return 0;
}

【C++STL】vector的介绍和使用_第10张图片

程序运行一段时间,只删除对象,不删除容量,当前对象的个数远远小于容量,如何降容量减下来?

int main(void)
{
	vector<Object> vec = { Object(10), Object(20), Object(30),Object(40) };
	cout << vec.size() << endl;
	cout << vec.capacity() << endl;
	{
		vector<Object> tmp(vec);
		tmp.swap(vec);
	}
	cout << vec.size() << endl;
	cout << vec.capacity() << endl;
	return 0;
}

【C++STL】vector的介绍和使用_第11张图片

1.调用拷贝构造函数将vec的中的对象拷贝到tmp
2.使用swap,将tmp和vec中的first,last,end进行交换。
3.块作用域结束,释放tmp中的对象,以及空间。
完成容量的缩小(必须使用块作用域)
vector的数据老是搬家,非常的危险。

push_back()

int main(void)
{
	vector<Object> vec = { Object(10), Object(20), Object(30),Object(40) };
	cout << vec.size() << endl;
	cout << vec.capacity() << endl;
	vec.reserve(10);
	vector<Object>::iterator it = vec.begin();
	cout << it->Value() << endl;
	vec.push_back(Object(40));
	cout << it->Value() << endl;

	return 0;
}

【C++STL】vector的介绍和使用_第12张图片
如果没有一开始就给vec增容,就会导致插入数据时,增容,导致迭代器失效。和reserve()的原因一样,it所指的原空间释放。

迭代器失效:

由于插入数据时没有空间,就需要扩容,原空间释放。迭代器失效。
【C++STL】vector的介绍和使用_第13张图片

insert()

int main(void)
{
	vector<Object> vec = { Object(10), Object(20), Object(30),Object(40) };
	cout << vec.size() << endl;
	cout << vec.capacity() << endl;
	vec.reserve(10);
	vector<Object>::iterator it = vec.begin();
	cout << it->Value() << endl;
	vec.insert(it, Object(90));  //均是调用拷贝构造函数,将Object对象拷贝到vec中,it所指的位置。
	vec.insert(it, { Object(70),Object(80) });
	           //可以初始化列表方式进行初始化。
	cout << it->Value() << endl;

	return 0;
}

原位构建元素:emplace_back(),

使用定位new,创建对象。
如下:

template<class T,class<...Arg>>
 void Make(T* p,Arg...arg)
 {
   new(p) T(std::forward<Arg...>(arg)...);
 }

emplace_back(100)
将100作为实参,在vector容器中的插入点,调用Object的构造函数,直接初始化,创建对象。
而push_back 是隐式的调用拷贝构造,将obj无名对象拷贝到vector的插入点。然后将拷贝构造析构掉。

int main(void)
{
	vector<Object> vec = { Object(10), Object(20), Object(30),Object(40) };
	vec.reserve(10);
	cout << vec.size() << endl;
	cout << vec.capacity() << endl;
	
	vec.emplace_back(100);
     cout << vec.size() << endl;
	cout << vec.capacity() << endl;

	return 0;
}

【C++STL】vector的介绍和使用_第14张图片
【C++STL】vector的介绍和使用_第15张图片
#emplace_back和push_back相比,前者的效率更快。

区别:两者的构建形式有什么区别?

      前者使用定位new在vector的插入节点,调用构造函数,创建对象
      后者,调用拷贝构造将Object对象,将无名对象拷贝到vector容器中。

解决迭代器失效的方法:

提前增容,就不会因为容量不足导致扩容,让先前的空间被析构。

你可能感兴趣的:(C++深入学习,STL,c++,开发语言)