由于问题场景的特殊,子函数调用时我们无法返回一个临时对象。
而且我们也只能用赋值的方式接收一个函数调用的返回值。
我们来分析分析上面的代码:
- 1、首先,str1和str2都是调用普通构造函数。
实参str1到形参str是引用的方式接收,没有产生构造。 - 2、然后普通构造tmpStr局部对象。 tmpStr这个对象占4个字节,有一个char*指针,指向外部的一个new出来的堆内存,堆内存存放的是字符串。然后return tmpStr;tmpStr是局部对象。
- 3、只能调用拷贝构造函数,在main函数的栈帧中构造一个临时对象(没有办法用GetString函数栈帧上的tmStr);
- 4、tmpStr拷贝构造新对象,然后调用析构函数析构tmpStr对象,这样就白耗费资源了,tmpStr资源不要就早说,直接给函数栈帧上的对象就好啦。
在GetString函数中,先构造一个tmpStr对象,将其拷贝构造给main函数上的临时对象,再释放tmpStr,可以看到tmpStr自己开辟又释放是非常浪费资源的。
- 用临时量给str2赋值,str2是原本已经存在的对象,它也有一个指针mptr,原先也指向了一个空间。
- 对于赋值来说,排除自赋值,然后把原先指向的空间释放掉,然后按照str的尺寸开辟空间,然后拷贝数据进行来。即按照临时对象的字符串大小开辟空间,然后把数据一个一个拷贝进来。
然后出语句,把这个临时对象析构及它指向的堆内存空间释放掉。
瞎折腾!直接把临时对象的外部资源给str2不就完了吗?
在main函数上有一个临时对象,但是这个临时对象又会拷贝赋值给str2,然后临时对象自己又释放,和上面的tmpStr一样,非常浪费资源。
右值引用绑定右值的,左值引用绑定左值的;
左值: 有内存,有名字;
右值(常量数字、临时量):没名字(临时量)、没内存(C++都是将临时量当做右值的)
3、用右值引用优化CMyStirng代码一个右值引用变量,本身是一个左值。
带右值引用的拷贝构造:
带左值引用参数的赋值重载函数:
示意图如下:
- 只是添加了两个右值引用函数,运行原来程序;
- 直接避免上面临时对象操作过程中开辟新空间的问题。
- 没有任何内存的开辟和释放和数据的拷贝。
#include4、MyString在vector上的应用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 = ' '; } } ~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; }; CMyString GetString(CMyString& str) { const char* pstr = str.c_str(); CMyString tmpStr(pstr); return tmpStr; } int main() { CMyString str1("aaaaaaaaaaaaaaaaaaaaa"); CMyString str2; str2 = GetString(str1); cout << str2.c_str() << endl; return 0; }
上面的CMyString是不变的。
下面的所有讨论,只围绕 +重载运算符号。
运行结果:
问题:+重载函数里的ptmp指针指向的内存无法释放,内存泄漏;
解决方法:
这样可以将ptmp内存释放了,但是构造了tmpStr,又是一个开辟相同大小内存空间(然后拷贝数据,然后出函数,又自己析构了,效率低)。
改进方法:
修改“+重载”函数。
运行结果:
3:构造"+重载函数的" tmpStr,5是它的析构函数
4:说明了在用 “+重载函数中的返回值 tmpStr 拷贝构造 str3时”,匹配的是右值引用的拷贝构造。相当于让tmpStr的堆资源,直接让str3指向了,将tmpStr置为nullptr,右值引用的拷贝构造后面的析构函数,其实没有析构什么;
说明在执行上面的函数过程中,没有涉及任何的内存开辟,释放,拷贝(效率非常高)。
到此,上面的"+"重载函数就好了。
最终代码:
#include例2:vector相关问题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 = ' '; } } ~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; }
CMyString类和例1的完全相同。
匹配左值: 调用左值引用的拷贝构造;vec.push_back(str1);
匹配右值: 调用右值引用的拷贝构造;vec.push_back(CMyString("bbb"));



