文章目录
- 右值引用
- 值得形式返回对象的缺陷
- 移动语句
- 移动赋值
右值引用
能够取地址、能够被修改的被称之为左值。
不能够取地址、不能够被修改、以及将亡值被称之为右值。
- 普通类型的变量,因为有名字,可以取地址,都认为是左值。
- const修饰的常量,不可修改,只读类型的,理论应该按照右值对待,但因为其可以取地址(如果只是 const类型常量的定义,编译器不给其开辟空间,如果对该常量取地址时,编译器才为其开辟空间), C++11认为其是左值。
- 如果表达式的运行结果是一个临时变量或者对象,认为是右值。
- 如果表达式运行结果或单个变量是一个引用则认为是左值。
C++11对右值进行了严格的区分: C语言中的纯右值,比如:a+b, 100 将亡值。比如:表达式的中间结果、函数按照值的方式进行返回。
#include<iostream>
using namespace std;
void test(int a=0)
{
return a;//a是将亡值
}
int main(void)
{
int a=10;//a是左值,10是右值
int b=20;//b是左值,20是右值
b=a;//左值a也可以放在等于号=右边
return 0;
}
值得形式返回对象的缺陷
#include<iostream>
#include<string>
using namespace std;
class String
{
public:
String(const char* str = "")
{
cout << "构造函数()->String(const char* str = "")" << endl;
if (nullptr == str)
str = "";
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
String(const String & s)
: _str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
String& operator=(const String& s)
{
if (this != &s)
{
char* pTemp = new char[strlen(s._str) + 1];
strcpy(pTemp, s._str);
delete[] _str;
_str = pTemp;
}
return *this;
}
String operator+(const String& s)
{
char* pTemp = new char[strlen(_str) + strlen(s._str) + 1];
strcpy(pTemp, _str);
strcpy(pTemp + strlen(_str), s._str);
String strRet(pTemp);
return strRet;
}
~String()
{
if (_str) delete[] _str;
}
private:
char* _str;
};
int main()
{
String s1("hello");
String s2("world");
String s3=s1 + s2;
return 0;
}
对于如上代码的operator+
中,返回strRet
的时候需要临时创建一个对象,然后销毁strRet
,最后再把strRet
给s3
。
这乍一看是没什么问题,但是这里的strRet
、临时对象
、s3
的内容是完全相同的,但是开辟了三块空间,是不是太浪费空间了。
移动语句
C++11当中,如果需要实现移语句,必须使用右值引用。
#include<iostream>
using namespace std;
class String
{
public:
String(const char* str = "")
{
cout << "构造函数()->String(const char* str = "")" << endl;
if (nullptr == str)
str = "";
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
String(const String & s)
: _str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
void swap(String& s)
{
std::swap(_str,s._str);
}
String(String&& s)noexcept
:_str(nullptr)
{
swap(s);
}
String& operator=(const String& s)
{
if (this != &s)
{
char* pTemp = new char[strlen(s._str) + 1];
strcpy(pTemp, s._str);
delete[] _str;
_str = pTemp;
}
return *this;
}
String operator+(const String& s)
{
char* pTemp = new char[strlen(_str) + strlen(s._str) + 1];
strcpy(pTemp, _str);
strcpy(pTemp + strlen(_str), s._str);
String strRet(pTemp);
return strRet;
}
~String()
{
if (_str) delete[] _str;
}
private:
char* _str;
};
int main()
{
String s1("hello");
String s2("world");
String s3=(s1 + s2);
return 0;
}
代码添加一个移动构造,就可以减少申请空间,在strRet
返回的时候会去调用String(String&& s)noexcept
,调用String(String&& s)noexcept
的时候可以看到,直接把s
和this
中的资源做了交换,出了作用域,s
就会被销毁,那么在销毁之前把资源先给this
不是就可以减少拷贝了吗?
移动赋值
移动赋值和移动构造一样,可以减少拷贝,将现有的资源最大利用,减少空间的开辟。
#include<iostream>
using namespace std;
class String
{
public:
String(const char* str = "")
{
cout << "构造函数()->String(const char* str = "")" << endl;
if (nullptr == str)
str = "";
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
String(const String & s)
: _str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
void swap(String& s)
{
std::swap(_str,s._str);
}
//移动构造
String(String&& s)noexcept
:_str(nullptr)
{
cout << "移动构造(资源移动)" << endl;
swap(s);
}
// 移动赋值
String& operator=(String&& s)noexcept
{
cout << "String& operator=(string s) -- 移动赋值(资源移动)" << endl;
swap(s);
return *this;
}
String& operator=(const String& s)
{
if (this != &s)
{
char* pTemp = new char[strlen(s._str) + 1];
strcpy(pTemp, s._str);
delete[] _str;
_str = pTemp;
}
return *this;
}
String operator+(const String& s)
{
char* pTemp = new char[strlen(_str) + strlen(s._str) + 1];
strcpy(pTemp, _str);
strcpy(pTemp + strlen(_str), s._str);
String strRet(pTemp);
return strRet;
}
~String()
{
if (_str) delete[] _str;
}
private:
char* _str;
};
int main()
{
String s1("hello");
String s2("world");
String s3=(s1 + s2);
s3 = move(s1);//强行转换成将亡值
return 0;
}
这里的move就是强制类型转化成一个将亡值,让其变成右值,好去调用我们这里的移动赋值(这里只是为了强行调用移动赋值,这样的代码其实是有问题的,会导致s1变成nullptr)。
move(s1)==static_cast<String&&>(s1);