vector中emplace_back和push_back详解,源码解读

C++11之前
通常使用push_back()向容器中加入一个右值元素(临时对象)的时候,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放入容器中。原来的临时变量释放。这样造成的问题是临时变量申请的资源就浪费。
C++11之后
引入了右值引用,转移构造函数后,push_back()右值时就会调用构造函数和转移构造函数。

emplace_back
源码:主要看有注释的地方,通过完美转发,最终到构造对象,找到对应的构造函数进行构造

	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 */
		}

template<class... _Valty>
		pointer _Emplace_reallocate(const pointer _Whereptr, _Valty&&... _Val)
		{	// reallocate and insert by perfectly forwarding _Val at _Whereptr
			// pre: !_Has_unused_capacity()
		const size_type _Whereoff = static_cast<size_type>(_Whereptr - this->_Myfirst());
		_Alty& _Al = this->_Getal();
		const size_type _Oldsize = size();

		if (_Oldsize == max_size())
			{
			_Xlength();
			}

		const size_type _Newsize = _Oldsize + 1;
		const size_type _Newcapacity = _Calculate_growth(_Newsize);

		const pointer _Newvec = _Al.allocate(_Newcapacity);
		const pointer _Constructed_last = _Newvec + _Whereoff + 1;
		pointer _Constructed_first = _Constructed_last;

		_TRY_BEGIN
		// 接收完美转发后的参数,根据参数构造对象
		_Alty_traits::construct(_Al, _Unfancy(_Newvec + _Whereoff), _STD forward<_Valty>(_Val)...);
		_Constructed_first = _Newvec + _Whereoff;

		if (_Whereptr == this->_Mylast())
			{	// at back, provide strong guarantee
			_Umove_if_noexcept(this->_Myfirst(), this->_Mylast(), _Newvec);
			}
		else
			{	// provide basic guarantee
			_Umove(this->_Myfirst(), _Whereptr, _Newvec);
			_Constructed_first = _Newvec;
			_Umove(_Whereptr, this->_Mylast(), _Newvec + _Whereoff + 1);
			}
		_CATCH_ALL
		_Destroy(_Constructed_first, _Constructed_last);
		_Al.deallocate(_Newvec, _Newcapacity);
		_RERAISE;
		_CATCH_END

		_Change_array(_Newvec, _Newsize, _Newcapacity);
		return (this->_Myfirst() + _Whereoff);
		}

	template<class _Objty,
		class... _Types>
		static void construct(_Alloc&, _Objty * const _Ptr, _Types&&... _Args)
		{	// construct _Objty(_Types...) at _Ptr
		//最终传入的参数类型在这里使用,根据完美转发规则,传入的时什么类型参数,然后匹配其对象对应的构造函数进行对象构造。如果是一系列参数,就调用自定义的构造函数,如果是左值,就调用拷贝构造,如果是右值,就调用移动构造。若没有移动构造,就调用拷贝构造
		::new (const_cast<void *>(static_cast<const volatile void *>(_Ptr)))
			_Objty(_STD forward<_Types>(_Args)...);
		}

结论:

  • 对于传入的是对象构造参数(右值),empalce_pack内部是使用的完美转发,在构造对象时是进行原地构造,直接调用对象的构造函数进行构造,并存入容器中,减少push_back的临时对象构造和析构过程。
  • 对于传入的是对象(左值),empalce_pack内部是使用的完美转发,在构造对象时调用其拷贝构造函数进行构造并存入容器中
  • 对于传入的时对象右值(类似std::move()转换后的对象),empalce_pack内部是使用的完美转发,在构造对象时调用其移动构造函数进行构造并存入容器中。若没有移动构造,则调用拷贝构造创建对象并存入容器中。

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));
		}

结论:

  • 传入的是左值,根据emplace_back的完美转发原则,会调用拷贝构造函数进行元素添加
  • 传入的是右值,同理根据完美转发原则,会调用移动构造函数进行构造,如果没有移动构造,则调用拷贝构造函数进行构造,并进行元素添加

代码测试:

#include <iostream>
#include <vector>
#include <string>
struct Persion
{
  std::string name;
  std::string country;
  int year;

  Persion(std::string p_name, std::string p_country, int p_year)
    : name(std::move(p_name)), country(std::move(p_country)), year(p_year)
  {
    std::cout << "I am being constructed.\n";
  }
  Persion(const Persion& other)
    : name(std::move(other.name)), country(std::move(other.country)), year(other.year)
  {
    std::cout << "I am being copy constructed.\n";
  }
  Persion(Persion&& other)
    : name(std::move(other.name)), country(std::move(other.country)), year(other.year)
  {
    std::cout << "I am being moved.\n";
  }
  Persion& operator=(const Persion& other);
};

int main() {
  std::vector<Persion> elections;
  std::cout << "emplace_back:\n";
  elections.emplace_back("person1", "South Africa", 1991); //没有类的创建  

  std::vector<Persion> reElections;
  std::cout << "\npush_back:\n";
  Persion test("person12", "the USA", 1992);
  reElections.push_back(test);
  reElections.push_back(std::move(test));
  system("pause");
  return 0;
}
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值