vector是C++中的动态数组,它的容量capacity
随着元素的增加,可以动态长。而容量每一次生长时候,意味着重新开辟一块内存,将原有的数据拷贝到新的内存上,并删除之前的数据。
基于这个特点,可以对vector
的使用进行适当的优化。
野蛮复制
下面一段代码,执行目的是要在std::vector<Student> vecs
中插入三个同学对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <iostream> #include <vector>
class Student { public: Student(std::string name, int age) { name_ = name; age_ = age; printf("construct \n"); } Student(const Student& stu) : name_(stu.name_), age_(stu.age_) { printf("copy \n"); } private: std::string name_; int age_; };
int main() { std::vector<Student> vecs; vecs.push_back(Student("小刚", 10)); printf("======================== \n"); vecs.push_back(Student("小红", 11)); printf("======================== \n"); vecs.push_back(Student("小李", 12)); std::cin.get(); }
|
打印结果
1 2 3 4 5 6 7 8 9 10 11
| construct copy ======================== construct copy copy ======================== construct copy copy copy
|
可以看到向vecs
添加三个元素,Student
的构造函数执行了3次,构造复制了6次,如果插入的学生更多,这个复制次数会野蛮生长。
reserve设置容量
根据使用场景,reserve
提前分设置容量,这样可以避免每一次capacity
自生长带来的内存分配和copy动作。
1 2 3 4 5 6 7 8 9 10
| int main() { std::vector<Student> vecs; vecs.reserve(3); vecs.push_back(Student("小刚", 10)); printf("======================== \n"); vecs.push_back(Student("小红", 11)); printf("======================== \n"); vecs.push_back(Student("小李", 12)); std::cin.get(); }
|
打印结果
1 2 3 4 5 6 7 8
| construct copy ======================== construct copy ======================== construct copy
|
emplace_back
push_back
入参是容器元素类型本身,每次添加元素会构造临时的对象,而实际添加到容器中的又是拷贝出来的那个对象。C++提供了emplace_back
接口,通过构造入参直接再容器中构造对象。
1 2 3 4 5 6 7 8 9 10
| int main() { std::vector<Student> vecs; vecs.reserve(3); vecs.emplace_back("小刚", 10); printf("======================== \n"); vecs.emplace_back("小红", 11); printf("======================== \n"); vecs.emplace_back("小李", 12); std::cin.get(); }
|
打印结果
1 2 3 4 5
| construct ======================== construct ======================== construct
|
现在看打印结果,已经没有copy
字样了,代码变得更加高效,三次代码达到了同样的目的,但是经过几次优化后,代码执行过程中不再有冗余的副本产生。