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