基础2:值类型与右值引用
1.函数返回
在讲解右值之前,要知道下面这个函数要进行几次拷贝以及为什么?
int get_x()
{
int x = 20;
return x;
}
int a=get_x();
答案:两次
# 第一次
int tmp=a;
# 第二次
int x=tmp;
2.左值与右值
🍏2.1 能取地址操作的就是左值
int gx = 10;
int get_gx()
{
return gx;
}
int get_x()
{
int x = 20;
return x;
}
get_x(); //右值
get_gx(); //右值,因为返回值是一个tmp变量
延伸一下,对 x++ 取地址和 ++x 取地址哪个可以成功?
int* p=&x++; // 错误
// 后++等价于
/*
int func(int &a) {
int b=a;
a=a+1;
return b;
}
*/
int *q=&++x; // 正确
// 前++等价于
/*
int func(int &a) {
a=a+1;
return a;
}
*/
🍎 2.2 左值和右值的爱恨情仇
- 左值引用不接受右值,所以只能用右值引用来接收, Type&&
int &y=-10; // 错误
int &&y=-10; // 正确
int &a=get_x(); // 错误
int &&a=get_x(); // 正确
- 如何将左值转换为右值
- 移动语义:std::move 可以将左值转换为右值
- static_cast<type &&>(xxx)
3. 赋值操作只有拷贝这一种解法吗
- 拷贝操作:我想与一名10年水平的码农一样强,那我应该学习10年
- 引用操作:与它共享一份大脑
- 移动操作:
- 这个10年的码农今年150岁了去世了,把它的大脑移植过来
- 这位码农150岁,干净长生不老了,弄死后移植过来
🍐 例子
拷贝操作:
#include <iostream>
#include <string.h>
#include <string>
// copy from 【现代 C++ 语言核心特性解析】
class BigMemoryPool
{
public:
static const int PoolSize = 4096;
BigMemoryPool() : pool_(new char[PoolSize]) {}
~BigMemoryPool()
{
if (pool_ != nullptr)
{
delete[] pool_;
}
}
BigMemoryPool(const BigMemoryPool &other) : pool_(new char[PoolSize])
{
std::cout << "copy" << std::endl;
memcpy(pool_, other.pool_, PoolSize);
}
private:
char *pool_;
};
BigMemoryPool getPool()
{
BigMemoryPool memoryPool;
return memoryPool;
}
int main()
{
BigMemoryPool bbb = getPool();
}
输出
copy
copy
将上述代码加入移动构造函数(这个10年的码农今年150岁了去世了,把它的大脑移植过来)
class BigMemoryPool
{
public:
static const int PoolSize = 4096;
BigMemoryPool() : pool_(new char[PoolSize]) {}
~BigMemoryPool()
{
if (pool_ != nullptr)
{
delete[] pool_;
}
}
BigMemoryPool(BigMemoryPool &&other)
{
std::cout << "move" << std::endl;
pool_ = other.pool_;
other.pool_ = nullptr;
}
BigMemoryPool(const BigMemoryPool &other) : pool_(new char[PoolSize])
{
std::cout << "copy" << std::endl;
memcpy(pool_, other.pool_, PoolSize);
}
private:
char *pool_;
};
int main()
{
BigMemoryPool bbb = getPool(); // 这个10年的码农今年150岁了去世了,把它的大脑移植过来
}
输出
move
move
这位码农150岁,干净长生不老了,弄死后移植过来
BigMemoryPool aaa;
BigMemoryPool ccc = std::move(aaa);
输出
move
4. Notes:
- 纯右值也可以std::move
- 类中未实现移动构造,std::move之后仍是拷贝
- 右值引用仍是左值
int x=10; int &&z=std::move(x); &z; // 左值
- 右值绑定在左值上连移动构造都不会发生
BigMemoryPool aaa; BigMemoryPool &&ccc = std::move(aaa);