C++ STL vector

C++ STL vector,第1张

vector
  • vector简介
  • vector 的使用
    • 初始化
    • operator[] 与 at 对容器元素访问
    • reserve 、 resize 、assign
    • push_back、insert
    • emplace_back 原位构造

vector简介
  1. vector 是表示可以改变大小的数组的序列容器
  2. vector 与数组一样,元素使用连续的存储空间,就可以使用常规指针,指向其元素,使用偏移量来访问存储空间中的元素
  3. vector 与数组不同的是,vector 的大小可以动态变化,容器会自动扩容存储空间
  4. vector 使用一个动态分配的连续存储空间来存储元素,在插入新元素时存储空间可能需要重新分配,以便增大大小,这意味着分配一个新存储空间要将所有元素移动到其中,就处理时间而言,这是一项相对昂贵的任务,因此,向量不会在每次向容器添加元素时重新分配
  5. vector 容器可以分配一些额外的存储空间以适应可能的增长,因此容器的实际容量可能大于严格需要的存储容量,即容量大小
  6. vector 与 array 相比,向量消耗更多的内存,以换取管理存储和以高效方式动态增长的能力
  7. 与其他动态序列容器(deques、list、和forward_list)相比,vector可以非常高效的访问其元素,并相对高效地从其末尾添加或删除元素,对于在结尾以外的位置插入或删除元素的 *** 作,其性能较差
vector 的使用 初始化
int main()
{
	vector<int> ar1;
	vector<int> ar2 = { 12,23,34,45,56,67,78,89,90 };
	vector<int> ar3(10, 20);//开辟10个空间,每个空间存放20
	vector<int> ar4({ 12,23,34,45,56,67 });

	vector<int> ar5(ar1);
	vector<int> ar6(std::move(ar2));
}
operator[] 与 at 对容器元素访问
int main()
{
	vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
	cout << objvec.size() << endl;
	cout << objvec.capacity() << endl;

	return 0;
}


这里首先构造Object对象,然后将Object对象拷贝构造进vector容器,随后析构第一遍构建的Object对象

int main()
{
	vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
	cout << objvec.size() << endl;     //有效数据大小
	cout << objvec.capacity() << endl; //容量大小

	for (int i = 0; i < objvec.size(); i++)
	{
		cout << objvec[i].Value() << endl;
	}
	for (int i = 0; i < objvec.capacity(); i++)
	{
		cout << objvec.at(i).Value() << endl;
	}
	vector<Object>::iterator it = objvec.begin();
	for (; it != objvec.end(); it++)
	{
		cout << it->Value();
		it->Value() += 100;
		cout << " " << (*it).Value() << endl;
	}

	return 0;
}

这里的无论operator[]、at、iterator 都是对其进行引用

当我们不希望对引用的数据进行改变的时候,可以使用常性迭代器

	//不希望对其进行改变  常性迭代器
	vector<Object>::const_iterator cit = objvec.begin();
	for (; cit != objvec.end(); cit++)
	{
		cout << cit->Value() << endl;
	}

并且需要创建常性Value()方法,因为常性迭代器只能指向常性返回数据

int main()
{
	vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
	for (auto x : objvec) //不能将x认为是一个迭代器 推演: x => Object 
	{
		cout << x.Value() << endl;
	}
}

auto在这里会不断产生拷贝构造与析构

如果直接进行引用,那么就可以省掉多次的拷贝构造与析构

for (auto &x : objvec) //for(const auto &x : objvec)
{
	cout << x.Value() << endl;
}

reserve 、 resize 、assign
  • reserve
int main()
{
	vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
	cout << objvec.size() << endl;
	cout << objvec.capacity() << endl;

	objvec.reserve(10); //预留存储空间

	cout << objvec.size() << endl;
	cout << objvec.capacity() << endl;
}

当空间扩容至10,将原本的对象进行拷贝构造,随后进行析构,容量大小变为10

若new_cap大于capacity(),则所有迭代器,包含尾后迭代器和所有到元素的引用都被非法化

	vector<Object>::iterator it = objvec.begin();
	Object& obj = objvec.back();
	objvec.reserve(10); //预留存储空间
	//迭代器与引用非法 

这是因为,扩容伴随着拷贝构造与析构,原本我们的迭代器所指的空间或者引用的对象都会被析构掉

  • resize
int main()
{
	vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
	cout << objvec.size() << endl;
	cout << objvec.capacity() << endl;

	objvec.resize(10); 

	cout << objvec.size() << endl;
	cout << objvec.capacity() << endl;
}

resize扩容首先,根据上面代码,首先构建了6个Object对象,再将原本有的4个Object对象进行拷贝构造,随后析构原本的对象

resize 与 reserve 不同的是,如果reserve 扩容的大小 < 原本容量的大小,则按照原本的容量进行,而resize 扩容大小 > 原本大小,则不但进行扩容增加空间并且会构造对象,而此时再次进行reszie 扩容大小 < 原本大小,则会对多余对象进行析构

int main()
{
	vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
	cout << objvec.size() << endl;
	cout << objvec.capacity() << endl;
	objvec.resize(10); 
	cout << objvec.size() << endl;
	cout << objvec.capacity() << endl;
	objvec.resize(2); //不进行缩容,但是对象个数改变
	cout << objvec.size() << endl;
	cout << objvec.capacity() << endl;
}

  • assign
int main()
{
	vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
	cout << objvec.size() << endl;
	cout << objvec.capacity() << endl;

	for (auto& x : objvec)
	{
		cout << x.Value() << endl;
	}

	objvec.assign(10, Object(100));
	cout << objvec.size() << endl;
	cout << objvec.capacity() << endl;

	for (auto& x : objvec)
	{
		cout << x.Value() << endl;
	}
	return 0;
}

assign的作用就是,将原本容器的全部对象进行析构,随后构建10个对象再将其进行填充

可以看到,首先构建了1个对象,然后析构旧的4个对象,再拷贝构造10个对象,再将一开始构建的对象进行析构

若我们申请空间远远大于实际大小的时候

int main()
{
	vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
	cout << objvec.size() << endl;
	cout << objvec.capacity() << endl;

	objvec.reserve(10000);

	cout << objvec.size() << endl;
	cout << objvec.capacity() << endl;
	//objvec.size() <<<< objvec.capacity();

	{
		vector<Object> tmp(objvec);
		tmp.swap(objvec);
	}
	cout << objvec.size() << endl;
	cout << objvec.capacity() << endl;
	return 0;	
}

可以通过上面的方式将容量缩减回来

我们再块作用域中,通过容器进行初始化tmp,此时tmp的size为4,capacity为4,然后将tmp与objvec进行交换,那么此时objvec的size为4,capacity为4,而tmp的size为4,capacity为10000;当块作用域结束,tmp将被释放掉;从而实现了容量的缩减

reserve 、 resize 、assign这三个函数都会引起迭代器的失效问题

push_back、insert

首先从效率上讲,push_base 尾插 比 insert 随机插入,效率要高得多

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

	return 0;
}


我们发现容器进行了扩容,进行了拷贝构造,那么这里的迭代器指向的是原本的容器已经被析构掉,所以迭代器会失效

emplace_back 原位构造

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

objvec.emplace_back(100); 意味着,直接将Object对象构建在尾部空间,与 push_back 不同的地方在于,objvec.push_back(Object(100)) 会首先构造一个无名对象,接着进行拷贝构造于容器尾部,emplace_back相当于进行类定位new

object.emplace_back(100);      //传输地址 定位new
object.push_back(100);         //构建对象 右值引用
object.push_back(Object(10));  

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/langs/707137.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-24
下一篇 2022-04-24

发表评论

登录后才能评论

评论列表(0条)

保存