6. C++ emplace_back()

2024-01-07 23:07:23
将已构造好的元素插入vector的末尾

当使用push_back()时,看如下代码,先构造变量a,再调用push_back()

代码如下:

#include <iostream>
#include<vector>
using namespace std;

class testDemo
{
     public:
     testDemo(int num):num(num){
             std::cout <<"调用构造函数"<< endl;
     }
     testDemo(const testDemo& other):num(other.num){
             std::cout <<"调用拷贝构造函数"<< endl;
     }

     private:
     int num;
};

int main()
{
    vector<testDemo>  values{};
    
    testDemo a(1);
    cout << "开始push_back1 \n";
    values.push_back(a);
    cout << "结束push_back1 \n";

    return 0;
}

代码运行有如下输出:

调用构造函数
开始push_back1 
调用拷贝构造函数
结束push_back1 
Program ended with exit code: 0

从以上输出可以看到,将已构造好的元素 a 使用push_back()插入vector时,只调用了拷贝构造函数。

当使用emplace_back()时,看如下代码,先构造变量a,再调用emplace_back()

#include <iostream>
#include<vector>
using namespace std;

class testDemo
{
     public:
     testDemo(int num):num(num){
             std::cout <<"调用构造函数"<< endl;
     }
     testDemo(const testDemo& other):num(other.num){
             std::cout <<"调用拷贝构造函数"<< endl;
     }

     private:
     int num;
};

int main()
{
    vector<testDemo>values{};
    
    testDemo a(1);
    cout << "开始emplace_back1 \n";
    values.emplace_back(a);
    cout << "结束emplace_back1 \n";

    return 0;
}

代码运行有如下输出:

调用构造函数
开始emplace_back1 
调用拷贝构造函数
结束emplace_back1 
Program ended with exit code: 0

从以上输出可以看到,将已构造好的元素 a 使用emplace_back()插入vector时,只调用了 拷贝构造函数。

从上面两组实验可知,插入一个已经构造好的数据时,push_back()和emplace_back()都是调用拷贝构造函数,效率应该差不多一致。

将未构造好的元素插入vector的末尾

当使用push_back()时,看如下代码,将一个int类型的数字,作为push_back()的参数。

#include <iostream>
#include<vector>
using namespace std;

class testDemo
{
     public:
     testDemo(int num):num(num){
             std::cout <<"调用构造函数"<< endl;
     }
     testDemo(const testDemo& other):num(other.num){
             std::cout <<"调用拷贝构造函数"<< endl;
     }

     private:
     int num;
};

int main()
{
    vector<testDemo>values{};
    
    cout << "开始push_back1 \n";
    values.push_back(1);
    cout << "结束push_back1 \n";

    return 0;
}

代码运行有如下输出:

开始push_back1 
调用构造函数
调用拷贝构造函数
结束push_back1 
Program ended with exit code: 0

从以上输出可以看到,push_back的参数是数字 1 时,需要调用构造函数和拷贝构造函数。

当使用emplace_back()时,看如下代码,将一个int类型的数字,作为emplace_back()的参数。

代码如下:

#include <iostream>
#include<vector>
using namespace std;

class testDemo
{
     public:
     testDemo(int num):num(num){
             std::cout <<"调用构造函数"<< endl;
     }
     testDemo(const testDemo& other):num(other.num){
             std::cout <<"调用拷贝构造函数"<< endl;
     }

     private:
     int num;
};

int main()
{
    vector<testDemo>values{};
    
    cout << "开始emplace_back1 \n";
    values.emplace_back(1);
    cout << "结束emplace_back1 \n";

    return 0;
}

代码运行有如下输出:

开始emplace_back1 
调用构造函数
结束emplace_back1 
Program ended with exit code: 0

从以上输出可以看到,emplace_back()的参数是数字 1 时,只需要调用构造函数。而不像 push_back 要同时调用构造函数和拷贝构造函数。

所以这里就是大家通常认为 emplace_back 比 push_back的效率要高的地方。push_back 要先将参数构造成类型testDemo的,然后调用拷贝构造函数复制到vector的末尾,而 emplace_back可以根据参数直接在vector的末尾构造数据,省去了拷贝的过程。

将从源码级别去理解push_back和emplace_back

简化版本的 push_back源码:

template <typename T>
void vector<T>::push_back(const T& value) {  
//这里类型转换调用构造函数

    if (size == capacity) {
        expand_capacity();
    }
    data[size] = value;  // 这里调用 构造函数
    size++;
}

看上面的代码:

  1. 如果输入参数和类型不一致,就会发生类型转换,调用构造函数,生成临时变量。

  2. 调用拷贝构造函数将数据拷贝到vector。

简化版本的 emplace_back源码:
template <typename T>
template <typename... Args>
void vector<T>::emplace_back(Args&&... args) { // 可变参参数
    if (size == capacity) {
        expand_capacity();
    }
    
    // 使用传入的参数在vector的内存空间中直接构造一个对象, 
    // 调用构造函数
    new (&data[size]) T(std::forward<Args>(args)...);
    size++;
}

看上面的代码:

  1. 不需要创建类型T 的临时变量;

? 2. 调用构造函数,在vector中原地构造数据。

总的来说,emplace_back()和push_back()都是用于向容器的尾部插入新元素的函数。它们的最大区别在于emplace_back()直接在容器内部构造新元素,而push_back()是将一个已存在的元素的副本拷贝到容器中。

使用emplace_back()可以避免额外的元素拷贝操作,因为它直接在容器内部构造了新元素。这对于一些体积较大或有复杂构造函数的元素来说尤为重要,可以提高效率并减少资源的消耗。另外,emplace_back()还接受构造新元素所需的参数,使得代码更加简洁明了。

文章来源:https://blog.csdn.net/weixin_46645965/article/details/135354476
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。