使用自實現的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強轉,一個函數寫兩個版本.

完美轉發實現了參數在傳遞過程中保持其值屬性的功能,即若是左值,則傳遞之後仍然是左值,若是右值,則傳遞之後仍然是右值。

這裏放一個解釋不錯的文字鏈接,可以結合我上面例子進行學習

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章