C++11 move移动语义和forward类型完美转发

文章目录

      • move移动语义,将左值转为右值
      • forward类型完美转发

一个自定义空间配置器的vector

template<typename T>
class Allocator {
public:
	// 开辟size字节
	T* allocate(size_t size) {
		return (T*)malloc(size);
	}

	void deallocate(void* p) {
		free(p);
	}

	void construct(T* p, const T& val) {
		new (p) T(val);  // 定位new,指定地址构造值为val的对象,调用拷贝构造函数
	}

	void destroy(T* p) {
		p->~T();
	}
};

const int INIT_SIZE = 10;

template<typename T, typename Alloc = Allocator<T>>
class Vector {
public:
	Vector(int size = INIT_SIZE, const Alloc& alloc = Allocator<T>())
		: allocator_(alloc)
	{
		// first_ = new T[size];
		first_ = allocator_.allocate(size * sizeof(T));
		last_ = first_;
		end_ = first_ + size;
	}

	~Vector() {
		// delete[] first_;

		// 析构时,只析构容器中有效元素[first_, last - 1]
		for (T* p = first_; p != last_; p++) {
			allocator_.destroy(p);
		}
		// 释放整个容器空间
		allocator_.deallocate(first_);
		last_ = first_ = end_ = nullptr;
	}

	Vector(const Vector<T>& src) {
		// 深拷贝
		// 开辟空间
		int size = src.end_ - src.first_;
		// first_ = new T[size];
		first_ = allocator_.allocate(sizeof(T) * size);

		// 拷贝有效元素
		int len = src.last_ - src.first_;
		for (int i = 0; i < len; i++) {
			// first_[i] = src.first_[i];
			allocator_.construct(first_ + i, src.first_[i]);
		}
		last_ = first_ + len;
		end_ = first_ + size;
	}

	Vector<T>& operator=(const Vector<T>& src) {
		if (this == &src) {
			return *this;
		}
		// 释放当前对象原来的资源
		// delete[] first_;
		for (T* p = first_; p != last_; p++) {
			allocator_.destroy(p);
		}
		allocator_.deallocate(first_);
		last_ = first_ = end_ = nullptr;

		// 开辟空间
		int size = src.end_ - src.first_;
		// first_ = new T[size];
		first_ = allocator_.allocate(sizeof(T) * size);

		// 拷贝有效元素
		int len = src.last_ - src.first_;
		for (int i = 0; i < len; i++) {
			allocator_.construct(first_ + i, src.first_[i]);
		}
		last_ = first_ + len;
		end_ = first_ + size;
		// 支持连续赋值
		return *this;
	}

	bool inline full() const {
		return last_ == end_;
	}

	bool inline empty() const {
		return first_ == last_;
	}

	bool inline size() const {
		return last_ - first_;
	}

	// 在容器末尾构造一个值为val的对象
	void push_back(const T& val) {
		if (full()) {
			expand();
		}
		allocator_.construct(last_, val);
		last_++;
	}

	void push_back(T&& val) {
		if (full()) {
			expand();
		}
		allocator_.construct(last_, std::move(val));
		last_++;
	}

	// 从末尾删除元素
	void pop_back() {
		if (empty()) {
			return;
		}
		last_--;
		allocator_.destroy(last_);
	}

	T back() const {
		return *(last_ - 1);
	}

private:
	void expand() {
		int size = end_ - first_;
		// T* tmp = new T [2 * size];
		T* tmp = allocator_.allocate(2 * sizeof(T) * size);
		for (int i = 0; i < size; i++) {
			allocator_.construct(tmp + i, first_[i]);
		}

		// delete[] first_;
		for (T* p = first_; p != last_; p++) {
			allocator_.destroy(p);
		}
		allocator_.deallocate(first_);

		first_ = tmp;
		last_ = first_ + size;
		end_ = first_ + 2 * size;
	}

	T* first_;   // 指向数组起始位置
	T* last_;    // 指向数组中有效元素的后继位置
	T* end_;     // 指向数组空间的后继位置
	Alloc allocator_;
};

当push_back方法的参数是一个临时对象时,我们希望匹配带右值引用参数的push_back,直接把临时对象的资源转移到vector数组中,并将临时对象指向资源的指针置为nullptr

vector类添加带右值引用参数的push_back

	void push_back(T&& val) {
		if (full()) {
			expand();
		}
		allocator_.construct(last_, val);
		last_++;
	}

空间配置器添加带右值引用参数的construct

	void construct(T* p, T&& val) {
		new (p) T(val);  // 定位new,指定地址构造值为val的对象,调用拷贝构造函数
	}
int main() {
	String str1("hello ");
	Vector<String> vec;

	cout << "=========================" << endl;
	vec.push_back(str1);
	vec.push_back("123");
	cout << "=========================" << endl;

	return 0;
}

String类定义见下方完整代码

C++11 move移动语义和forward类型完美转发_第1张图片

move移动语义,将左值转为右值

虽然"123"调用普通构造函数生成的String是一个右值,匹配的也是带右值引用参数的push_back,但右值引用变量实际上是一个左值,push_back中调用construct,匹配的还是construct(T* p, const T& val)

在vector类中添加带右值引用参数的push_back,用move将左值变量强转成右值

	void push_back(T&& val) {
		if (full()) {
			expand();
		}
		allocator_.construct(last_, std::move(val));
		last_++;
	}

虽然匹配的是带右值引用参数的construct,但形参val本身是 一个左值,还需要将val转换为左值,才能匹配String(String &&)

	void construct(T* p, T&& val) {
		new (p) T(std::move(val)); 
	}

注意,这里不要把T&& val写成const T&& val,否则std::move返回的是右值,不能匹配const T&& val

int main() {
	String str1("hello ");
	Vector<String> vec;

	vec.push_back(str1);
	cout << "=========================" << endl;
	vec.push_back(String("123"));
	cout << "=========================" << endl;
	
	return 0;
}

C++11 move移动语义和forward类型完美转发_第2张图片
由于是用临时对象给Vector底层数组构造元素,所以匹配的是带右值引用的push_back,接着匹配带右值引用的construct,最后匹配带右值引用的String构造函数

而使用带右值引用的构造函数时,是直接转移资源,而不是开辟空间再进行数据拷贝

上述添加的两段代码中,虽然传入的实参是右值,然后右值引用形参变量本身是个左值,所以函数内使用形参变量时,如果我们想要得到右值形式,需要使用move。我们提供了分别提供了两个带左值引用参数和带右值引用参数的push_back和construct,实际上同一个函数不需要提供两个带左值引用参数和带右值引用参数的函数,使用函数模板的类型推演 + 引用折叠 + forward后使用一个函数即可

forward类型完美转发

template<typename Ty>
void push_back(Ty&& val) {
	// 实参是String&&,引用折叠后,则val是String&&
	// 实参是String&,引用折叠后,则val是String&
	if (full()) {
		expand();
	}
	allocator_.construct(last_, std::forward<Ty>(val));
	last_++;
}
template<typename Ty>
void construct(T* p, Ty&& val) {
	new (p) T(std::forward<Ty>(val));
}

传入的实参是右值,引用折叠后,val还是右值引用,是一个左值;传入的实参是左值,引用折叠后,val还是左值引用,是一个左值。使用forward可以获取传入的实参的类型,传入左值返回左值,传入右值返回右值

string&& + && = string&&
string& + && = string&

C++11 move移动语义和forward类型完美转发_第3张图片

完整代码如下:

#include 

using namespace std;

class String{
public:
	String(const char* str = nullptr){
		cout << "String(const char*)" << endl;
		if (str != nullptr){
			m_data = new char[strlen(str) + 1];
			strcpy(m_data, str);
		}
		else{
			m_data = new char[1];
			*m_data = '\0';
		}
	}

	
	String(String&& src) {
		cout << "String(String&& src)" << endl;
		m_data = src.m_data;
		src.m_data = nullptr;
	}

	String(const String& src){
		cout << "String(const String& src)" << endl;
		m_data = new char[strlen(src.m_data) + 1];
		strcpy(m_data, src.m_data);
	}

	~String(){
		cout << "~String()" << endl;
		delete[]m_data;
		m_data = nullptr;
	}

	//调用String&是为了支持连续的operator=赋值操作
	String& operator=(const String& src){
		cout << "operator=(const String& src)" << endl;
		if (&src == this){
			return *this;
		}
		delete[]m_data;

		m_data = new char[strlen(src.m_data) + 1];
		strcpy(m_data, src.m_data);
		return *this;
	}

	String& operator=(String&& src) {
		cout << "operator=(String&& src)" << endl;
		if (&src == this) {
			return *this;
		}
		delete[]m_data;
		m_data = src.m_data; // 改变堆区资源指向
		src.m_data = nullptr; // 临时对象的指针置空,防止析构的时候释放资源
		return *this;
	}

	const char* c_str() const {
		return m_data;
	}

	friend String operator+(const String& str1, const String& str2);
	friend ostream& operator<<(ostream& out, const String& str);

private:
	char* m_data;//用于保存字符串
};

String operator+(const String& str1, const String& str2) {
	String tmp;
	tmp.m_data = new char[strlen(str1.m_data) + strlen(str2.m_data) + 1];
	strcpy(tmp.m_data, str1.m_data);
	strcat(tmp.m_data, str2.m_data);
	return tmp;
}
ostream& operator<<(ostream& out, const String& str) {
	out << str.m_data;
	return out;
}

String get_string(String& str) {
	const char* pstr = str.c_str();
	String tmp(pstr);
	return tmp;
}



template<typename T>
class Allocator {
public:
	// 开辟size字节
	T* allocate(size_t size) {
		return (T*)malloc(size);
	}

	void deallocate(void* p) {
		free(p);
	}

	template<typename Ty>
	void construct(T* p, Ty&& val) {
		new (p) T(std::forward<Ty>(val));
	}

	//void construct(T* p, const T& val) {
	//	new (p) T(val);  // 定位new,指定地址构造值为val的对象,调用拷贝构造函数
	//}

	void destroy(T* p) {
		p->~T();
	}
};

const int INIT_SIZE = 10;

template<typename T, typename Alloc = Allocator<T>>
class Vector {
public:
	Vector(int size = INIT_SIZE, const Alloc& alloc = Allocator<T>())
		: allocator_(alloc)
	{
		// first_ = new T[size];
		first_ = allocator_.allocate(size * sizeof(T));
		last_ = first_;
		end_ = first_ + size;
	}

	~Vector() {
		// delete[] first_;

		// 析构时,只析构容器中有效元素[first_, last - 1]
		for (T* p = first_; p != last_; p++) {
			allocator_.destroy(p);
		}
		// 释放整个容器空间
		allocator_.deallocate(first_);
		last_ = first_ = end_ = nullptr;
	}

	Vector(const Vector<T>& src) {
		// 深拷贝
		// 开辟空间
		int size = src.end_ - src.first_;
		// first_ = new T[size];
		first_ = allocator_.allocate(sizeof(T) * size);

		// 拷贝有效元素
		int len = src.last_ - src.first_;
		for (int i = 0; i < len; i++) {
			// first_[i] = src.first_[i];
			allocator_.construct(first_ + i, src.first_[i]);
		}
		last_ = first_ + len;
		end_ = first_ + size;
	}

	Vector<T>& operator=(const Vector<T>& src) {
		if (this == &src) {
			return *this;
		}
		// 释放当前对象原来的资源
		// delete[] first_;
		for (T* p = first_; p != last_; p++) {
			allocator_.destroy(p);
		}
		allocator_.deallocate(first_);
		last_ = first_ = end_ = nullptr;

		// 开辟空间
		int size = src.end_ - src.first_;
		// first_ = new T[size];
		first_ = allocator_.allocate(sizeof(T) * size);

		// 拷贝有效元素
		int len = src.last_ - src.first_;
		for (int i = 0; i < len; i++) {
			allocator_.construct(first_ + i, src.first_[i]);
		}
		last_ = first_ + len;
		end_ = first_ + size;
		// 支持连续赋值
		return *this;
	}

	bool inline full() const {
		return last_ == end_;
	}

	bool inline empty() const {
		return first_ == last_;
	}

	bool inline size() const {
		return last_ - first_;
	}

	template<typename Ty>
	void push_back(Ty&& val) {
		if (full()) {
			expand();
		}
		allocator_.construct(last_, std::forward<Ty>(val));
		last_++;
	}


	// 从末尾删除元素
	void pop_back() {
		if (empty()) {
			return;
		}
		last_--;
		allocator_.destroy(last_);
	}

	T back() const {
		return *(last_ - 1);
	}

private:
	void expand() {
		int size = end_ - first_;
		// T* tmp = new T [2 * size];
		T* tmp = allocator_.allocate(2 * sizeof(T) * size);
		for (int i = 0; i < size; i++) {
			allocator_.construct(tmp + i, first_[i]);
		}

		// delete[] first_;
		for (T* p = first_; p != last_; p++) {
			allocator_.destroy(p);
		}
		allocator_.deallocate(first_);

		first_ = tmp;
		last_ = first_ + size;
		end_ = first_ + 2 * size;
	}

	T* first_;   // 指向数组起始位置
	T* last_;    // 指向数组中有效元素的后继位置
	T* end_;     // 指向数组空间的后继位置
	Alloc allocator_;
};

int main() {
	String str1("hello ");
	Vector<String> vec;

	vec.push_back(str1);
	cout << "=========================" << endl;
	vec.push_back(String("123"));
	cout << "=========================" << endl;

	return 0;
}

你可能感兴趣的:(C++,c++,开发语言)