c++11之前,左值可以定义两种引用:
<1>T& 别名=rvalue
<2>const T& 别名=rvalue
区别:
int _tmain(int argc, _TCHAR* argv[])
{
const int & a = 10;//int & a = 10; 是错的,只有加入const才能直接引用一个常量
int b = 5,c = 6;
//二者都可以直接引用前面定义的两个变量
int & d = b;
const int & e = c;
d += 1;
//e += 1; 有const修饰的话不能对左值进行修改
c += 1;//但是能对其引用的右值进行修改
const int &f = b + c;//int &f=b+c;是错的,因为b+c只能看成一个数,而不是一个变量,那么就与上面第一行相同
}
c++11提出了非常量引用
T && 名称=rvalue;
int _tmain(int argc, _TCHAR* argv[])
{
int a = 10, b = 20;
int &&c = a + b;
int &&d = 100;
//可以随意加减
c = c + 10;
d = d + 10;
cout << "c=" << c << endl;
cout << "d=" << d << endl;
}
作用:
相当于把右值变成了一个持久的对象,这样提高了效率(相当于在后面的代码中可以直接用右值,一般情况下右值不能一直使用),在引用函数返回值时使用较多
深拷贝和浅拷贝
浅拷贝简单来说就是数据间的简单复制,指针指向同一块内存区
深拷贝创建不同的内存区并且把数据都复制过来,指针分别指向两块不同的内存区
下面这段代码是普通深拷贝构造函数与右值引用深拷贝构造函数并存,只有当参数为Foo && r;时才被认为是右值引用拷贝构造函数
class Foo{
private:
int *p;
public:
Foo(int x)
{
p = new int(x);
}
Foo(const Foo& r)
{
cout << "普通深拷贝构造函数" << endl;
p = new int(*r.p);
}
Foo(Foo &&r)//这里写成&r不会报错,但就变成了普通的深拷贝构造函数,但如果写&&再把前面的普通深拷贝函数删除,就会出现问题
{
cout << "右值引用拷贝构造函数" << endl;
p = r.p;
r.p = NULL;
}
~Foo()
{
if (p != NULL) delete p;
}
void show_p()
{
cout << "p=" << p << endl;
cout << "*p=" << *p << endl;
}
};
Foo fuct()
{
Foo foof(10);
return foof;
}
int _tmain(int argc, _TCHAR* argv[])
{
Foo f1(fuct());
f1.show_p();
Foo f2(f1);//这里调用的是普通深拷贝函数,删除后就会执行默认的浅拷贝(f2.p=f1.p),那么在析构时,f1释放了堆内存,而f2的p依然指向释放了的内存区,那么就会报错
f2.show_p();
return 0;
}
class Foo{
private:
int *p=NULL;
public:
Foo(){};
Foo(int x)
{
p = new int(x);
}
Foo(const Foo& r)
{
cout << "普通深拷贝构造函数" << endl;
p = new int(*r.p);
}
Foo(Foo && r)
{
cout << "右值引用拷贝构造函数" << endl;
p = r.p;
r.p = NULL;
}
Foo& operator=(Foo &r)
{
cout << "普通赋值运算符" << endl;
if (p != NULL) delete p;
p = new int(*r.p);
return *this;
}
Foo& operator=(Foo &&r)
{
cout << "右值引用赋值符" << endl;
if (p != NULL) delete p;//释放原来分配的内存空间
p = r.p;//共享内存区
r.p = NULL;
return *this;
}
~Foo()
{
if (p != NULL)
{
delete p;
p = NULL;
}
}
void show_p()
{
cout << "p=" << p << endl;
cout << "*p=" << *p << endl;
}
};
Foo fuct()//这里不能写成Foo &原因是foof会被析构,此引用无意义
{
Foo foof(10);
return foof;
}
int _tmain(int argc, _TCHAR* argv[])
{
Foo f1(fuct());//这里会调用一次右值引用拷贝
Foo f2;
f2 = fuct();//这里还会调用一次右值引用拷贝,原因是fuct()函数里的局部变量foof被析构之前,它会先构造一个临时对象将数据先拷贝出来。 再调用右值引用赋值符
return 0;
}
转移函数move()
T && move(T & var)
可以把一个左值变成右值引用
转移语义的使用可以方便的把一个对象转移给另一个对象,避免了中间变量的定义、拷贝和删除等过程,提高了效率
class Foo{
.
.
.
Foo& operator=(Foo &&r)
{
cout << "右值引用赋值符" << endl;
if (p != NULL) delete p;//释放原来分配的内存空间
p = r.p;//共享内存区
r.p = NULL;
return *this;
}
.
.
.
void show_p()
{
cout << "p=" << p << endl;
cout << "*p=" << *p << endl;
}
void show_ptr()
{
cout << p << endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Foo foo1(40);
Foo foo2(1);
foo2 = move(foo1);//等号右边被变成右值引用,所以这里调用的是右值引用赋值符,foo1的p指针被置为空,内存区传递给foo2
foo2.show_p();
foo1.show_ptr();
return 0;
}
参数完美转发模板
在模板函数中调用其它外部函数,怎样用模板内的参数为这些函数传递参数
问题:模板转发的值为左值,那么就不能调用需要参数为右值的外部函数
还有就是参数转发的值传递会占用内存空间,需要实现引用传递
template<typename T>
void Tmp(T && a)//右值引用
{
Func(/*forward<T>*/(a));//此函数可以把经模板转变成左值的右值还原
}
void Func(int & a)
{
cout << "success" << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
Tmp(10);//参数为右值
int a=10;
Tmp(a);//这里之所以可以用左值是其中有一个参数推导过程,(模板函数才有用,不然编译不能通过)
return 0;
}
完美转发函数模板
template<typename T>
void FuncT(T && a)
{
Func(forward<T>(a));
}
void Func(int & a)
{
cout << "lvalue" << endl;
}
void Func(int && a)
{
cout << "rvalue" << endl;
}
void Func(const int & a)
{
cout << "const lvalue" << endl;
}
void Func(const int && a)
{
cout << "const rvalue" << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
cout << "形参是右值 ";
FuncT(10);
cout << "形参是左值 ";
int x = 10;
FuncT(x);
cout << "形参是右值 ";
FuncT(move(x));
cout << "形参是左值 ";
const int b = 10;
FuncT(b);
cout << "形参是右值 ";
FuncT(move(b));
return 0;
}