跳至主要內容

C++的右值引用的应用

张威大约 5 分钟c/c++对象优化

C++的右值引用的应用

1、CMyStirng代码的问题分析

class CMyString
{
public:
	CMyString(const char* str = nullptr)
	{
		cout << "CMyString(const char*)" << endl;
		if (str != nullptr)
		{
			mptr = new char[strlen(str) + 1];
			strcpy(mptr, str);
		}
		else
		{
			// 最少都初始化只有一个\0的字符串,使得不需要对字符串判空操作
			mptr = new char[1];
			*mptr = '\0';
		}
	}
	~CMyString()
	{
		cout << "~CMyString" << endl;
		delete[]mptr;
		mptr = nullptr;
	}
	// 带左值引用参数的拷贝构造
	CMyString(const CMyString& str)
	{
		cout << "CMyString(const CMyString&)" << endl;
		mptr = new char[strlen(str.mptr) + 1];
		strcpy(mptr, str.mptr);
	}
	
	// 带左值引用参数的赋值重载函数
	CMyString& operator=(const CMyString& str)
	{
		cout << "operator=(const CMyString&)" << endl;
		if (this == &str)
			return *this;

		delete[]mptr;

		mptr = new char[strlen(str.mptr) + 1];
		strcpy(mptr, str.mptr);
		return *this;
	}
	
	const char* c_str()const { return mptr; }
private:
	char* mptr;
};


CMyString GetString(CMyString& str)
{
	const char* pstr = str.c_str();
	CMyString tmpStr(pstr);
	return tmpStr;
}
int main()
{
	CMyString str1("aaaaaaaaaaaaaaaaaaaa");
	CMyString str2;
	str2 = GetString(str1);
	cout << str2.c_str() << endl;

	return 0;
}
CMyString(const char*)
CMyString(const char*)
CMyString(const char*)
CMyString(const CMyString&) // m1
~CMyString
operator=(const CMyString&) // m2
~CMyString
aaaaaaaaaaaaaaaaaaaa
~CMyString
~CMyString

m1位置处,是GetString返回时候调用拷贝构造返回main函数栈帧的临时对象,这里会做,因为对象存放一个动态开辟的char数组。并且拷贝完立马就释放了。

m2位置是main函数上临时对象给str2拷贝赋值,等于说又做一次,并且拷贝完立马就释放了。

这两次操作的,我们

解决方法

[引用 | 张威的编程学习笔记 (gitee.io)](https://iszhwei.gitee.io/ccpp/02 c__基础/引用.html#右值引用)

右值引用、实现接收右值的拷贝构造和拷贝赋值函数移动构造和赋值

临时对象是右值

添加的函数如下:

// 带右值引用参数的拷贝构造,移动构造
// 将右边的资源直接窃取过来
CMyString(CMyString&& str) // str传入的就是右值亦称为将亡值
{ 
	cout << "CMyString(CMyString&& str)" << endl;
	mptr = str.mptr;
	str.mptr = nullptr;
}

// 带右值引用参数的赋值重载函数,移动赋值
// 将右边的资源直接窃取过来
CMyString& operator=(CMyString&& str)
{
	cout << "operator=(CMyString&& str)" << endl;
	if (this == &str)
		return *this;

	delete[] mptr;
	mptr = str.mptr;
	str.mptr = nullptr;
	return *this;
}
CMyString(const char*)
CMyString(const char*)
CMyString(const char*)
CMyString(CMyString&& str)
~CMyString
operator=(CMyString&& str)
~CMyString
aaaaaaaaaaaaaaaaaaaa
~CMyString
~CMyString

两次拷贝全都走移动构造移动赋值,全都是进行指针指向的改变,而不是内存空间的重新分配,这样必然可以极大的提高效率

operator+()效率问题

CMyString operator+ (const String &lhs, const String &rhs)
{
	char* ptmp = new char[strlen(lhs._ptr) + strlen(rhs._ptr) + 1];//内存泄漏
	strcpy(ptmp, lhs.mptr);
	strcat(ptmp, rhs.mptr);
	return CMyString(ptmp);
}

如果上面这样写会导致,因此不能直接返回临时对象

CMyString operator+(const CMyString &lhs, const CMyString& rhs) {
    char* ptmp = new char[strlen(lhs.mptr) + strlen(rhs.mptr) + 1];
    strcpy(ptmp, lhs.mptr);
    strcat(ptmp, rhs.mptr);
    CMyString tmpStr(ptmp);
    delete[] ptmp;
    return tmpStr;
    //return CMyString(ptmp);
}

这样写虽然解决了内存泄漏问题,

解决办法

CMyString operator+(const CMyString& lhs,
	const CMyString& rhs)
{
	//char *ptmp = new char[strlen(lhs.mptr) + strlen(rhs.mptr) + 1];
	CMyString tmpStr;
	tmpStr.mptr = new char[strlen(lhs.mptr) + strlen(rhs.mptr) + 1];
	strcpy(tmpStr.mptr, lhs.mptr);
	strcat(tmpStr.mptr, rhs.mptr);
	//delete []ptmp;
	return tmpStr;//直接调用右值拷贝构造函数
	//return CMyString(ptmp);
}
#include <iostream>
using namespace std;

class CMyString
{
public:
	CMyString(const char* str = nullptr)
	{
		cout << "CMyString(const char*)" << endl;
		if (str != nullptr)
		{
			mptr = new char[strlen(str) + 1];
			strcpy(mptr, str);
		}
		else
		{
			mptr = new char[1];
			*mptr = '\0';
		}
	}
	~CMyString()
	{
		cout << "~CMyString" << endl;
		delete[]mptr;
		mptr = nullptr;
	}
	//带左值引用参数的拷贝构造
	CMyString(const CMyString& str)
	{
		cout << "CMyString(const CMyString&)" << endl;
		mptr = new char[strlen(str.mptr) + 1];
		strcpy(mptr, str.mptr);
	}
	//带右值引用参数的拷贝构造
	CMyString(CMyString&& str)//str引用的就是一个临时对象
	{
		cout << "CMyString(CMyString&&)" << endl;
		mptr = str.mptr;
		str.mptr = nullptr;
	}
	//带左值引用参数的赋值重载函数
	CMyString& operator=(const CMyString& str)
	{
		cout << "operator=(const CMyString&)" << endl;
		if (this == &str)
			return *this;

		delete[]mptr;

		mptr = new char[strlen(str.mptr) + 1];
		strcpy(mptr, str.mptr);
		return *this;
	}
	//带右值引用参数的赋值重载函数
	CMyString& operator=(CMyString&& str)//str引用的是临时对象
	{
		cout << "operator=(CMyString&&)" << endl;
		if (this == &str)
			return *this;

		delete[]mptr;

		mptr = str.mptr;
		str.mptr = nullptr;
		return *this;
	}
	const char* c_str()const { return mptr; }
private:
	char* mptr;

	friend CMyString operator+(const CMyString& lhs,
		const CMyString& rhs);
	friend ostream& operator<<(ostream& out, const CMyString& str);
};

CMyString operator+(const CMyString& lhs,
	const CMyString& rhs)
{
	//char *ptmp = new char[strlen(lhs.mptr) + strlen(rhs.mptr) + 1];
	CMyString tmpStr;
	tmpStr.mptr = new char[strlen(lhs.mptr) + strlen(rhs.mptr) + 1];
	strcpy(tmpStr.mptr, lhs.mptr);
	strcat(tmpStr.mptr, rhs.mptr);
	//delete []ptmp;
	return tmpStr;//直接调用右值拷贝构造函数
	//return CMyString(ptmp);
}
ostream& operator<<(ostream& out, const CMyString& str)
{
	cout << str.mptr;
	return out;
}


CMyString GetString(CMyString& str)
{
	const char* pstr = str.c_str();
	CMyString tmpStr(pstr);
	return tmpStr;
}

int main()
{
	CMyString str1 = "hello ";
	CMyString str2 = "world!";
	cout << "--------------------------" << endl;
	CMyString str3 = str1 + str2;
	cout << "--------------------------" << endl;
	cout << str3 << endl;
}

分析下面打印什么

int main()
{
	CMyString str1 = "aaa";

	vector<CMyString> vec;
	vec.reserve(10);

	cout << "----------------------------------" << endl;
	vec.push_back(str1); // 调用左值引用的拷贝
	vec.push_back(CMyString("nnn")); // 调用构造 和 右值引用的拷贝
	cout << "----------------------------------" << endl;
}

匹配左值: 调用左值引用的拷贝构造;

匹配右值: 调用右值引用的拷贝构造;

CMyString(const char*)
----------------------------------
CMyString(const CMyString&)
CMyString(const char*)
CMyString(CMyString&& str)
~CMyString
----------------------------------
~CMyString
~CMyString
~CMyString