C++11使用emplace_back代替push_back

简介: 最近在写一段代码的时候,突然很好奇C++11中对push_back有没有什么改进以增加效率,上网搜了一些资料,发现果然新增了emplace_back方法,比push_back的效率要高很多。 首先,写了一个类用于计时, //time_interval.

最近在写一段代码的时候,突然很好奇C++11中对push_back有没有什么改进以增加效率,上网搜了一些资料,发现果然新增了emplace_back方法,比push_back的效率要高很多。

首先,写了一个类用于计时,

//time_interval.h
#pragma once

#include <iostream>
#include <memory>
#include <string> #ifdef GCC #include <sys/time.h> #else #include <ctime> #endif // GCC class TimeInterval { public: TimeInterval(const std::string& d) : detail(d) { init(); } TimeInterval() { init(); } ~TimeInterval() { #ifdef GCC gettimeofday(&end, NULL); std::cout << detail << 1000 * (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec) / 1000 << " ms" << endl; #else end = clock(); std::cout << detail << (double)(end - start) << " ms" << std::endl; #endif // GCC } protected: void init() { #ifdef GCC gettimeofday(&start, NULL); #else start = clock(); #endif // GCC } private: std::string detail; #ifdef GCC timeval start, end; #else clock_t start, end; #endif // GCC }; #define TIME_INTERVAL_SCOPE(d) std::shared_ptr<TimeInterval> time_interval_scope_begin = std::make_shared<TimeInterval>(d)

 

使用方法就是在作用域中使用宏TIME_INTERVAL_SCOPE(d),d为打印用的字符串,输出作用域的耗时情况。

其次,看一下现在push到vector的5种方法的耗时情况对比:

#include <vector>
#include <string>
#include "time_interval.h"

int main() {


    std::vector<std::string> v; int count = 10000000; v.reserve(count); //预分配十万大小,排除掉分配内存的时间 { TIME_INTERVAL_SCOPE("push_back string:"); for (int i = 0; i < count; i++) { std::string temp("ceshi"); v.push_back(temp);// push_back(const string&),参数是左值引用 } } v.clear(); { TIME_INTERVAL_SCOPE("push_back move(string):"); for (int i = 0; i < count; i++) { std::string temp("ceshi"); v.push_back(std::move(temp));// push_back(string &&), 参数是右值引用 } } v.clear(); { TIME_INTERVAL_SCOPE("push_back(string):"); for (int i = 0; i < count; i++) { v.push_back(std::string("ceshi"));// push_back(string &&), 参数是右值引用 } } v.clear(); { TIME_INTERVAL_SCOPE("push_back(c string):"); for (int i = 0; i < count; i++) { v.push_back("ceshi");// push_back(string &&), 参数是右值引用 } } v.clear(); { TIME_INTERVAL_SCOPE("emplace_back(c string):"); for (int i = 0; i < count; i++) { v.emplace_back("ceshi");// 只有一次构造函数,不调用拷贝构造函数,速度最快 } } }

 

vs2015 release下编译,运行结果:

push_back string:327 ms 
push_back move(string):213 ms 
push_back(string):229 ms 
push_back(c string):215 ms 
emplace_back(c string):122 ms

第1中方法耗时最长,原因显而易见,将调用左值引用的push_back,且将会调用一次string的拷贝构造函数,比较耗时,这里的string还算很短的,如果很长的话,差异会更大

第2、3、4中方法耗时基本一样,参数为右值,将调用右值引用的push_back,故调用string的移动构造函数,移动构造函数耗时比拷贝构造函数少,因为不需要重新分配内存空间。

第5中方法耗时最少,因为emplace_back只调用构造函数,没有移动构造函数,也没有拷贝构造函数。

为了证实上述论断,我们自定义一个类,并在普通构造函数、拷贝构造函数、移动构造函数中打印相应描述:

#include <vector>
#include <string>
#include "time_interval.h"

class Foo {
public: Foo(std::string str) : name(str) { std::cout << "constructor" << std::endl; } Foo(const Foo& f) : name(f.name) { std::cout << "copy constructor" << std::endl; } Foo(Foo&& f) : name(std::move(f.name)){ std::cout << "move constructor" << std::endl; } private: std::string name; }; int main() { std::vector<Foo> v; int count = 10000000; v.reserve(count); //预分配十万大小,排除掉分配内存的时间 { TIME_INTERVAL_SCOPE("push_back T:"); Foo temp("ceshi"); v.push_back(temp);// push_back(const T&),参数是左值引用 //打印结果: //constructor //copy constructor } v.clear(); { TIME_INTERVAL_SCOPE("push_back move(T):"); Foo temp("ceshi"); v.push_back(std::move(temp));// push_back(T &&), 参数是右值引用 //打印结果: //constructor //move constructor } v.clear(); { TIME_INTERVAL_SCOPE("push_back(T&&):"); v.push_back(Foo("ceshi"));// push_back(T &&), 参数是右值引用 //打印结果: //constructor //move constructor } v.clear(); { std::string temp = "ceshi"; TIME_INTERVAL_SCOPE("push_back(string):"); v.push_back(temp);// push_back(T &&), 参数是右值引用 //打印结果: //constructor //move constructor } v.clear(); { std::string temp = "ceshi"; TIME_INTERVAL_SCOPE("emplace_back(string):"); v.emplace_back(temp);// 只有一次构造函数,不调用拷贝构造函数,速度最快 //打印结果: //constructor } }

 

结论:在C++11情况下,果断用emplace_back代替push_back

目录
相关文章
|
6月前
|
编译器 程序员 C++
【C/C++ 容器操作】C++高效编程:掌握emplace_back与push_back的使用和机制
【C/C++ 容器操作】C++高效编程:掌握emplace_back与push_back的使用和机制
154 0
|
存储 Cloud Native Linux
C++ 表驱动方法代替if-else
C++ 表驱动方法代替if-else
|
存储 C++ 容器
C++中push_back和emplace_back的区别
在 `C++11` 之后,`vector` 容器中添加了新的方法:`emplace_back()` ,和 `push_back()` 一样的是都是在容器末尾添加一个新的元素进去,不同的是 `emplace_back()` 在效率上相比较于 `push_back()` 有了一定的提升。
887 0
C++中push_back和emplace_back的区别
|
C++ 容器 程序员
C++雾中风景9:emplace_back与可变长模板
C++11的版本在vector容器添加了emplace_back方法,相对于原先的push_back方法能够在一定程度上提升vector容器的表现性能。
1521 0
|
网络协议 数据安全/隐私保护 C语言
|
6天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
29 4
|
7天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
23 4