push_back和emplace_back的区别

环境

VS2017,C++14

push_back应该是emplace_back的子集

不管是左值还是右值,push_back都调用的emplace_back。所以平时就用emplace_back也没毛病。

多变量的时候,只能是emplace_back

emplace版本会先进行分配内存,然后用placement new在给定的地址上进行调用构造函数

struct X {
	int i;
	int j;
public:
	X()// 默认构造函数
	{
		std::cout << "X()" << std::endl;
	}
	X(int _i, int _j)// 默认构造函数
	{
		i = _i;
		j = _j;
		std::cout << "X(int _i, int _j)" << std::endl;
	}
	X(const X& rhs)// 复制构造函数
	{
		i = rhs.i;
		j = rhs.j;
		std::cout << "X(const X& rhs)" << std::endl;
	}                   
	~X()// 析构函数
	{
		std::cout << "~X()" << std::endl;
		std::cout << this << std::endl;
	}
	X& operator=(const X& rhs) // 复制赋值运算符
	{
		i = rhs.i;
		j = rhs.j;
		std::cout << "X& operator=(const X& rhs)" << std::endl;
		return *this;
	}

	// C++11新增
	X(X&& rhs)              // 移动构造函数
	{
		i = rhs.i;
		j = rhs.j;
		std::cout << "X(X&& rhs)" << std::endl;
	}
	X& operator=(X&& rhs)       // 移动赋值运算符
	{
		i = rhs.i;
		j = rhs.j;
		std::cout << "X& operator=(X&& rhs)" << std::endl;
	}
};


int main()
{
	std::vector<X> v0;
	//X x1;
	v0.emplace_back(1, 2);
	return 1;
}

 C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\include\xmemory0

 placement new,相当于
new (ptr) Object(1,1)

ptr是分配好的一个地址。

参考:

char buf[100];
new(buf) X(1,1);

C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\include\xmemory0

push_back左值的时候

std::vector<X> v0;
X x1(1,2);
v0.push_back(x1);
return 1;

依然会走到:

调用的构造函数是左值的拷贝构造函数

X(const X& rhs)// 复制构造函数
	{
		i = rhs.i;
		j = rhs.j;
		std::cout << "X(const X& rhs)" << std::endl;
	}   

这个construct是个模板,可以接受可变参数列表,所以不管是左值、右值,还是多个参数,都直接forward给构造函数,所以真的威力十足。

push_back左值的时候

可以想象的出,如果push_back的是右值,最后会调用X的右值构造函数。

再看个含隐式转换的例子,看出区别了

    #include <vector> 
    #include <iostream> 
    using namespace std;
    class testDemo
    {
    public:
        testDemo(int num):num(num){
            std::cout << "调用构造函数" << endl;
        }
        testDemo(const testDemo& other) :num(other.num) {
            std::cout << "调用拷贝构造函数" << endl;
        }
        testDemo(testDemo&& other) :num(other.num) {
            std::cout << "调用移动构造函数" << endl;
        }
    private:
        int num;
    };
    int main()
    {
        cout << "emplace_back:" << endl;
        std::vector<testDemo> demo1;
        demo1.emplace_back(2);  
        cout << "push_back:" << endl;
        std::vector<testDemo> demo2;
        demo2.push_back(2);
    }

输出:
emplace_back:
调用构造函数
push_back:
调用构造函数
调用移动构造函数

结论

首先呢,push_back在处理左值和右值时,都是转发到emplace_back。

之前所说的push_back不如emplace_back效率高,应该是在下面的情况:

testDemo(int num):num(num){
            std::cout << "调用构造函数" << endl;
        }

demo2.push_back(2);

以及

X(int _i, int _j)// 默认构造函数
	{
		i = _i;
		j = _j;
		std::cout << "X(int _i, int _j)" << std::endl;
	}

v0.emplace_back(1, 2);

由于没有使用explicit,在push_back(2)的时候,这里面包含了一个隐式转换,生成了一个临时的testDemo对象。这个是push_back和emplace_back不同的地方,其余的都相同。

这个时候,调用的是:

所以,会有两个步骤,一个是生成临时对象,一个是将这个临时对象move到了vector的末尾

在变参数的时候,emplace_back会先分配内存,然后用placement new在指定的地址处调用构造函数进行初始化。省去了生成临时对象这一步骤。

所以,如果是往vector里添加左值或者右值,push_back和emplace_back是一样的。如果想直接用参数,或者会带隐式转换的,那就一定要用emplace_back。

最后的结论,用emplace_back,准没错。

另外,还有一点。不管是push_back还是emplace_back,最后调用的都是构造函数(或者拷贝构造函数),因为是用的placement new,而不是operator = (...)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值