右值
传统 C++ 只能引用左值,例如:
int n;
int * pt = new int;
const int b = 101; // can't assign to b, but &b is valid
int & rn = n; // n identifies datum at address &n
int & rt = *pt; // *pt identifies datum at address pt
const int & rb = b; // b identifies const datum at address &b
前面的这些变量,我们很容易理解到,能够取他们的地址。但是还有一些变量,我们在左值这个概念下无法获取到地址。
int & r1 = 13;
int & r2 = x + y;
double & r3 = std::sqrt (2.0) ;
上面的这种写法一定会报错,我们无法通过左值引用获取它的地址。这个时候引入了右值引用的概念,右值引用则可以获取到它们的地址。右值引用的方式如下:
int && r1 = 13;
int && r2 = x + y;
double && r3 = std::sqrt (2.0) ;
右值引用主要的能力是能够使用到临时内存区域。这个时候对高性能编程就有非常大的帮助了。对于高性能编程除了理解右值引用外,还需要理解移动。
移动
移动的概念在我看来就是把临时内存区域加以利用。与其说是移动,不如说是复制右值。还是以例子来看移动语义吧。下面的例子有点长,内容也有些分散。下面主要讲内存的分配情况。
// stdmove.cpp -- using std::move()
#include <iostream>
#include <utility>
// use the following for g++4.5
// #define nullptr 0
// interface
class Useless {
private:
int n; // number of elements
char *pc; // pointer to data
static int ct; // number of objects
void ShowObject() const;
public:
Useless();
explicit Useless(int k);
Useless(int k, char ch);
Useless(const Useless &f); // regular copy constructor
Useless(Useless &&f); // move constructor
~Useless();
Useless operator+(const Useless &f) const;
Useless &operator=(const Useless &f); // copy assignment
Useless &operator=(Useless &&f); // move assignment
void ShowData() const;
};
// implementation
int Useless::ct = 0;
Useless::Useless() {
++ct;
n = 0;
pc = nullptr;
}
Useless::Useless(int k) : n(k) {
++ct;
pc = new char[n];
}
Useless::Useless(int k, char ch) : n(k) {
++ct;
pc = new char[n];
for (int i = 0; i < n; i++) pc[i] = ch;
}
Useless::Useless(const Useless &f) : n(f.n) {
++ct;
pc = new char[n];
for (int i = 0; i < n; i++) pc[i] = f.pc[i];
}
Useless::Useless(Useless &&f) : n(f.n) {
++ct;
pc = f.pc; // steal address
f.pc = nullptr; // give old object nothing in return
f.n = 0;
}
Useless::~Useless() {
delete[] pc;
}
Useless &Useless::operator=(const Useless &f) // copy assignment
{
std::cout << "copy assignment operator called:\n";
if (this == &f) return *this;
delete[] pc;
n = f.n;
pc = new char[n];
for (int i = 0; i < n; i++) pc[i] = f.pc[i];
return *this;
}
Useless &Useless::operator=(Useless &&f) // move assignment
{
std::cout << "move assignment operator called:\n";
if (this == &f) return *this;
delete[] pc;
n = f.n;
pc = f.pc;
f.n = 0;
f.pc = nullptr;
return *this;
}
Useless Useless::operator+(const Useless &f) const {
Useless temp = Useless(n + f.n);
for (int i = 0; i < n; i++) temp.pc[i] = pc[i];
for (int i = n; i < temp.n; i++) temp.pc[i] = f.pc[i - n];
return temp;
}
void Useless::ShowObject() const {
std::cout << "Number of elements: " << n;
std::cout << " Data address: " << (void *)pc << std::endl;
}
void Useless::ShowData() const {
if (n == 0)
std::cout << "(object empty)";
else
for (int i = 0; i < n; i++) std::cout << pc[i];
std::cout << std::endl;
}
// application
int main() {
using std::cout;
{
Useless one(10, 'x');
Useless two = one + one; // calls move constructor
cout << "object one: ";
one.ShowData();
cout << "object two: ";
two.ShowData();
Useless three, four;
cout << "three = one\n";
three = one; // automatic copy assignment
cout << "now object three = ";
three.ShowData();
cout << "and object one = ";
one.ShowData();
cout << "four = one + two\n";
four = one + two; // automatic move assignment
cout << "now object four = ";
four.ShowData();
cout << "four = move(one)\n";
four = std::move(one); // forced move assignment
cout << "now object four = ";
four.ShowData();
cout << "and object one = ";
one.ShowData();
}
std::cin.get();
}
上面的例子是 C++ Primer Plus 里面的,从这个例子里,我们能够学习到很多知识。
在上述代码中的 112 行
Useless two = one + one; // calls move constructor
通过增加日志我们会发现如下几种情况(MAC 上执行):
能够看到 two = one + one 执行了 Useless Useless::operator+(const Useless &f) 函数
这里的移动语义并没有真的执行到 Useless::Useless(Useless &&f) 而是将 Useless Useless::operator+(const Useless &f) 返回值 temp 的地址直接赋值给 two。
其中第 1 点很容易理解,需要注意的是,左边的 one 调用了 Useless Useless::operator+(const Useless &f) 语句,右边的 one 相当于 const Useless &f 传入进来。
第 2 点也可以认为是移动语义,但是这里并没有真的调用移动复制构造函数,而是直接将 temp 的地址赋值给 two。
我们看 four = one + two,这时调用了 Useless::Useless(Useless &&f)
然后 four = std::move(one),也是调用了移动语义 Useless::Useless(Useless &&f)
其他都相对容易理解,在这里就不再多做解释了。