C++:如何高效的使用std::vector?

目录:

一、在std::vector尾部添加对象时应尽量使用emplace_back,而不要使用push_back

二、添加多个元素前应使用reserve设置容量,防止扩容时发生元素复制

三、删除元素时应从最后一个元素开始删除,不要从中间开始删除

四、添加新对象时应从结尾处添加,而不要从中间添加

五、使用std::vector(std::vector)和std::vector(std::initializer_list)对std::vector赋初始值会将std::vector和std::initializer_list中所有对象复制

六、需要将对象全部转移到另外一std::vector时,应使用std::vector.swap()、std::swap()、std::move(std::vector)

七、如果std::vector中在存放指针对象,即std::vector,则应使用智能指针


先说一下关于std::vector的一些基本知识:

  1. std::vector<T>中存放的T对象是在堆中分配的。
  2. std::vector<T>会负责管理T对象的生命周期,所以当将T对象存放到std::vector<T>中时,std::vector<T>会构造一个自己能完全控制的T对象,构造方式可以是转移构造函数、拷贝构造函数还是普通构造函数。
  3. 当std::vector<T>的生命周期结束时,std::vector<T>也同时会将其中存放的对象析构。

对于如何高效使用std::vector,以下有几点建议。先给出一个类的代码,后面会用到它:

class Girl {

public:
	string name;
	int age;


	Girl() {
		cout << "Girl()" << endl;
	}

	Girl(const string& _name, int _age) : name(_name), age(_age) {
		cout << "Girl(string _name, int _age)" << name << endl;
	}

	Girl(const Girl& b) : name(b.name), age(b.age) {
		cout << "Girl(const Girl&)" << name << endl;
	}

	Girl(Girl&& b) : name(move(b.name)), age(move(b.age)) {
		cout << "Girl(Girl&&)" << name << endl;
	}

	Girl& operator=(const Girl& b) {
		this->name = b.name;
		this->age = b.age;

		cout << "operator=(const Girl&)" << name << endl;

		return *this;
	}

	~Girl() {
		cout << "~Girl()" << name << endl;
	}

};

一、在std::vector<T>尾部添加对象时应尽量使用emplace_back,而不要使用push_back

int main() {
	vector<Girl> vvv1;
	vector<Girl> vvv2;

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

	vvv1.push_back(Girl("dd", 90));

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

	vvv2.emplace_back("dd", 90);

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

	return 1;
}

------------------------------------------------
Girl(string _name, int _age)dd
Girl(Girl&&)dd
~Girl()
------------------------------------------------
Girl(string _name, int _age)dd
------------------------------------------------
~Girl()dd
~Girl()dd

从结果中可以看到,使用push_back会比emplace_back多调了转移构造函数和析构函数,因而性能偏低。

二、添加多个元素前应使用reserve设置容量,防止扩容时发生元素复制

int main() {
	vector<Girl> vvv1;

	cout << vvv1.size() << endl;
	cout << vvv1.capacity() << endl;

	vvv1.emplace_back("ss", 12);
	cout << "------------------------------------------------" << endl;
	vvv1.emplace_back("xx", 33);
	cout << "------------------------------------------------" << endl;
	vvv1.emplace_back("wq", 123);

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

	cout << vvv1.size() << endl;
	cout << vvv1.capacity() << endl;
	cout << "------------------------------------------------" << endl;

	vector<Girl> vvv2;
	vvv2.reserve(10);

	cout << vvv2.size() << endl;
	cout << vvv2.capacity() << endl;

	vvv2.emplace_back("ss", 12);
	cout << "------------------------------------------------" << endl;
	vvv2.emplace_back("xx", 33);
	cout << "------------------------------------------------" << endl;
	vvv2.emplace_back("wq", 123);

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

	cout << vvv2.size() << endl;
	cout << vvv2.capacity() << endl;
	cout << "------------------------------------------------" << endl;

	return 1;
}

0
0
Girl(string _name, int _age)ss
------------------------------------------------
Girl(string _name, int _age)xx
Girl(const Girl&)ss
~Girl()ss
------------------------------------------------
Girl(string _name, int _age)wq
Girl(const Girl&)ss
Girl(const Girl&)xx
~Girl()ss
~Girl()xx
------------------------------------------------
3
3
------------------------------------------------
0
10
Girl(string _name, int _age)ss
------------------------------------------------
Girl(string _name, int _age)xx
------------------------------------------------
Girl(string _name, int _age)wq
------------------------------------------------
3
10
------------------------------------------------
~Girl()ss
~Girl()xx
~Girl()wq
~Girl()ss
~Girl()xx
~Girl()wq

从结果中可以看到,由于vvv1没有设置reserver,导致初始容量不足,每添加一个新的对象都要先扩容,然后将以前存放的对象复制过去,因而性能偏低。

三、删除元素时应从最后一个元素开始删除,不要从中间开始删除

int main() {
	vector<Girl> vvv1;
	vvv1.reserve(10);

	vvv1.emplace_back("ss", 12);
	vvv1.emplace_back("xx", 33);
	vvv1.emplace_back("wq", 123);

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

	vector<Girl> vvv2;
	vvv2.reserve(10);

	vvv2.emplace_back("ss", 12);
	vvv2.emplace_back("xx", 33);
	vvv2.emplace_back("wq", 123);

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

	vector<Girl> vvv3;
	vvv3.reserve(10);

	vvv3.emplace_back("ss", 12);
	vvv3.emplace_back("xx", 33);
	vvv3.emplace_back("wq", 123);

	cout << "++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
	
	vvv1.erase(vvv1.begin());
	cout << "------------------------------------------------" << endl;
	vvv1.erase(vvv1.begin());
	cout << "++++++++++++++++++++++++++++++++++++++++++++++++" << endl;

	vvv2.erase(vvv2.end() - 1);
	cout << "------------------------------------------------" << endl;
	vvv2.erase(vvv2.end() - 1);
	cout << "++++++++++++++++++++++++++++++++++++++++++++++++" << endl;

	vvv3.pop_back();
	cout << "------------------------------------------------" << endl;
	vvv3.pop_back();
	cout << "++++++++++++++++++++++++++++++++++++++++++++++++" << endl;

	return 1;
}

Girl(string _name, int _age)ss
Girl(string _name, int _age)xx
Girl(string _name, int _age)wq
------------------------------------------------
Girl(string _name, int _age)ss
Girl(string _name, int _age)xx
Girl(string _name, int _age)wq
------------------------------------------------
Girl(string _name, int _age)ss
Girl(string _name, int _age)xx
Girl(string _name, int _age)wq
++++++++++++++++++++++++++++++++++++++++++++++++
operator=(const Girl&)xx
operator=(const Girl&)wq
~Girl()wq
------------------------------------------------
operator=(const Girl&)wq
~Girl()wq
++++++++++++++++++++++++++++++++++++++++++++++++
~Girl()wq
------------------------------------------------
~Girl()xx
++++++++++++++++++++++++++++++++++++++++++++++++
~Girl()wq
------------------------------------------------
~Girl()xx
++++++++++++++++++++++++++++++++++++++++++++++++
~Girl()ss
~Girl()ss
~Girl()wq

从结果中可以看到,如果从中间或开头开始删除,后面的对象由于要向前移动而要调用赋值函数,运行性能偏低。这里移动的方式要注意,它是每二个对象赋值给第一个对象,第三个对象赋值给第二个对象,第三个对象析构销毁。

四、添加新对象时应从结尾处添加,而不要从中间添加

int main() {
	vector<Girl> vvv1;
	vvv1.reserve(10);

	vvv1.emplace_back("ss", 12);

	cout << "------------------------------------------------" << endl;
	vvv1.insert(vvv1.begin(), Girl("xx", 33));
	
	cout << "------------------------------------------------" << endl;
	vvv1.insert(vvv1.begin(), Girl("wq", 123));

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

	vector<Girl> vvv2;
	vvv2.reserve(10);

	vvv2.emplace_back("ss", 12);

	cout << "------------------------------------------------" << endl;
	vvv2.emplace(vvv2.begin(), "xx", 33);

	cout << "------------------------------------------------" << endl;
	vvv2.emplace(vvv2.begin(), "wq", 123);

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


	return 1;
}

Girl(string _name, int _age)ss
------------------------------------------------
Girl(string _name, int _age)xx
Girl(Girl&&)xx
Girl(Girl&&)ss
operator=(const Girl&)xx
~Girl()xx
~Girl()
------------------------------------------------
Girl(string _name, int _age)wq
Girl(Girl&&)wq
Girl(Girl&&)ss
operator=(const Girl&)xx
operator=(const Girl&)wq
~Girl()wq
~Girl()
------------------------------------------------
------------------------------------------------
Girl(string _name, int _age)ss
------------------------------------------------
Girl(string _name, int _age)xx
Girl(Girl&&)ss
operator=(const Girl&)xx
~Girl()xx
------------------------------------------------
Girl(string _name, int _age)wq
Girl(Girl&&)ss
operator=(const Girl&)xx
operator=(const Girl&)wq
~Girl()wq
------------------------------------------------
~Girl()wq
~Girl()xx
~Girl()ss
~Girl()wq
~Girl()xx
~Girl()ss

从运行结果可以看到,无论是使用emplace还是insert,在插入位置后的对象都会发生移动,这样会影响性能。

五、使用std::vector(std::vector)和std::vector(std::initializer_list<T>)对std::vector赋初始值会将std::vector和std::initializer_list<T>中所有对象复制

int main() {

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

	vector<Girl> vv({ Girl("d",90), Girl("ll",80) });

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

	vector<Girl> vvv1;
	vvv1.reserve(10);

	vvv1.emplace_back("ss", 12);
	vvv1.emplace_back("xx", 33);

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

	vector<Girl> vvv2(vvv1);

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


	return 1;
}

------------------------------------------------
Girl(string _name, int _age)d
Girl(string _name, int _age)ll
Girl(const Girl&)d
Girl(const Girl&)ll
~Girl()ll
~Girl()d
------------------------------------------------
Girl(string _name, int _age)ss
Girl(string _name, int _age)xx
------------------------------------------------
Girl(const Girl&)ss
Girl(const Girl&)xx
------------------------------------------------
~Girl()ss
~Girl()xx
~Girl()ss
~Girl()xx
~Girl()d
~Girl()ll

通过运行结果可以看到,当初始化std::vector时,对象双被复制了一份。通过初始化的方式无法将对象在多个集合中共享。如果像让std::vector中的所有对象暴露出来,可以使用T* std::vector.data(),它会返回std::vector中所包含的对象的指针。

六、需要将对象全部转移到另外一std::vector时,应使用std::vector.swap()、std::swap()、std::move(std::vector)

int main() {
	{
		cout << "------------------------------------------------" << endl;

		vector<Girl> vvv1;
		vvv1.reserve(10);

		vvv1.emplace_back("ss", 12);
		vvv1.emplace_back("xx", 33);
		vvv1.emplace_back("wq", 123);

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

		vector<Girl> vvv2 = move(vvv1);
		cout << "------------------------------------------------" << endl;
	}
	{
		cout << "------------------------------------------------" << endl;

		vector<Girl> vvv1;
		vvv1.reserve(10);

		vvv1.emplace_back("ss", 12);
		vvv1.emplace_back("xx", 33);
		vvv1.emplace_back("wq", 123);

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

		vector<Girl> vvv2;
		vvv2.swap(vvv1);
		cout << "------------------------------------------------" << endl;
	}
	{
		cout << "------------------------------------------------" << endl;

		vector<Girl> vvv1;
		vvv1.reserve(10);

		vvv1.emplace_back("ss", 12);
		vvv1.emplace_back("xx", 33);
		vvv1.emplace_back("wq", 123);

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

		vector<Girl> vvv2;
		swap(vvv1, vvv2);
		cout << "------------------------------------------------" << endl;
	}

	return 1;
}

------------------------------------------------
Girl(string _name, int _age)ss
Girl(string _name, int _age)xx
Girl(string _name, int _age)wq
------------------------------------------------
------------------------------------------------
~Girl()ss
~Girl()xx
~Girl()wq
------------------------------------------------
Girl(string _name, int _age)ss
Girl(string _name, int _age)xx
Girl(string _name, int _age)wq
------------------------------------------------
------------------------------------------------
~Girl()ss
~Girl()xx
~Girl()wq
------------------------------------------------
Girl(string _name, int _age)ss
Girl(string _name, int _age)xx
Girl(string _name, int _age)wq
------------------------------------------------
------------------------------------------------
~Girl()ss
~Girl()xx
~Girl()wq

通过运行结果可以看到,转移的过程只是更改了指针的指向,没有发生任何复制或拷贝。

七、如果std::vector中在存放指针对象,即std::vector<T*>,则应使用智能指针

  • std::vector<std::unique_ptr<T>>

  • std::vector<std::shared_ptr<T>>

因为如果std::vector中存放指针,指针指向的对象并不受std::vector管理,所以需要智能指针帮助管理这些对象。

猜你喜欢

转载自blog.csdn.net/netyeaxi/article/details/83277810