C++11 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类定义见下方完整代码

在这里插入图片描述

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

在这里插入图片描述
由于是用临时对象给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&

在这里插入图片描述

完整代码如下:

#include <iostream>

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bugcoder-9905

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值