对象作为参数或者返回值的处理

首先来看类的定义:

class Test
{
   
public:
	// Test() Test(20)
	Test(int data = 10) :ma(data)
	{
   
		cout << "Test(int)" << endl;
	}
	~Test()
	{
   
		cout << "~Test()" << endl;
	}
	Test(const Test &t):ma(t.ma)
	{
   
		cout << "Test(const Test&)" << endl;
	}
	void operator=(const Test &t)
	{
   
		cout << "operator=" << endl;
		ma = t.ma;
	}
	int getData()const {
    return ma; }
private:
	int ma;
};

主函数模块:

Test GetObject(Test &t) 
{
   
	int val = t.getData();
	/* 在传参的过程中,不用引用的话会产生一次临时对象的生成过程 Test tmp(val); return tmp; 在返回值中,也会产生一次在main主函数栈帧上的临时对象 */
	// 返回临时对象
	return Test(val); 
}
int main()
{
   
	Test t1;
	Test t2 = GetObject(t1);
	//t2 = GetObject(t1);
	return 0;
}

编写拷贝函数的规则:

  1. 函数参数传递过程中,对象优先按引用传递,不要按值传递
  2. 函数返回对象的时候,应该优先返回一个临时对象,而不要返回一个定义过的对象
  3. 接收返回值是对象的函数调用的时候,优先按初始化的方式接收,不要按赋值的方式接收
  4. 不能返回局部的或者临时对象的指针或引用

存在堆空间的对象作为返回值的情况

先看一个mystring类的定义:

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) // 临时对象
	{
   
		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;
};

主函数模块:

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&) => tmpStr拷贝构造main函数栈帧上的临时对象
~CMyString
operator=(const CMyString&) => main函数栈帧上的临时对象给t2赋值
~CMyString
aaaaaaaaaaaaaaaaaaaa
~CMyString
~CMyString

结论:在有内存开销的对象作为返回值的情况下,返回的临时对象会被以右值引用的方式将自我的资源移交给新对象,大体上就是拷贝地址,原指针置为空。

参考文献

[1] 施磊.腾讯课堂——C++高级.图论科技,2020.7.