IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> vector中emplace_back和push_back详解,源码解读 -> 正文阅读

[C++知识库]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;
}
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-10 22:13:22  更:2022-03-10 22:14:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 5:04:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码