一,综述
C++最早没有右值的概念,自从C++11以后引入右值概念。那么什么是右值呢?举例如下:
int a = 10;
a就是传统的变量,称为左值。10为常量,称为右值。左值是可以取得其地址的,右值是不能取得其地址的。我们可以用&a来取得变量a的地址,但10不能使用&符号,因此a为左值,10为右值。函数的返回值也是一个右值。
如函数:
int add(int a, int b)
{
int sum = a + b;
return sum;
}
此函数返回一个sum,我们无法取得sum的地址,因此函数的返回值是一个右值。
那么为什么C++ 11要引入右值的概念呢?我们先举一个例子。
二,没有右值引用的情况下的例子
先上代码:
#include <iostream>
using namespace std;
class MyBlob
{
private:
char* m_p;
int m_size;
public:
MyBlob(): m_p(nullptr), m_size(0) {}
MyBlob(int size)
{
m_p = new char[size];
m_size = size;
cout << "Construct " << m_size << " bytes" << endl;
}
~MyBlob()
{
if(!m_p)
{
delete[] m_p;
m_p = nullptr;
}
m_size = 0;
}
MyBlob(const MyBlob& b)
{
cout << "Copy Construct " << endl;
m_size = b.m_size;
m_p = new char[m_size];
memcpy(m_p, b.m_p, m_size);
}
MyBlob& operator=(const MyBlob& b) //拷贝赋值
{
cout << "Copy assignment......" << endl;
if (!m_p)
{
delete[] m_p;
}
m_size = b.m_size;
m_p = new char[m_size];
memcpy(m_p, b.m_p, m_size);
return *this;
}
MyBlob& operator=(MyBlob& b) //移动赋值
{
cout << "Move assignment ......" << endl;
if (!m_p)
{
delete[] m_p;
}
m_p = b.m_p;
m_size = b.m_size;
b.m_p = nullptr;
b.m_size = 0;
return *this;
}
void print()
{
cout << "this = " << static_cast<void*>(this) << ", addr " << static_cast<void*>(m_p) << ",size = " << m_size << endl;
}
};
int main()
{
MyBlob a(10);
MyBlob b1(100);
b1 = a; //调用移动赋值函数
MyBlob b(100);
const MyBlob& c = b;
MyBlob b2(1000);
b2 = c; //调用拷贝赋值函数
}
MyBlob类中重载了两次运算符“=”,第一个为拷贝赋值函数,第二个为移动赋值函数。移动赋值函数不重新分配内存,直接使用b的内存,同时把b的内存指针设置为null,这样就省去了拷贝过程,因此可以提高运行速度。
我们如何能分别调用拷贝赋值函数和移动赋值函数呢?上述代码中:b1 = c 会调用移动赋值函数,如果需要使用拷贝赋值函数,则需要使用如下代码:
MyBlob b(100);
const MyBlob& c = b;
MyBlob b2(1000);
b2 = c; //调用拷贝赋值函数
需要重新声明一个b的const引用,这样b2=c就会调用拷贝构造函数。这样是不是比较麻烦?这时该右值出场了。