使用自实现的string和vector类说明std::move和std::forward

在看文章前需要明确一点,引用的重叠

template <typename T>
void Func(T &&val){//....}

1.当实例化的T的为一个左值引用时,即 T& ++ &&,最终val是一个左值;
2.当实例化的T的为一个右值引用时,即 T&& ++ &&,最终val是一个右值;

string和vector类的代码:

#include <iostream>
#include <string.h>
using namespace std;

#include<iostream>
#include <string.h>
using namespace std;

//自定义string类
class String
{
public:
//构造函数
String(const char *ptr = nullptr){
	std::cout << "String(const char *ptr)" << std::endl;
	if (ptr == nullptr){
		mpstr = new char[1];
		mpstr[0] = '\0';
	}else{
		mpstr = new char[strlen(ptr) + 1];
		strcpy(mpstr, ptr);
	}
}
//左值引用参数的拷贝构造函数
String(const String &src){ //src引用的是一个左值
	std::cout << "String(const String &src)" << std::endl;
	mpstr = new char[strlen(src.mpstr) + 1];
	strcpy(mpstr, src.mpstr);
}

//右值引用参数的拷贝构造函数
String(String &&src){//src引用的是一个临时对象
	std::cout << "String(const String &&src)" << std::endl;
	mpstr = src.mpstr;
	src.mpstr = nullptr;
}

//左值引用参数赋值运算符的重载函数
String& operator=(const String &src){//src引用的是一个左值
	std::cout << "String& operator=(const String &src)" << std::endl;
	if (this == &src)
		return *this;
	delete[]mpstr;
	mpstr = new char[strlen(src.mpstr) + 1];
	strcpy(mpstr, src.mpstr);
	return *this;
}

//右值引用参数赋值运算符的重载函数
String& operator=(String &&src){ //src引用的是一个临时对象
	std::cout << "String& operator=(const String &&src)" << std::endl;
	if (this == &src)
		return *this;
	delete[]mpstr;
	mpstr = src.mpstr;
	src.mpstr = nullptr;
	return *this;
}

~String(){
    std::cout << "~String()" << std::endl;
	if (mpstr != nullptr){
		delete mpstr;
		mpstr = nullptr;
	}
}

bool operator>(const String &src){
		if (strcmp(mpstr, src.mpstr) > 0)
			return true;
		return false;
}
bool operator<(const String &src){
		if (strcmp(mpstr, src.mpstr) < 0)
			return true;
		return false;
	}
bool operator==(const String &src){
		if (strcmp(mpstr, src.mpstr) == 0)
			return true;
		return false;
	}
	//获取字符串的长度
int length()const{ return strlen(mpstr); }
	//根据下标返回对应的字符
char& operator[](int index){ return mpstr[index]; }
	//返回该字符串
const char* c_str()const{ return mpstr; }
private:
	char *mpstr;
	friend String operator+(const String &lhs, const String &rhs);
	friend ostream& operator<<(ostream &out, const String &src);
};
//operator+
String operator+(const String &lhs, const String &rhs){
    String str;
	char* temp = new char[lhs.length() + rhs.length() + 1];
	strcpy(str.mpstr, lhs.mpstr);
	strcat(str.mpstr, rhs.mpstr);
	return str;
}
ostream& operator<<(ostream &out, const String &src){
	out << src.mpstr;
	return out;
}


//vector的空间配置器 Allocator
template<typename T>
struct Allocator{
    T*allocate(size_t size){ //负责开辟内存
        return (T*)malloc(sizeof(T)*size);
    }
    void deallocate(void *p){//负责内存的释放
        free(p);
    }
    //带左值参数的construct
    void construct(T*p,const T&val){//负责在指定位置构造对象
        new (p) T(val);//定位new
    }
    //带右值参数的construct
    void construct(T*p,T&&val){//负责在指定位置构造对象
        new (p) T(std::move(val));//定位new
    }
    void destroy(T*p){ //负责指定对象p的析构,类型为T
        p->~T();
    }
};

//自定义vector类
template <typename T, typename Alloca = Allocator<T>>
class Vector
{
public:
	//默认构造的vector,底层没分配过内存0
Vector() :mpVec(NULL), mSize(0), mCur(0){}
	//size表示初始的内存大小,val表示内存初始值
Vector(int size, const T &val = T())
	:mSize(size), mCur(size){
	mpVec = _allocator.allocate(mSize * sizeof(T));
	for (int i = 0; i < mSize; ++i){
		_allocator.construct(mpVec + i, val);
	}
}
//带左值参数的拷贝构造
Vector(const Vector &src)
	:mSize(src.mSize), mCur(src.mCur){
	mpVec = _allocator.allocate(sizeof(T)*mSize);
	for (int i = 0; i < mCur; ++i){
		_allocator.construct(mpVec + i, src.mpVec[i]);
	}
}

//operator=
Vector& operator=(const Vector &src){
	if (this == &src)
		return *this;

	for (int i = 0; i < mCur; ++i){
		_allocator.destroy(mpVec + i);
	}
	_allocator.deallocate(mpVec);

	mpVec = _allocator.allocate(sizeof(T)*mSize);
	for (int i = 0; i < mCur; ++i){
		_allocator.construct(mpVec + i, src.mpVec[i]);
	}
	return *this;
}
~Vector(){
	for (int i = 0; i < mCur; ++i){
		_allocator.destroy(mpVec + i);
	}
	_allocator.deallocate(mpVec);
	mpVec = NULL;
}

//带左值参数的末尾添加元素    
void push_back(const T &val) {
	std::cout<<"push_back(const T &val)"<<std::endl;
	if (full())
		reSize();
    _allocator.construct(mpVec + mCur, val);
	mCur++;
}

//带右值参数的末尾添加元素    
void push_back(T &&val) {
    std::cout<<"push_back(T &&val)"<<std::endl;
	if (full())
		reSize();
    _allocator.construct(mpVec + mCur, std::move(val));
	mCur++;
}
	//末尾删除
void pop_back(){
	if (empty())
		return;
	--mCur;
	_allocator.destroy(mpVec + mCur);
}

T front()const{ return mpVec[0]; }
T back()const{ return mpVec[mCur - 1]; }

bool full()const{ return mCur == mSize; }
bool empty()const{ return mCur == 0; }

T& operator[](int index){ return mpVec[index]; }

//内存以2倍方式增长
void reSize(){
	//默认的size==0
	if (mSize == 0){
		mpVec = _allocator.allocate(sizeof(T));mSize = 1;mCur = 0;
	}else{
	    T *ptmp = _allocator.allocate(mSize * 2 * sizeof(T));
		for (int i = 0; i < mCur; ++i){
		    _allocator.construct(ptmp + i, mpVec[i]);
		}
		mSize *= 2;
		for (int i = 0; i < mCur; ++i){
			_allocator.destroy(mpVec + i);
		}
		_allocator.deallocate(mpVec);
		mpVec = ptmp;
	}
}

int size()const{ return mCur; }

//定义当前容器的迭代器类型 ,遍历容器(遍历容器底层的数据结构)
class iterator
{
public:
	iterator(T *p = NULL){
		ptr = p;
	}
	bool operator!=(const iterator &it){
		return ptr != it.ptr;
	}
	int operator-(const iterator &it){
		return ptr - it.ptr;
	}
	void operator++(){
		++ptr;
	}
	T& operator*(){
	    return *ptr;
	}
	private:
		T *ptr;  //本质上就是一根被泛化的指针
	};

	iterator begin(){ return iterator(mpVec); }
	iterator end(){ return iterator(mpVec + mCur); }

private:
	T *mpVec;//动态数组的起始地址,相当于first
	int mSize;//容量
	int mCur;//当前size
	Alloca _allocator;//空间配置器对象
};

int main(){
    String str1 = "123";
    Vector<String> vec1;
    Vector<String> vec2;
    Vector<String> vec3;
    vec1.push_back(str1);
    vec2.push_back(std::move(str1));
    vec3.push_back(String("xxxx"));
    return 0;
}

我们将代码中vector的push_back和Allocator的construct取出来说明 std::move 的作用:

//带左值参数的末尾添加元素    
void push_back(const T &val) {
	if (full())
		reSize();
    _allocator.construct(mpVec + mCur, val);
	mCur++;
}

//带右值参数的末尾添加元素    
void push_back(T &&val) {
    std::cout<<"push_back(T &&val)"<<std::endl;
	if (full())
		reSize();
    _allocator.construct(mpVec + mCur, std::move(val));
    /*
    val接收的是一个右值,但是在当前函数本身是一个左值,所以,我们想
    调用配置器的带右值引用的construct,在这里调用String的带右值的拷贝构造函数,
    需要将val强转成一个右值进行传递,
    所以,使用std::move进行转换,不管val是左值还是右值,得到的都是一个右值。
    */
	mCur++;
}

总结:
std::move将一个值,无论是左值还是右值都强转为右值。但是出现一个问题,这样的代码我们每次都要去实现一个带右值参数的,一个带左值参数的,效率并不高,接下来将引出std::forward,将最初传进来的右值完美的转发,保证不会变为左值,不需要我们进行强转。

std::forward:

template <typename Ty>
void push_back(Ty &&val){
    std::cout<<"template push_back"<<std::endl;
	if (full())
		reSize();
    _allocator.construct(mpVec + mCur, std::forward(val));
    mCur++;
}

template <typename Ty>
void construct(T*p,Ty&&val){
     std::cout<<"template construct"<<std::endl;
     new (p) T(std::forward<Ty>(val));//定位new
}

运行结果:

String(const char *ptr)
template push_back
template construct
String(const String &src)
template push_back
template construct
String(const String &&src)
String(const char *ptr)
template push_back
template construct
String(const String &&src)
~String()
~String()
~String()
~String()
~String()

std::forward总结:

  1. 不过不加std::forward,在push_back中,val是一个左值,其实我们想给construct中传入一个右值;
  2. 加上std::forward,尽管val在函数push_back中是一个左值,但是std::forward能知道,能够识别,调用者传给push_back的一开始就是个右值,所以会把val是右值的类型完美的转发传达给construct,就不需要我们手动去调用std::move强转,一个函数写两个版本.

完美转发实现了参数在传递过程中保持其值属性的功能,即若是左值,则传递之后仍然是左值,若是右值,则传递之后仍然是右值。

这里放一个解释不错的文字链接,可以结合我上面例子进行学习

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值