信息服务类网站建设方案,西宁高端网站建设,好看的单页面网站模板免费下载,建设部建设厅报考网站能出现在赋值号左边的表达式称为“左值”#xff0c;不能出现在赋值号左边的表达式称为“右值”。一般来说#xff0c;左值是可以取地址的#xff0c;右值则不可以。
非 const 的变量都是左值。函数调用的返回值若不是引用#xff0c;则该函数调用就是右值。之前我们了解的…能出现在赋值号左边的表达式称为“左值”不能出现在赋值号左边的表达式称为“右值”。一般来说左值是可以取地址的右值则不可以。
非 const 的变量都是左值。函数调用的返回值若不是引用则该函数调用就是右值。之前我们了解的“引用”是引用变量的而变量是左值因此它们都是“左值引用”。
C11 新增了一种引用可以引用右值因而称为“右值引用”。无名的临时变量不能出现在赋值号左边因而是右值。右值引用就可以引用无名的临时变量。定义右值引用的格式如下
类型 引用名 右值表达式;例如
class A{};
A rl A(); //错误无名临时变量 A() 是右值因此不能初始化左值引用 r1
A r2 A(); //正确因 r2 是右值引用引入右值引用的主要目的是提高程序运行的效率。有些对象在复制时需要进行深复制深复制往往非常耗时。合理使用右值引用可以避免没有必要的深复制操作。例如下面的程序
#include iostream
#include string
#include cstring
using namespace std;
class String
{
public:char* str;String() : str(new char[1]) { str[0] 0; }String(const char* s) {str new char[strlen(s) 1];strcpy(str, s);}String(const String s) {//复制构造函数cout copy constructor called endl;str new char[strlen(s.str) 1];strcpy(str, s.str);}String operator (const String s) {//复制赋值号cout copy operator called endl;if (str ! s.str) {delete[] str;str new char[strlen(s.str) 1];strcpy(str, s.str);}return *this;}String(String s) : str(s.str) { //移动构造函数cout move constructor called endl;s.str new char[1];s.str[0] 0;}String operator (String s) { //移动赋值号cout move operator called endl;if (str ! s.str) {str s.str;s.str new char[1];s.str[0] 0;}return *this;}~String() { delete[] str; }
};
template class T
void MoveSwap(T a, T b) {T tmp(move(a)); //std::move(a) 为右值这里会调用移动构造函数a move(b); //move(b) 为右值因此这里会调用移动赋值号b move(tmp); //move(tmp) 为右值因此这里会调用移动赋值号
}
int main()
{String s;s String(this); //调用移动赋值号cout * * * * endl;cout s.str endl;String s1 hello, s2 world;MoveSwap(s1, s2); //调用一次移动构造函数和两次移动赋值号cout s2.str endl;return 0;
}程序的输出结果如下
move operator called
****
this
move constructor called
move operator called
move operator called
hello第 33 行重载了一个移动赋值号。它和第 19 行的复制赋值号的区别在于其参数是右值引用。在移动赋值号函数中没有执行深复制操作而是直接将对象的 str 指向了参数 s 的成员变量 str 指向的地方然后修改 s.str 让它指向别处以免 s.str 原来指向的空间被释放两次。
该移动赋值号函数修改了参数这会不会带来麻烦呢答案是不会。因为移动赋值号函数的形参是一个右值引用则调用该函数时实参一定是右值。右值一般是无名临时变量而无名临时变量在使用它的语句结束后就不再有用因此其值即使被修改也没有关系。
第 53 行如果没有定义移动赋值号则会导致复制赋值号被调用引发深复制操作。临时无名变量String(“this”)是右值因此在定义了移动赋值号的情况下会导致移动赋值号被调用。移动赋值号使得 s 的内容和 String(“this”) 一致然而却不用执行深复制操作因而效率比复制赋值号高。
虽然移动赋值号修改了临时变量 String(“this”)但该变量在后面已无用处因此这样的修改不会导致错误。
第 46 行使用了 C 11 中的标准模板 move。move 能接受一个左值作为参数返回该左值的右值引用。因此本行会用定义于第 28 行、以右值引用作为参数的移动构造函数来初始化 tmp。该移动构造函数没有执行深复制将 tmp 的内容变成和 a 相同然后修改 a。由于调用 MoveSwap 本来就会修改 a所以 a 的值在此处被修改不会产生问题。
第 47 行和第 48 行调用了移动赋值号在没有进行深复制的情况下完成了 a 和 b 内容的互换。对比 Swap 函数的以下写法
template class T
void Swap(T a, T b) {T tmp(a); //调用复制构造函数ab; //调用复制赋值号btmp; //调用复制赋值号
}Swap 函数执行期间会调用一次复制构造函数两次复制赋值号即一共会进行三次深复制操作。而利用右值引用使用 MoveSwap则可以在无须进行深复制的情况下达到相同的目的从而提高了程序的运行效率。