概述
c++开发中我们会经常用到插入操作对stl的各种容器进行操作,比如vector,map,set等。在引入右值引用,转移构造函数,转移复制运算符之前,通常使用push_back()向容器中加入一个右值元素(临时对象)时,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放入容器中。原来的临时变量释放,这样造成的问题就是临时变量申请资源的浪费。
引入了右值引用,转移构造函数后,push_back()右值时就会调用构造函数和转移构造函数。如果可以在插入的时候直接构造,就只需要构造一次即可,这就是c++11 新加的emplace_back。
emplace_back()函数原型:
template <class... Args>
void emplace_back (Args&&... args);
emplace_back()在容器尾部添加一个元素,这个元素原地构造,不需要触发拷贝构造和转移构造。而且调用形式更加简洁,直接根据参数初始化临时对象的成员。
<示例代码>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
struct President
{
std::string name;
std::string country;
int year;
President(std::string p_name, std::string p_country, int p_year)
: name(std::move(p_name)), country(std::move(p_country)), year(p_year)
{
std::cout << "I am being constructed.n";
}
President(const President& other)
: name(std::move(other.name)), country(std::move(other.country)), year(other.year)
{
std::cout << "I am being copy constructed.n";
}
President(President&& other)
: name(std::move(other.name)), country(std::move(other.country)), year(other.year)
{
std::cout << "I am being moved.n";
}
President& operator=(const President& other);
};
int main()
{
std::vector<President> elections;
std::cout << "emplace_back:n";
elections.emplace_back("Nelson Mandela", "South Africa", 1994); //没有类的创建
std::vector<President> reElections;
std::cout << "npush_back:n";
reElections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936));
std::cout << "nContents:n";
for (President const& president: elections) {
std::cout << president.name << " was elected president of "
<< president.country << " in " << president.year << ".n";
}
for (President const& president: reElections) {
std::cout << president.name << " was re-elected president of "
<< president.country << " in " << president.year << ".n";
}
}
输出:
emplace_back:
I am being constructed.
push_back:
I am being constructed.
I am being moved.
Contents:
Nelson Mandela was elected president of South Africa in 1994.
Franklin Delano Roosevelt was re-elected president of the USA in 1936.
【思考】建议尽量使用emplace_back代替 push_back,但有没有什么特例是不能替换的呢?
举例在window visual studio 2015 编译下面程序会出现引用失效问题,而linux gcc 和qt 等编译环境中不会出现问题。
<引用失效示例>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
int main()
{
vector<int> ivec;
ivec.emplace_back(1);
ivec.emplace_back(ivec.back());
for (auto it = ivec.begin(); it != ivec.end(); ++it)
cout << *it << " ";
return 0;
}
//输出:
1 -572662307
<尝试解决1:不直接给emplace_back传递ivec.back()>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
int main()
{
vector<int> ivec;
ivec.emplace_back(1);
auto &it = ivec.back();
ivec.emplace_back(it);
for (auto it = ivec.begin(); it != ivec.end(); ++it)
cout << *it << " ";
return 0;
}
输出:
1 -572662307
<尝试解决2:不给emplace_back传递引用>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
int main()
{
vector<int> ivec;
ivec.emplace_back(1);
auto it = ivec.back();
ivec.emplace_back(it);
for (auto it = ivec.begin(); it != ivec.end(); ++it)
cout << *it << " ";
return 0;
}
输出:
1 1
通过方法2我们如愿以偿:ivec.back()返回的是引用,但是这个引用失效了,所以才会输出不正确;我们之前也提到过,重新分配内存会造成迭代器的失效,这里是造成了引用的失效。
if a reallocation happens, all iterators, pointers and references related to this container are invalidated. Otherwise, only the end iterator is invalidated, and all other iterators, pointers and references to elements are guaranteed to keep referring to the same elements they were referring to before the call.
<尝试解决3:避免emplace_back引起重新分配内存>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
int main()
{
vector<int> ivec;
ivec.reserve(4);
ivec.emplace_back(1);
ivec.emplace_back(ivec.back());
for (auto it = ivec.begin(); it != ivec.end(); ++it)
cout << *it << " ";
return 0;
}
输出:
1 1
最后
以上就是靓丽战斗机为你收集整理的C++程序员应了解的那些事(30)C++11 之emplace_back 与 push_back的区别的全部内容,希望文章能够帮你解决C++程序员应了解的那些事(30)C++11 之emplace_back 与 push_back的区别所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复