emplace_back深度剖析

一,emplace_back和push_back

1,直接插入对象:emplace_back和push_back无区别

①当传递已经存在的对象时,是无区别的

#include <iostream>
#include <vector>

using namespace std;


/*
C++11  STL   容器    push/insert  =>  emplace方法
*/


class Test
{
public:
    Test(int a) {cout << "Test(int)" << endl;}
    ~Test() {cout << "~Test()" << endl;}
    Test(int a, int b) {cout << "Test(int, int)" << endl;}
    Test(const Test&)  {cout << "Test(const Test&)" << endl;}
    Test(Test &&)   {cout << "Test(Test &&)" << endl;}
private:
};


int main()
{
    Test t1(10);
    vector<Test> v;
    v.reserve(100);

    cout << "---------" << endl;
    // 直接插入对象,两个是没有区别的
    v.push_back(t1);
    v.emplace_back(t1);

    cout << "---------" << endl;

    return 0;
}

打印结果如下:

从以上打印结果可知。当直接插入已经存在对象时,emplace_back和push_back两个方法都是调用了Test的左值引用的拷贝构造函数,两者无区别。

②当传递的是临时对象时,也是没有区别:

#include <iostream>
#include <vector>

using namespace std;


/*
C++11  STL   容器    push/insert  =>  emplace方法
*/


class Test
{
public:
    Test(int a) {cout << "Test(int)" << endl;}
    ~Test() {cout << "~Test()" << endl;}
    Test(int a, int b) {cout << "Test(int, int)" << endl;}
    Test(const Test&)  {cout << "Test(const Test&)" << endl;}
    Test(Test &&)   {cout << "Test(Test &&)" << endl;}
private:
};


int main()
{
    vector<Test> v;
    v.reserve(100);

    cout << "---------" << endl;
    // 直接插入对象,两个是没有区别的
    v.push_back(Test(10));
    v.emplace_back(Test(10));

    cout << "---------" << endl;

    return 0;
}

打印结果如下:

从以上打印结果可知。当直接插入临时对象时,emplace_back和push_back两个方法都是调用了Test的右值引用的拷贝构造函数,都做了资源转移,两者无区别。

2,当直接传递构造函数所需的实参时,emplace_back 比push_back更加高效

考虑如下代码:

#include <iostream>
#include <vector>
#include <map>

using namespace std;

class Test
{
public:
    Test(int a) {cout << "Test(int)" << endl;}
    ~Test() {cout << "~Test()" << endl;}
    Test(int a, int b) {cout << "Test(int, int)" << endl;}
    Test(const Test&)  {cout << "Test(const Test&)" << endl;}
    Test(Test &&)   {cout << "Test(Test &&)" << endl;}
private:
};


int main()
{
    vector<Test> v;
    v.reserve(100);

    cout << "---------" << endl;
    // 给 emplace传入Test对象构造所需要的参数,直接在容器底层构造对象即可
    v.push_back(20);
    cout << "---------" << endl;
    v.emplace_back(20);

    cout << "---------" << endl;

    return 0;
}

打印结果:

从打印结果可知:如果是直接传递构造函数所需的参数下,push_back()首先是调用Test(int)创建了一个临时对象,然后调用右值引用拷贝构造传递给vector,再析构该临时对象。但是使用emplace_back(20),则是直接在容器底层构造对象即可。可见直接传递构造函数所需的实参时,emplace_back 比push_back更加高效。

同样的在其它容器中也一样,例如在map中:

    map<int, string> m;
    m.insert(make_pair(10, "zhang san"));

    m.emplace(10, "zhang san");     //在map底层直接调用普通构造函数,生成一个pair对象

如果是调用map的insert构造一个临时的pair再去传递给map,效率很低。可以使用emplace,直接传递所需的实参,则会在map底层直接调用构造函数,生成pair对象,不会产生临时对象。 

二,简单实现emplace_back 

1,简易vector

template<typename T, typename Alloc = MyAlloccator<T>>
class vector
{
public:
    vector() :  vec_(nullptr), size_(0), idx_(0) {}
    //预留内存空间
    void reserve(size_t size)
    {
        vec_ = allocator_.allocate(size);
        size_ = size;
    }

    //push_back
    template<typename Type>
    void push_back(Type&& val)
    {
        allocator_.construct(vec_ + idx_, std::forward<Type>(val));
    }


    // 1.引用折叠
    template<typename... Types>
    void emplace_back(Types&&... args)
    {
        //不管是左值引用,右值引用变量,它本身是左值。传递
        //的过程中,要保持args的引用类型(左值? 右值?)  类型的完美转发
        allocator_.construct(vec_ + idx_, std::forward<Types>(args)...);
        idx_++;
    }

private:
    T* vec_;
    int size_;
    int idx_;
    Alloc allocator_;
};

可以看出:emplace_back(Types&&... args)的形参使用的是可变参模板,为调用内存适配器allocator_的construct方法能够保持左值右值特性,使用forward保证此特性。

2,简易容器的空间适配器

//实现容器的空间配置器
template<typename T>
struct MyAlloccator
{
    // allocate deallocate
    // construct destory
    T* allocate(size_t size)
    {
        return (T*)malloc(size * sizeof(T));
    }

    template<typename... Types>
    void construct(T* ptr, Types&&... args)
    {
        //args只是一个参数,而且是Test对象,T也是一个Test类型
        new (ptr) T(std::forward<Types>(args)...);
    }
};

主要分析:void construct(T* ptr, Types&&... args),在ptr指针指向的空间上创建对象,为保证传递给创建对象的构造函数参数与传入construct的实参类型相同,使用forward完美转发。

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
emplace_back()是C++中的一个容器函数,用于在容器的末尾插入一个新的元素。它可以接受任意数量的参数,并将这些参数传递给元素的构造函数,以在容器中直接构造新的元素。与push_back()函数相比,emplace_back()函数的优势在于它避免了额外的拷贝或移动操作,因为它可以直接在容器的内存空间中构造新的元素。 使用emplace_back()的语法如下: ``` container.emplace_back(args); ``` 其中,container是一个容器对象,args是用于构造新元素的参数。可以通过传递适当的参数来满足元素构造函数的要求。 需要注意的是,emplace_back()函数适用于直接构造对象,而push_back()函数适用于将已经存在的对象添加到容器中。对于直接构造对象的情况,使用emplace_back()可以提高性能和效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [谈谈c++11 emplace_back](https://blog.csdn.net/weixin_45880571/article/details/119450328)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatgptT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [emplace_back深度剖析](https://blog.csdn.net/weixin_45477737/article/details/129348431)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatgptT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值