-
为在容器操作时尽可能的减少构造函数的调用和内存的拷贝,C++11 引入了emplace_back的方法,该方法可以改善往容器内推入对象元素时的效率。相比push_back,可以节省一次拷贝构造函数的调用从而提高插入效率; -
push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程; -
push_back()方法要调用构造函数和复制构造函数,这也就代表着要先构造一个临时对象,然后把临时的copy构造函数拷贝或者移动到容器最后面;而emplace_back()在实现时,则是直接在容器的尾部创建这个元素,省去了拷贝或移动元素的过程; -
emplace_back()在容器尾部添加一个元素,调用构造函数原地构造,不需要触发拷贝构造和移动构造。在实际使用时,优先选用 emplace_back()。 -
在调用push_back时,每次执行push_back操作,相当于底层的数组实现要重新分配大小;这种实现体现到vector上,就是每当push_back一个元素,都要重新分配一个大一个元素的存储,然后将原来的元素拷贝到新的存储,之后在拷贝push_back的元素,最后要析构原有的vector并释放原有的内存。如:v.push_back(s1); vector会申请一个内存空间,并调用拷贝构造函数将s1放到vector中; -
如果想提高效率时,也可以让容器变为指针容器,到时候直接推入指针变量即可。这样的话,每次推入的代价就由真个类的的拷贝构造函数转为了指针的拷贝,效率会明显提升。
vector<pair<int, int>> ret;
ret.push_back(1,1)//会报错,因为没有构造一个临时对象
ret.push_back(pair(1,1))//不会报错,因为构成了一个pair对象
ret.emplace_back(1,1)//不会报错,因为直接在容器的尾部创建对象
vector<vector<int>> ans;
int lastcol = INT_MIN;
for (const auto& [col, row, value]: nodes) {
if (col != lastcol) {
lastcol = col;
ans.emplace_back();//这里的emplace_back()直接在ans的尾部创建一个类型为vector<int>的空对象,如果省去这一行,后面的ans.back()会是一个空指针而报错。
}
ans.back().emplace_back(value);
}
#include<iostream>
using namespace std;
#include<string>
#include<vector>
class student {
public:
student(string s, int a) {
cout << "构造函数调用" << endl;
name = s;
a = age;
}
student(const student& s ) {
cout << "拷贝构造函数调用" << endl;
}
private:
string name;
int age;
};
int main() {
vector<student> v;
student s1("tom", 12);
v.push_back(s1); //需先创建studnet类对象,才能进行插入操作;
v.emplace_back("marry", 18); //无需先创建一个student类对象,就可以直接插入一个类对象到vector容器
//v.push_back("bob", 19); //会报错,必须先创建一个student类对象,才能进行尾插操作
return 0;
}
测试结果输出:当两个插入一起插入时,会触发未知的拷贝构造操作,就会看到两种函数插入方式都调用了拷贝构造函数;
- 单独进行 emplace_back() 操作;当多次插入操作导致vector已满时,就要分配一块更大的内存(比原始大小多50%),将原始数据复制过来并释放之前的内存。原始数据的复制就是这些拷贝构造操作。
- 测试代码
#include<iostream>
using namespace std;
#include<string>
#include<vector>
class student {
public:
student(string s, int a) {
cout << "构造函数调用" << endl;
name = s;
a = age;
}
student(const student& s ) {
cout << "拷贝构造函数调用" << endl;
}
~student() {
cout << "析构函数" << endl;
}
private:
string name;
int age=0;
};
int main() {
vector<student> v;
cout << "初始vector容器大小为:" << v.size() << endl;
v.emplace_back("marry", 18);
v.emplace_back("tom", 8); //至此vecotr容器满,才需调用拷贝构造函数
cout << "vector容器大小为:" << v.size() << endl;
v.emplace_back("xiaoming", 28);
cout << "vector容器大小为:" << v.size() << endl;
v.emplace_back("xiaoming", 8);
cout << "vector容器大小为:" << v.size() << endl;
return 0;
}
- 测试结果输出;当vector容器填满时,则调用一次拷贝构造函数,并调用析构函数释放原来内存空间;下打印的“拷贝构造函数调用”即vector满时,需要分配更大的内存并复制原始数据而产生的。并且每次分配更大内存时,都比原始大小多50%;
- vector容器:内存空间只会增长,不会减小;为了支持快速的随机访问,vector容器的元素以连续方式存放,每一个元素都紧挨着前一个元素存储。设想一下,当vector添加一个元素时,为了满足连续存放这个特性,都需要重新分配空间、拷贝元素、撤销旧空间;
|