emplace_back 和 push_back 的差别
有一个类Test定义如下
class Test
{
public:
Test(int a) { cout << "Test(int a)" << endl; }
Test(int a, int b) { cout << "Test(int a, int b)" << endl; }
~Test() { cout << "~Test()" << endl; }
Test(const Test &) { cout << "Test(const Test &)" << endl; }
Test(Test &&) { cout << "Test(Test &&)" << endl; }
};
case1:直接插入对象,emplace_back 和 push_back 没有区别
Test t1(10);
vector<Test> v;
v.reserve(100);
v.push_back(t1);
v.emplace_back(t1);
输出结果:
Test(const Test &)
Test(const Test &)
case2:插入临时对象
都是先调用构造创建临时对象,再调用移动构造(接受右值)
若没有移动构造则调用拷贝构造
v.push_back(Test(20));
v.emplace_back(Test(20));
输出结果
Test(int a)
Test(Test &&)
~Test()
Test(int a)
Test(Test &&)
~Test()
case3:直接传入构造函数的所需参数,进行隐式类型转换调用
v.push_back(20);
cout << "============" << endl;
v.emplace_back(20);
输出结果
Test(int a)
Test(Test &&)
~Test()
============
Test(int a)
分析
push_back
先调用构造创建临时对象(隐式类型转换,因为没有接受一个int的push_back
重载函数),再调用移动构造;
emplace_back
只调用了构造函数;
可以看出节省了临时对象的析构以及移动(拷贝)构造函数的调用;
相同的还有map
的insert
该换为 `emplace方法也可以节省一次移动构造(拷贝构造的开销)
简单实现 vector 的 emplace_back
核心思路(重要)
实现这一省去临时对象创建和拷贝调用的方法可以提供一个接受构造函数参数类型变量的 emplace_back
,让 emplace_back
拿这这些参数直接去调用对应的构造函数。
push_back
之所以会产生临时对象创建和拷贝调用,是因为其只有接受容器对象类型的参数的重载版本,传递对象构造函数的参数时。要先隐式转换就会调用构造产生临时对象再调用拷贝。
引用折叠使得引用类型形参可接受左值和右值,再利用完美转发使得左值引用和右值引用的函数能合并到一个函数中
完整代码:
template<typename T>
struct MyAllocator
{
T* allocate(size_t size)
{
return (T*)malloc(size * sizeof(T));
}
template<typename... Types>
void construct(T *ptr,Types&&... args)
{
new (ptr) T(std::forward<Types>(args)...);
}
};
template<typename T, typename Alloc = MyAllocator<T>>
class Vector
{
public:
Vector() : vec_(nullptr), size_(0), idx_(0) {}
void reserve(size_t size)
{
vec_ = allocator_.allocate(size);
size_ = size;
}
void push_back(const T& val)
{
allocator_.construct(vec_ + idx_, val);
idx_++;
}
void push_back(T &&val)
{
allocator_.construct(vec_ + idx_, std::move(val));
idx_++;
}
template<typename... Types>
void emplace_back(Types&&... args)
{
allocator_.construct(vec_ + idx_, std::forward<Types>(args)...);
idx_++;
}
private:
T* vec_;
int size_;
int idx_;
Alloc allocator_;
};
上面代码重点关注push_back
和 emplace_back
的写法
测试代码
class Test
{
public:
Test(int a) { cout << "Test(int a)" << endl; }
Test(int a, int b) { cout << "Test(int a, int b)" << endl; }
~Test() { cout << "~Test()" << endl; }
Test(const Test &) { cout << "Test(const Test &)" << endl; }
Test(Test &&) { cout << "Test(Test &&)" << endl; }
};
int main()
{
Test t1(10);
Vector<Test> v;
v.reserve(100);
cout << "============" << endl;
v.push_back(t1);
v.emplace_back(t1);
cout << "============" << endl;
v.push_back(Test(20));
v.emplace_back(Test(20));
cout << "============" << endl;
v.push_back(20);
v.emplace_back(20);
cout << "============" << endl;
return 0;
}
输出结果和STL的vector的效果相同:
Test(int a)
============
Test(const Test &)
Test(const Test &)
============
Test(int a)
Test(Test &&)
~Test()
Test(int a)
Test(Test &&)
~Test()
============
Test(int a)
Test(Test &&)
~Test()
Test(int a)
============
~Test()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)