右值引用主要作用是解决大对象在作为函数返回值返回时的深度拷贝问题,以及大对象之间的快速复制。
比如,
std::vector<int> fun()
{
std::vector<int> a;
...
return a;
}
会自动把a的指针赋值给临时变量,而不会深度拷贝。赋值后,a就废了。
这个 && 符号主要作为构造函数的参数出现,其他的使用方法虽然有的可以,但是很少见。
1. 测试代码
我们开发一个测试代码。
#include <iostream>
#include <algorithm>
using namespace std;
#define BUFFER_SIZE 10240
class A {
public:
A() {
m_ins = ++instance;
cout << m_ins << "\tA() object created.\n";
};
~A() {
cout << m_ins << "\t~A()\n";
if (c)
{
delete c;
c = nullptr;
cout << m_ins << "\t\tc is deleted.\n";
}
else
cout << m_ins << "\t\tc is empty.\n";
};
A(const A & T) {
m_ins = ++instance;
cout << m_ins << "\tA(const A & T)\n";
if (T.c)
{
c = new char[BUFFER_SIZE];
cout << m_ins << "\t\tc is allocated.\n";
copy(T.c, T.c + BUFFER_SIZE, c);
cout << m_ins << "\t\tc is copied from " << T.m_ins << "\n";
}
};
A(A && T) {
m_ins = ++instance;
cout << m_ins << "\tA(A && T)\n";
c = T.c;
T.c = nullptr;
cout << m_ins << "\t\tc is moved from " << T.m_ins << "\n";
};
A& operator = (const A & T)
{
cout << m_ins << "\toperator=(const A & T)\n";
if (c)
{
delete[] c;
c = nullptr;
cout << m_ins << "\t\tc is deleted.\n";
}
if (T.c)
{
c = new char[BUFFER_SIZE];
cout << m_ins << "\t\tc is allocated.\n";
copy(T.c, T.c + BUFFER_SIZE, c);
cout << m_ins << "\t\tc is copied from " << T.m_ins << "\n";
}
return *this;
}
A& operator = (A && T)
{
cout << m_ins << "\toperator=(A && T)\n";
if (c)
{
delete[] c;
c = nullptr;
cout << m_ins << "\t\tc is deleted.\n";
}
c = T.c;
T.c = nullptr;
cout << m_ins << "\t\tc is moved from " << T.m_ins << "\n";
return *this;
}
public:
void alloc() {
if (c)
return;
cout << m_ins<< "\talloc()\n";
c = new char[BUFFER_SIZE];
cout << m_ins << "\t\tc is allocated.\n";
}
private:
char * c = nullptr;
int m_ins = -1;
static int instance;
};
int A::instance = -1;
A fun()
{
cout <<"A fun()\n";
A a;
a.alloc();
cout <<"A fun() returnning\n";
return a;
}
int main()
{
cout << "Call fun.\n";
A a = fun();
cout << "define 4 instances.\n";
A b, c, d, e;
cout << "Assign.\n";
b = a;
cout << "Assign move.\n";
c = move(a);
cout << "Assign fun.\n";
e = d = fun();
cout << "Exiting.\n";
return 0;
}
1.2 Visual Studio 2017输出
1.2.1 Debug模式下输出
在debug下,输出为:
Call fun.
A fun()
0 A() object created.
0 alloc()
0 c is allocated.
A fun() returnning
1 A(A && T)
1 c is moved from 0
0 ~A()
0 c is empty.
define 4 instances.
2 A() object created.
3 A() object created.
4 A() object created.
5 A() object created.
Assign.
2 operator=(const A & T)
2 c is allocated.
2 c is copied from 1
Assign move.
3 operator=(A && T)
3 c is moved from 1
Assign fun.
A fun()
6 A() object created.
6 alloc()
6 c is allocated.
A fun() returnning
7 A(A && T)
7 c is moved from 6
6 ~A()
6 c is empty.
4 operator=(A && T)
4 c is moved from 7
5 operator=(const A & T)
5 c is allocated.
5 c is copied from 4
7 ~A()
7 c is empty.
Exiting.
5 ~A()
5 c is deleted.
4 ~A()
4 c is deleted.
3 ~A()
3 c is deleted.
2 ~A()
2 c is deleted.
1 ~A()
1 c is empty.
请按任意键继续. . .
1.2.2 Release 模式下输出
在Release下,系统做了更加智能的优化。
Call fun.
A fun()
0 A() object created.
0 alloc()
0 c is allocated.
A fun() returnning
define 4 instances.
1 A() object created.
2 A() object created.
3 A() object created.
4 A() object created.
Assign.
1 operator=(const A & T)
1 c is allocated.
1 c is copied from 0
Assign move.
2 operator=(A && T)
2 c is moved from 0
Assign fun.
A fun()
5 A() object created.
5 alloc()
5 c is allocated.
A fun() returnning
3 operator=(A && T)
3 c is moved from 5
4 operator=(const A & T)
4 c is allocated.
4 c is copied from 3
5 ~A()
5 c is empty.
Exiting.
4 ~A()
4 c is deleted.
3 ~A()
3 c is deleted.
2 ~A()
2 c is deleted.
1 ~A()
1 c is deleted.
0 ~A()
0 c is empty.
请按任意键继续. . .
release 比 debug 少创建了2个变量。
1.3 GNU C/C++ 7.3输出
GNUC++ 输出在debug、release下一致:
Call fun.
A fun()
0 A() object created.
0 alloc()
0 c is allocated.
A fun() returnning
define 4 instances.
1 A() object created.
2 A() object created.
3 A() object created.
4 A() object created.
Assign.
1 operator=(const A & T)
1 c is allocated.
1 c is copied from 0
Assign move.
2 operator=(A && T)
2 c is moved from 0
Assign fun.
A fun()
5 A() object created.
5 alloc()
5 c is allocated.
A fun() returnning
3 operator=(A && T)
3 c is moved from 5
4 operator=(const A & T)
4 c is allocated.
4 c is copied from 3
5 ~A()
Exiting.
4 ~A()
4 c is deleted.
3 ~A()
3 c is deleted.
2 ~A()
2 c is deleted.
1 ~A()
1 c is deleted.
0 ~A()
2 不设置右值引用构造的输出
如果我们注释掉代码中所有右值引用的部分:
#include <iostream>
#include <algorithm>
using namespace std;
#define BUFFER_SIZE 10240
class A {
public:
A() {
m_ins = ++instance;
cout << m_ins << "\tA() object created.\n";
};
~A() {
cout << m_ins << "\t~A()\n";
if (c)
{
delete c;
c = nullptr;
cout << m_ins << "\t\tc is deleted.\n";
}
else
cout << m_ins << "\t\tc is empty.\n";
};
A(const A & T) {
m_ins = ++instance;
cout << m_ins << "\tA(const A & T)\n";
if (T.c)
{
c = new char[BUFFER_SIZE];
cout << m_ins << "\t\tc is allocated.\n";
copy(T.c, T.c + BUFFER_SIZE, c);
cout << m_ins << "\t\tc is copied from " << T.m_ins << "\n";
}
};
A& operator = (const A & T)
{
cout << m_ins << "\toperator=(const A & T)\n";
if (c)
{
delete[] c;
c = nullptr;
cout << m_ins << "\t\tc is deleted.\n";
}
if (T.c)
{
c = new char[BUFFER_SIZE];
cout << m_ins << "\t\tc is allocated.\n";
copy(T.c, T.c + BUFFER_SIZE, c);
cout << m_ins << "\t\tc is copied from " << T.m_ins << "\n";
}
return *this;
}
public:
void alloc() {
if (c)
return;
cout << m_ins<< "\talloc()\n";
c = new char[BUFFER_SIZE];
cout << m_ins << "\t\tc is allocated.\n";
}
private:
char * c = nullptr;
int m_ins = -1;
static int instance;
};
int A::instance = -1;
A fun()
{
cout <<"A fun()\n";
A a;
a.alloc();
cout <<"A fun() returnning\n";
return a;
}
int main()
{
cout << "Call fun.\n";
A a = fun();
cout << "define 4 instances.\n";
A b, c, d, e;
cout << "Assign.\n";
b = a;
cout << "Assign move.\n";
c = move(a);
cout << "Assign fun.\n";
e = d = fun();
cout << "Exiting.\n";
return 0;
}
再试试看:
2.1 Visual Studio
2.1.1 debug
可以发现,出现了大量的内存分配。
Call fun.
A fun()
0 A() object created.
0 alloc()
0 c is allocated.
A fun() returnning
1 A(const A & T)
1 c is allocated.
1 c is copied from 0
0 ~A()
0 c is deleted.
define 4 instances.
2 A() object created.
3 A() object created.
4 A() object created.
5 A() object created.
Assign.
2 operator=(const A & T)
2 c is allocated.
2 c is copied from 1
Assign move.
3 operator=(const A & T)
3 c is allocated.
3 c is copied from 1
Assign fun.
A fun()
6 A() object created.
6 alloc()
6 c is allocated.
A fun() returnning
7 A(const A & T)
7 c is allocated.
7 c is copied from 6
6 ~A()
6 c is deleted.
4 operator=(const A & T)
4 c is allocated.
4 c is copied from 7
5 operator=(const A & T)
5 c is allocated.
5 c is copied from 4
7 ~A()
7 c is deleted.
Exiting.
5 ~A()
5 c is deleted.
4 ~A()
4 c is deleted.
3 ~A()
3 c is deleted.
2 ~A()
2 c is deleted.
1 ~A()
1 c is deleted.
请按任意键继续. . .
2.1.2 Release
Call fun.
A fun()
0 A() object created.
0 alloc()
0 c is allocated.
A fun() returnning
define 4 instances.
1 A() object created.
2 A() object created.
3 A() object created.
4 A() object created.
Assign.
1 operator=(const A & T)
1 c is allocated.
1 c is copied from 0
Assign move.
2 operator=(const A & T)
2 c is allocated.
2 c is copied from 0
Assign fun.
A fun()
5 A() object created.
5 alloc()
5 c is allocated.
A fun() returnning
3 operator=(const A & T)
3 c is allocated.
3 c is copied from 5
4 operator=(const A & T)
4 c is allocated.
4 c is copied from 3
5 ~A()
5 c is deleted.
Exiting.
4 ~A()
4 c is deleted.
3 ~A()
3 c is deleted.
2 ~A()
2 c is deleted.
1 ~A()
1 c is deleted.
0 ~A()
0 c is deleted.
请按任意键继续. . .
可以看到,尽管临时变量比Debug还是少,但是内存拷贝仍旧无法避免。
扫描二维码关注公众号,回复:
8951786 查看本文章
2.3 GNU C++
我们看看:
A fun()
0 A() object created.
0 alloc()
0 c is allocated.
A fun() returnning
define 4 instances.
1 A() object created.
2 A() object created.
3 A() object created.
4 A() object created.
Assign.
1 operator=(const A & T)
1 c is allocated.
1 c is copied from 0
Assign move.
2 operator=(const A & T)
2 c is allocated.
2 c is copied from 0
Assign fun.
A fun()
5 A() object created.
5 alloc()
5 c is allocated.
A fun() returnning
3 operator=(const A & T)
3 c is allocated.
3 c is copied from 5
4 operator=(const A & T)
4 c is allocated.
4 c is copied from 3
5 ~A()
5 c is deleted.
Exiting.
4 ~A()
4 c is deleted.
3 ~A()
3 c is deleted.
2 ~A()
2 c is deleted.
1 ~A()
1 c is deleted.
0 ~A()
0 c is deleted.
3 总结
看来,右值引用真的对避免内存拷贝有大作用!自己在实现类的时候,一定要实现右值引用哦!
现代STL库都有很强的优化设计,基本上可以避免深度拷贝了。