c++ emplace_back

C++11中大部分的容器对于添加元素除了传统的 insert 或者 pusb_back/push_front 之外都提供一个新的函数叫做 emplace。 比如如果你想要向 std::vector 的末尾添加一个数据,你可以:

std::vector<int> nums;
nums.push_back(1);

你也可以使用:

std::vector<int> nums;
nums.empace_back(1);

那么这两种方式的区别到底是什么呢?

避免不必要的临时对象的产生

emplace 最大的作用是避免产生不必要的临时变量,因为它可以完成 in place 的构 造,

优势:在仅传入构造对象的参数时,直接用参数构造对象,不产生临时对象

测试:

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

class Test{
public:
  Test(int a = 0) { std::cout << "Test(int a = 0)" << std::endl; }
  ~Test() { std::cout << " ~Test()" << std::endl; }

  Test(const Test&) { std::cout << "Test(const Test&)" << std::endl; }
  Test(Test&&) { std::cout << "Test(Test&&)" << std::endl; }
};

int main(){

  Test t1;

  vector<Test>  v;
  v.reserve(100);//防止测试中扩容


  std::cout << "begin-------左值对象" << std::endl;
  /* 没有区别
begin-------左值对象
Test(const Test&)
Test(const Test&)
end----------------------
  */
  v.push_back(t1);//调用Test(const Test&)
  v.emplace_back(t1);//调用Test(const Test&)
  std::cout << "end----------------------" << std::endl;

  std::cout << "begin-------右值对象" << std::endl;
  /*  没有区别
 begin-------右值对象
Test(int a = 0)  临时对象构造
Test(Test&&)  调用右值拷贝构造函数
 ~Test()  //临时对象析构

Test(int a = 0)
Test(Test&&)
 ~Test()
end----------------------
  */
  v.push_back(Test(1));//
  v.emplace_back(Test(2));//
  std::cout << "end----------------------" << std::endl;


  std::cout << "begin-------仅仅传入构造函数的参数" << std::endl;
  /*   区别
begin-------
Test(int a = 0)   push_back 需要构造临时对象
Test(Test&&)
 ~Test()

Test(int a = 0)  emplace_back 直接使用参数构造对象
end----------------------
  */
  v.push_back(1);//
  v.emplace_back(2);//
  std::cout << "end----------------------" << std::endl;





  return 0;

}

实现

 右值引用     C++11 - 右值引用_大秦坑王的专栏-CSDN博客

函数原型

	template<class... _Valty>
		decltype(auto) emplace_back(_Valty&&... _Val)
		{	// insert by perfectly forwarding into element at end, provide strong guarantee
		if (_Has_unused_capacity())
			{
			return (_Emplace_back_with_unused_capacity(_STD forward<_Valty>(_Val)...));
			}

		_Ty& _Result = *_Emplace_reallocate(this->_Mylast(), _STD forward<_Valty>(_Val)...);
#if _HAS_CXX17
		return (_Result);
#else /* ^^^ _HAS_CXX17 ^^^ // vvv !_HAS_CXX17 vvv */
		(void)_Result;
#endif /* _HAS_CXX17 */
		}

        利用了c++ 11的新特性变长参数模板(variadic template),直接构造了一个新的对象,不需要拷贝或者移动内存,提高了效率。
        在容器尾部添加一个元素,这个元素原地构造,不需要触发拷贝构造和转移构造。而且调用形式更加简洁,直接根据参数初始化临时对象的成员。

        在c++11中,push_back 在左值引用和右值引用对象中,底层已经使用emplace_back 实现

	void push_back(const _Ty& _Val)
		{	// insert element at end, provide strong guarantee
		emplace_back(_Val);
		}

	void push_back(_Ty&& _Val)
		{	// insert by moving into element at end, provide strong guarantee
		emplace_back(_STD move(_Val));
		}

简单的实现:


  template<typename... Ty>//函数模板的类型推演 + 引用折叠
  void emplace_back(Ty && ...val)//可变参
  {
    if (full())
      expand();

    //move(左值):移动语义,得到右值类型   (int&&)a
    //forward:类型完美转发,能够识别左值和右值类型
    _allocator.construct(_last, std::forward<Ty>(val)...);
    _last++;
  }


  template<typename... Ty>
  void construct(T *p, Ty&& ... vals)
  {
    new (p) T(std::forward<Ty>(vals)...);
  }

完整代码:


#include <iostream>

//容器的空间配置器
template <typename T>
struct Allocator
{
  T* allocate(size_t size)//只负责内存开辟
  {
    return (T*)malloc(sizeof(T) * size);
  }
  void deallocate(void *p)//只负责内存释放
  {
    free(p);
  }

  template<typename... Ty>
  void construct(T *p, Ty&& ... vals)
  {
    new (p) T(std::forward<Ty>(vals)...);
  }

  void destroy(T *p)//只负责对象析构
  {
    p->~T();//~T()代表了T类型的析构函数
  }
};

template <typename T, typename Alloc = Allocator<T>>
class vector//向量容器
{
public:
  vector(int size = 10)//构造
  {
    //_first = new T[size];
    _first = _allocator.allocate(size);
    _last = _first;
    _end = _first + size;
  }

  void reserve(size_t size) {
  
  }
  ~vector()//析构
  {
    //delete[]_first;
    for (T *p = _first; p != _last; ++p)
    {
      _allocator.destroy(p);//把_first指针指向的数组的有效元素析构
    }
    _allocator.deallocate(_first);//释放堆上的数组内存
    _first = _last = _end = nullptr;
  }
  vector(const vector<T> &rhs)//拷贝构造
  {
    int size = rhs._end - rhs._first;//空间大小
    //_first = new T[size];
    _first = _allocator.allocate(size);
    int len = rhs._last - rhs._first;//有效元素
    for (int i = 0; i < len; ++i)
    {
      //_first[i] = rhs._first[i];
      _allocator.construct(_first + i, rhs._first[i]);
    }
    _last = _first + len;
    _end = _first + size;
  }
  vector<T>& operator=(const vector<T> &rhs)//赋值运算符重载
  {
    if (this == &rhs)
    {
      return *this;
    }

    //delete[]_first;
    for (T *p = _first; p != _last; ++p)
    {
      _allocator.destory(p);//把_first指针指向的数组的有效元素析构
    }
    _allocator.deallocate(_first);//释放堆上的数组内存

    int size = rhs._end - rhs._first;//空间大小
    _first = _allocator.allocate(size);
    int len = rhs._last - rhs._first;//有效元素
    for (int i = 0; i < len; ++i)
    {
      _allocator.construct(_first + i, rhs._first[i]);
    }
    _last = _first + len;
    _end = _first + size;
    return *this;
  }

  template<typename Ty>//函数模板的类型推演 + 引用折叠
  void push_back(Ty &&val)//Ty CMyString& + && = CMyString&
  {
    if (full())
      expand();

    //move(左值):移动语义,得到右值类型   (int&&)a
    //forward:类型完美转发,能够识别左值和右值类型
    _allocator.construct(_last, std::forward<Ty>(val));
    _last++;
  }

  template<typename... Ty>//函数模板的类型推演 + 引用折叠
  void emplace_back(Ty && ...val)//可变参
  {
    if (full())
      expand();

    //move(左值):移动语义,得到右值类型   (int&&)a
    //forward:类型完美转发,能够识别左值和右值类型
    _allocator.construct(_last, std::forward<Ty>(val)...);
    _last++;
  }

  void pop_back()//尾删
  {
    if (empty()) return;
    verify(_last - 1, _last);
    //erase(it); verift(it._ptr, _last);
    //insert(it,val); verift(it._ptr, _last);
    //--_last;
    //不仅要把_last指针--,还需要析构删除的元素
    --_last;
    _allocator.destroy(_last);
  }
  T back()const//返回容器末尾元素值
  {
    return *(_last - 1);
  }
  bool full()const
  {
    return _last == _end;
  }
  bool empty()const
  {
    return _first == _last;
  }
  int size()const//返回容器中元素个数
  {
    return _last - _first;
  }
  T& operator[](int index)
  {
    if (index < 0 || index >= size())
    {
      throw "OutOfRangeException";
    }
    return _first[index];
  }
  //迭代器一般实现成容器的嵌套类型
  class iterator
  {
  public:
    friend class vector <T, Alloc>;
    //新生成当前容器某一个位置元素的迭代器
    iterator(vector<T, Alloc> *pvec = nullptr
      , T *ptr = nullptr)
      :_ptr(ptr), _pVec(pvec)
    {
      Iterator_Base *itb = new Iterator_Base(this, _pVec->_head._next);
      _pVec->_head._next = itb;
    }
    bool operator!=(const iterator &it)const
    {
      //检查迭代器的有效性
      if (_pVec == nullptr || _pVec != it._pVec)//迭代器为空或迭代两个不同容器
      {
        throw "iterator incompatable!";
      }
      return _ptr != it._ptr;
    }
    void operator++()
    {
      //检查迭代器有效性
      if (_pVec == nullptr)
      {
        throw "iterator incalid!";
      }
      _ptr++;
    }
    T& operator*()
    {
      //检查迭代器有效性
      if (_pVec == nullptr)
      {
        throw "iterator invalid!";
      }
      return *_ptr;
    }
    const T& operator*()const
    {
      if (_pVec == nullptr)
      {
        throw "iterator invalid!";
      }
      return *_ptr;
    }
  private:
    T *_ptr;
    //当前迭代器是哪个容器对象
    vector<T, Alloc> *_pVec;//指向当前对象容器的指针
  };
  iterator begin()
  {
    return iterator(this, _first);
  }
  iterator end()
  {
    return iterator(this, _last);
  }
  //检查迭代器失效
  void verify(T *first, T *last)
  {
    Iterator_Base *pre = &this->_head;
    Iterator_Base *it = this->_head._next;
    while (it != nullptr)
    {
      if (it->_cur->_ptr > first && it->_cur->_ptr <= last)
      {
        //迭代器失效,把iterator持有的容器指针置nullptr
        it->_cur->_pVec = nullptr;
        //删除当前迭代器节点,继续判断后面的迭代器节点是否失效
        pre->_next = it->_next;
        delete it;
        it = pre->_next;
      }
      else
      {
        pre = it;
        it = it->_next;
      }
    }
  }

  //自定义vector容器insert方法实现
  iterator insert(iterator it, const T &val)
  {
    //1.这里我们未考虑扩容
    //2.还未考虑it._ptr指针合法性,假设它合法
    verify(it._ptr - 1, _last);
    T *p = _last;
    while (p > it._ptr)
    {
      _allocator.construct(p, *(p - 1));
      _allocator.destroy(p - 1);
      p--;
    }
    _allocator.construct(p, val);
    _last++;
    return iterator(this, p);
  }

  //自定义vector容器erase方法实现
  iterator erase(iterator it)
  {
    verify(it._ptr - 1, _last);
    T *p = it._ptr;
    while (p < _last - 1)
    {
      _allocator.destroy(p);
      _allocator.construct(p, *(p + 1));
      p++;
    }
    _allocator.destroy(p);
    _last--;
    return iterator(this, it._ptr);
  }
private:
  T *_first;//起始数组位置
  T *_last;//指向最后一个有效元素后继位置
  T *_end;//指向数组空间的后继位置
  Alloc _allocator;//定义容器的空间配置器对象

  //容器迭代器失效增加代码
  struct Iterator_Base
  {
    Iterator_Base(iterator *c = nullptr, Iterator_Base *n = nullptr)
      :_cur(c), _next(n) {}
    iterator *_cur;
    Iterator_Base *_next;
  };
  Iterator_Base _head;

  void expand()//扩容
  {
    int size = _end - _first;
    //T *ptmp = new T[2*size];
    T *ptmp = _allocator.allocate(2 * size);
    for (int i = 0; i < size; ++i)
    {
      _allocator.construct(ptmp + i, _first[i]);
      //ptmp[i] = _first[i];
    }
    //delete[]_first;
    for (T *p = _first; p != _last; ++p)
    {
      _allocator.destroy(p);
    }
    _allocator.deallocate(_first);
    _first = ptmp;
    _last = _first + size;
    _end = _first + 2 * size;
  }
};

class Test {
public:
  Test(int a ) { std::cout << "Test(int a = 0)" << std::endl; }
  Test(int a,int b ) { std::cout << " Test(int a = 0,int b=0) " << std::endl; }
  ~Test() { std::cout << " ~Test()" << std::endl; }

  Test(const Test&) { std::cout << "Test(const Test&)" << std::endl; }
  Test(Test&&) { std::cout << "Test(Test&&)" << std::endl; }
};

int main() {

  Test t1(1);

  vector<Test>  v;
  v.reserve(100);//防止测试中扩容


  std::cout << "begin-------左值对象" << std::endl;
  /* 没有区别
begin-------左值对象
Test(const Test&)
Test(const Test&)
end----------------------
  */
  v.push_back(t1);//调用Test(const Test&)
  v.emplace_back(t1);//调用Test(const Test&)
  std::cout << "end----------------------" << std::endl;

  std::cout << "begin-------右值对象" << std::endl;
  /*  没有区别
 begin-------右值对象
Test(int a = 0)  临时对象构造
Test(Test&&)  调用右值拷贝构造函数
 ~Test()  //临时对象析构

Test(int a = 0)
Test(Test&&)
 ~Test()
end----------------------
  */
  v.push_back(Test(1));//
  v.emplace_back(Test(2));//
  std::cout << "end----------------------" << std::endl;


  std::cout << "begin-------仅仅传入构造函数的参数" << std::endl;
  /*   区别
begin-------
Test(int a = 0)   push_back 需要构造临时对象
Test(Test&&)
 ~Test()

Test(int a = 0)  emplace_back 直接使用参数构造对象
end----------------------
  */
  v.push_back(1);//
//  v.push_back(1,2);//
  v.emplace_back(2);//
  v.emplace_back(2,3);//
  std::cout << "end----------------------" << std::endl;
  return 0;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值