emplace_back和push_back对比分析

  • Post author:
  • Post category:其他

emplace_back含义

emplace_back是C++11新引进的接口函数。
emplace_back是就地构造,不用构造后再次复制到容器中。因此效率更高。

push_back

简单的一个案例,将一个string对象添加到testVec中:

vector<string> testVec;
testVec.push_back(string(16, 'a'));

底层实现:

  • 首先,string(16, ‘a’)会创建一个string类型的临时对象,这涉及到一次string构造过程。
  • 其次,vector内会创建一个新的string对象,这是第二次构造。
  • 最后在push_back结束时,最开始的临时对象会被析构。

加在一起,这两行代码会涉及到两次string构造和一次析构。

emplace_back

c++11可以用emplace_back代替push_back,emplace_back可以直接在vector中构建一个对象,而非创建一个临时对象,再放进vector,再销毁。emplace_back可以省略一次构建和一次析构,从而达到优化的目的。

对比测试

首先写一个头文件time_interval.h,该头文件程序可以记录代码的运行时间。

#ifndef TIME_INTERVAL_H
#define TIME_INTERVAL_H
#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)
#endif // TIME_INTERVAL_H

写主函数

#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");// 只有一次构造函数,不调用拷贝构造函数,速度最快
        }
    }
}

运行结果:
在这里插入图片描述

  • 第1中方法耗时最长,原因显而易见,将调用左值引用的push_back,且将会调用一次string的拷贝构造函数,比较耗时,这里的string还算很短的,如果很长的话,差异会更大。
  • 第2、3、4中方法耗时基本一样,参数为右值,将调用右值引用的push_back,故调用string的移动构造函数,移动构造函数耗时比拷贝构造函数少,因为不需要重新分配内存空间。
  • 第5中方法耗时最少,因为emplace_back只调用构造函数,没有移动构造函数,也没有拷贝构造函数。

文章参考与<零声教育>的C/C++linux服务期高级架构系统教程学习


版权声明:本文为m0_70418130原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。