move移动语义和forward完美转发
大约 4 分钟
move移动语义和forward完美转发
vector使用右值的拷贝构造函数,可以直接用临时对象拷贝构造,直接将资源移动过来。
//容器空间适配器
template<typename T>
struct Allocator
{
T *allocate(size_t size)//负责内存的开辟
{
return (T*)malloc(sizeof(T)*size);
}
void deallocate(void *p)//负责内存的释放
{
free(p);
}
void construct(T *p, const T &val)//负责对象的构造
{
new(p) T(val);
}
void destroy(T *p)//负责对象的析构
{
p->~T();
}
};
template<typename T,typename Alloc = Allocator<T>>
class vector
{
public:
vector(size_t size = 10)
{
_first = _allocator.allocate(size);//只给数组开辟空间,不进行构造
_last = _first;
_end = _first + size;
}
~vector()
{
T *ptmp = _first;
for (; ptmp != _last; ++ptmp)//首先释放数组中的有效元素
{
_allocator.destroy(ptmp);
}
//释放数组
_allocator.deallocate(_first);
_first = _end = _last = nullptr;
}
vector(const vector&src)
{
//首先申请空间
size_t size = src._end - src._first;//获取src的数组长度
_first = _allocator.allocate(size);//申请空间
_last = _first;
int len = src._last - src._first;
for (int i = 0; i < len; i++)//拷贝数据
{
_allocator.construct(_last++, src._first[i]);
}
_end = _first + size;
}
void operator=(const vector&src)
{
//1,判断是否为自赋值
if (this == &src)
return;
//2,释放原来对象占用的空间
~vector();
//3,和拷贝构造过程一样
size_t size = src._end - src._first;//获取src的数组长度
_first = _allocator.allocate(size);//申请空间
_last = _first;
int len = src._last - src._first;
for (int i = 0; i < len; i++)
{
_allocator.construct(_last++, src._first[i]);
}
_end = _first + size;
}
void push_back(const T &val)
{
if (full())
expand();
_allocator.construct(_last++, val);
}
void pop_back()
{
if (empty())
return;
--_last;
_allocator.destroy(_last);
}
T back()
{
if (empty())
throw "the vector is empty";
return *(_last - 1);
}
bool full()const { return _last == _end; }
bool empty()const { return _first == _last; }
size_t size()const { return _last - _first; }
private:
T *_first;//指向数组的首元素的地址
T *_last;//指向数据最后一个有效元素的后继
T *_end;//指向数组有效空间最后一个元素的后继
Alloc _allocator;//空间适配器
void expand()//二倍扩容
{
size_t len = _end - _first;//当前的数组大小
size_t size = len * 2;//扩容之后的数组的大小
T *ptmp = _allocator.allocate(size);//先申请大小为原来二倍的空间
T *pcur = _first;//
for (int i = 0; i < len; i++)
_allocator.construct(ptmp + i, *pcur++);
for (int i = 0; i < len; i++)
_allocator.destroy(_first++);
_allocator.deallocate(_first);
_first = ptmp;
_end = _first + size;
_last = _first + len;
}
};
这里我们只讲述vector中的push_back方法;
匹配右值的push_back函数:
我们在vector类里面增加一个带右值引用的push_back的重载函数,
在空间配置器里面增加一个带右值引用的construct的方法。
void push_back(T &&val)
{
if (full())
expand();
_allocator.construct(_last++, std::move(val));
}
void construct(T *p, T &&val)//负责对象的构造
{
new(p) T(std::move(val));
}

上面用到了方法std::move()
移动语义。。。由于我们知道,(直接使用回调用左值的construct函数,而不是右值的),所以我们在传参的时候,希望使用它的本身的右值语义,我们这里使用std::move()相当于将左值强制转换成右值引用类型。move()源码如下

但是上面的方法不免显得有点了,因为一旦我们要使用val的右值引用就必须给它。
我们可以使用函数模板来简化我们的代码量。
template<typename Ty>
void push_bask(Ty&& val) {
if(full()) {
expand();
}
// forward完美转发,能复原val原本类型,即右值引用-》右值;左值引用-》左值
_allocate.construct(_last++, std::forward<Ty>(val));
}
template <typename Ty>
void construct(T* p, Ty&& val) {//负责对象的构造
new(p) T(std::forward<Ty>(val));
}
int main(void)
{
CMyString str1 = "aaa";
vector<CMyString> vec;
cout << "----------------------------------" << endl;
vec.push_back(str1); //CMyString&
vec.push_back(CMyString("nnn")); //CMyString&&
cout << "----------------------------------" << endl;
}
std::forward<Ty>(val)
自动识别val是左值还是右值并返回对应类型
引用折叠:右值引用+右值引用=右值引用
右值引用+左值引用=左值引用
函数模板参数推演Ty是左值CMyString&& 还是 CMyString&
CMyString&& + && =CMyString&&
CMyString& + && = CMyString&