概述
为了在容器操作时尽可能的减少构造函数的调用和内存的拷贝,C++11 引入了emplace_back的方法,该方法可以改善往容器内推入元素对象时的效率。相比push_back, push_front等成员函数,它可以节省一次拷贝构造函数的调用从而提高插入效率。
代码演示
#include <iostream>
#include <string>
#include <vector>
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";
}
}
执行结果
g++ emplace_back_push_back.cc --std=c++11
guanyunfei@ivan:~/practice/c++$ ./a.out
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成员函数的时候少了一次拷贝构造函数或者移动构造函数。但是细心的朋友可以发现,emplace_back()成员函数调用的时候我们是直接用类的参数列表作为形参的(push_back不能这么使用,它必须接收现成的对象或者对象在指针),所以确实可以少一次拷贝构造函数。那么问题来了,如果我们要把一个已存在的对象推入容器时,会不会调用呢?
代码展示
int main()
{
std::vector<President> elections;
std::cout << "emplace_back:\n";
//elections.emplace_back("Nelson Mandela", "South Africa", 1994); //没有类的创建
//elections.emplace_back("Nelson Mandela", "South Africa", 1994); //没有类的创建
President Nelson("Nelson Mandela", "South Africa", 1994);
elections.emplace_back(Nelson); //拷贝构造函数
std::cout << "\nemplace_back2:\n";
elections.emplace_back(President("Joseph Robinette Biden", "US", 2021)); //构造,移动构造,拷贝构造函数
std::vector<President> reElections;
std::cout << "\npush_back:\n";
President Trump("Donald Trump", "US", 2016);
reElections.push_back(Trump);
std::cout << "\npush_back2:\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.
I am being copy constructed.
emplace_back2:
I am being constructed.
I am being moved.
I am being copy constructed.
push_back:
I am being constructed.
I am being copy constructed.
push_back2:
I am being constructed.
I am being moved.
I am being copy constructed.
Contents:
Nelson Mandela was elected president of South Africa in 1994.
Joseph Robinette Biden was elected president of US in 2021.
Donald Trump was re-elected president of US in 2016.
Franklin Delano Roosevelt was re-elected president of the USA in 1936.
结果分析
从结果可以看出当我们推入一个已经存在的对象时push_back()和emplace_back()的表现完全一样都要去调用拷贝构造函数。当让临时对象作为形参推入容器时两者表现也完全一样,都要调用拷贝,移动,拷贝构造函数。
总结
emplace_back()之所以可能提高效率时因为其提供了可以直接输入类参数列表的功能,可以直接在容器内部新建这个对象。而push_back(),push_front()等函数不具备这个凡尔赛功能。
当然如果我们想提高效率时,也可以让容器变为指针容器,到时候直接推入指针变量即可。这样的话,每次推入的代价就由真个类的的拷贝构造函数转为了指针的拷贝,效率会明显提升。
网友评论