提起左右值引用我就头疼
左值:
1、在内存中开辟了空间的便叫左值
2、左值不一定可以赋值 如字符串常量
3、左值可以取地址
右值:
1、在内存中没有开辟空间的
2、右值无法取地址
如:
立即数(1,2,3)
函数返回值 直接返回非类型引用
Lvalue getT() { return Lvalue(); };
int get() { return c; };
返回的这个int值或Lvalue类型对象就是一个右值 因为没有开辟空间
3、将亡值
什么叫将亡值 我的理解是一种标志 告诉说这个对象我要销毁
Lvalue&& getR(); 函数返回值是右值引用 返回的这个对象 就叫将亡对象 即将销毁
Lvalue c;
static_cast<Lvalue&&>(c); 转换返回的操作数也叫将亡值
我的理解就是告诉我们 这个值是要销毁的 推荐使用一个新对象去存储
什么叫左值引用? 其实就是 这个对象所代表的内存空间的另一个名字 在编译器内部
本质上是指针实现的
对左值引用取地址 所取的是引用对象的地址值
Lvalue d;//左值
const Lvalue& b2 = d;//绑定左值
Lvalue& b3 = d;//绑定左值
&b2 == &d;
&b3 == &d;
而右值引用 则引用的是右值
Rvalue r;//左值
Rvalue&& r1 = Rvalue();//绑定右值
//r1 是左值 有名的右值引用 为 左值
const Rvalue&& r2 = static_cast<Rvalue&&>(r1);//绑定右值(实际上是左值转换到右值)成将亡值
Rvalue&& r3 = static_cast<Rvalue&&>(r);
记住有名字的右值引用 是左值 所以可以取地址
std::cout << "r:" << &r << endl;
std::cout << "r1:" << &r1 << endl;
std::cout << "r2:" << &r2 << endl;
std::cout << "r3:" << &r3 << endl;
&r == &r3;
&r1 == &r2;
由于是引用 当然可以改变所引用的对象
struct Rvalue
{
};
struct Lvalue : public Rvalue
{
int c;
Lvalue() = default;
Lvalue(int Inc) :c(Inc) {};
Lvalue getT() { return Lvalue(); };
Lvalue&& getR() {
auto c = new Lvalue(100);
return static_cast<Lvalue&&>(*c);
};
int get() { return c; };
};
std::cout << "" << endl;
Lvalue op(50);
std::cout << "op.c:" << op.c << endl;
Lvalue& Lop = op;
Lop.c = 100;
std::cout << "左值引用修改"<< endl;
std::cout << "op.c:" << op.c << endl;
Lvalue& Rop = op;
Lop.c = 800;
std::cout << "右值引用修改" << endl;
std::cout << "op.c:" << op.c << endl;
这么一看左右值引用都是一样的感觉
他们主要体现在函数上
struct Lvalue : public Rvalue
{
int c;
int* p;
Lvalue() = default;
Lvalue(int Inc) :c(Inc), p(new int()) {};
Lvalue& operator=(Lvalue& lc)
{
c = lc.c;
p = new int;
*p = * lc.p;
return *this;
};
Lvalue& operator=(const Lvalue& lc)
{
c = lc.c;
p = new int;
*p = *lc.p;
return *this;
};
Lvalue& operator=(Lvalue&& lc)
{
c = lc.c;
p = lc.p;
lc.p = nullptr;
return *this;
};
Lvalue& operator=(const Lvalue&& lc)
{
Lvalue&& Rc = const_cast<Lvalue&&> (lc);
c = Rc.c;
p = Rc.p;
Rc.p = nullptr;
return *this;
};
Lvalue getT() { return Lvalue(); };
Lvalue&& getR() {
auto c = new Lvalue(100);
return static_cast<Lvalue&&>(*c);
};
int get() { return c; };
};
注意那几个 operator= 重载赋值运算符的函数
这其实就是所谓的 深拷贝 浅拷贝
//深拷贝
Lvalue& operator=(Lvalue& lc)
{
c = lc.c;
p = new int; //重新开辟了堆空间
*p = * lc.p; //将 lc.p 中的int值 存入新开的堆空间中
return *this;
};
//浅拷贝
Lvalue& operator=(Lvalue&& lc)
{
c = lc.c;
p = lc.p;
//没有开辟新的堆空间 直接拿到 将亡值 lc 的 p所存储的内存地址 达到复用的目的
// 这也是他们所说的窃取资源
lc.p = nullptr;
return *this;
};
Lvalue& operator=(Lvalue& lc)
{
std::cout << "Lvalue& operator=(Lvalue& lc)" << endl;
c = lc.c;
delete p;
p = new int;
*p = * lc.p;
return *this;
};
Lvalue& operator=(const Lvalue& lc)
{
std::cout << "Lvalue& operator=(const Lvalue& lc)" << endl;
c = lc.c;
delete p;
p = new int;
*p = *lc.p;
return *this;
};
Lvalue& operator=(Lvalue&& lc)
{
std::cout << "Lvalue& operator=(Lvalue&& lc) " << endl;
c = lc.c;
delete p;
p = lc.p;
lc.p = nullptr;
return *this;
};
Lvalue& operator=(const Lvalue&& lc)
{
std::cout << "Lvalue& operator=(const Lvalue&& lc) " << endl;
Lvalue&& Rc = const_cast<Lvalue&&> (lc);
c = Rc.c;
delete p;
p = Rc.p;
Rc.p = nullptr;
return *this;
};
调用处
Lvalue t1(100);
*t1.p = 80;
Lvalue t2(200);
*t2.p = 800;
std::cout << "" << endl;
std::cout << "初始化时" << endl;
std::cout << "t1.c:" << t1.c << endl;
std::cout << "t1.p:" << t1.p << endl;
std::cout << "*t1.p:" << *t1.p << endl;
std::cout << "" << endl;
std::cout << "t2.c:" << t2.c << endl;
std::cout << "t12.p:" << t2.p << endl;
std::cout << "*t2.p:" << *t2.p << endl;
std::cout << "" << endl;
std::cout << "t1 = t2" << endl;
t1 = t2;//Lvalue& operator=(Lvalue& lc) ;
std::cout << "t1.c:" << t1.c << endl;
std::cout << "t1.p:" << t1.p << endl;
std::cout << "*t1.p:" << *t1.p << endl;
std::cout << "" << endl;
std::cout << "t2.c:" << t2.c << endl;
std::cout << "t12.p:" << t2.p << endl;
std::cout << "*t2.p:" << *t2.p << endl;
std::cout << "" << endl;
std::cout << "t1 = static_cast<Lvalue&&>(t2)" << endl;
t1 = static_cast<Lvalue&&>(t2);
//Lvalue& operator=(Lvalue&& lc) 如果没有 就考虑 Lvalue& operator=(const Lvalue&& lc)
如果还没有 就调用Lvalue& operator=(const Lvalue& lc)
std::cout << "t1.c:" << t1.c << endl;
std::cout << "t1.p:" << t1.p << endl;
std::cout << "*t1.p:" << *t1.p << endl;
std::cout << "" << endl;
std::cout << "t2.c:" << t2.c << endl;
std::cout << "t12.p:" << t2.p << endl;
//std::cout << "*t2.p:" << *t2.p << endl; //访问空指针是错误的
我们注释掉
/* Lvalue& operator=(Lvalue&& lc)
{
std::cout << "Lvalue& operator=(Lvalue&& lc) " << endl;
c = lc.c;
delete p;
p = lc.p;
lc.p = nullptr;
return *this;
};
*/
Lvalue& operator=(const Lvalue&& lc)
{
std::cout << "Lvalue& operator=(const Lvalue&& lc) " << endl;
Lvalue&& Rc = const_cast<Lvalue&&> (lc);
c = Rc.c;
delete p;
p = Rc.p;
Rc.p = nullptr;
return *this;
};
看调用 Lvalue& operator=(const Lvalue&& lc)
我们继续注释
/* Lvalue& operator=(Lvalue&& lc)
{
std::cout << "Lvalue& operator=(Lvalue&& lc) " << endl;
c = lc.c;
delete p;
p = lc.p;
lc.p = nullptr;
return *this;
};
*/
/*
Lvalue& operator=(const Lvalue&& lc)
{
std::cout << "Lvalue& operator=(const Lvalue&& lc) " << endl;
Lvalue&& Rc = const_cast<Lvalue&&> (lc);
c = Rc.c;
delete p;
p = Rc.p;
Rc.p = nullptr;
return *this;
};
*/
看调用与结果
这就是右值引用存在的价值
告诉大家这个对象需要销毁 你怎么处理看你的了
如果只用左值引用 无法很好的区分 因为cosnt lvalue&& 即可左值也可右值
你不知道传进来的这个对象是 const 修饰的对象 还是 要表示的销毁对象
我们也看到了引用是可以直接修改对象的 所以也是会有点问题 有些人不按照规则来
这样的函数 是有隐患的 我的本意是 这个返回值是即将销毁的值 然而外部可修改
Lvalue&& getR() {
auto c = new Lvalue(100);
return static_cast<Lvalue&&>(*c);
};
折叠引用 使用类型别名 using typedef 即可达到折叠引用的目的
using ob = Lvalue&;
typedef Lvalue& ob1;
using ou = Lvalue&&;
typedef Lvalue&& ou1;
Lvalue k;
ob& o = k;//左值引用
ob && o1= k;//左值引用
ou& o2 = k;//左值引用
ou&& o3 = (Lvalue&&)k;//右值引用
折叠引用只有一个规则:
右值引用的右值引用 才是右值引用 其余都为左值引用
&& && 右值引用
& && 左值引用
&& & 左值引用
& & 左值引用
而我们所知晓的 std::forward 完美转发 返回值就是 &&
用了点折叠引用
好了 到最后了
我们说一下
空类 和 继承 一个空类 内存大小是什么样的
Lvalue d;//左值
Rvalue r;//左值
std::cout<< sizeof(d)<<endl;//4 继承一个空类 空类所占的1字节被优化掉
std::cout << sizeof(r) << endl;//1
写完了
对了知乎大佬的参考 左右值引用的要点
(3 条消息) c++为什么要搞个引用岀来,特别是右值引用,感觉破坏了语法的简洁和条理,拷贝一个指针不是很好吗? - 知乎 (zhihu.com)
最后参考代码
struct Rvalue
{
};
struct Lvalue : public Rvalue
{
int c;
int* p;
Lvalue() = default;
Lvalue(int Inc) :c(Inc), p(new int()) {};
Lvalue& operator=(Lvalue& lc)
{
std::cout << "Lvalue& operator=(Lvalue& lc)" << endl;
c = lc.c;
delete p;
p = new int;
*p = * lc.p;
return *this;
};
Lvalue& operator=(const Lvalue& lc)
{
std::cout << "Lvalue& operator=(const Lvalue& lc)" << endl;
c = lc.c;
delete p;
p = new int;
*p = *lc.p;
return *this;
};
/*Lvalue& operator=(Lvalue&& lc)
{
std::cout << "Lvalue& operator=(Lvalue&& lc) " << endl;
c = lc.c;
delete p;
p = lc.p;
lc.p = nullptr;
return *this;
};*/
/*Lvalue& operator=(const Lvalue&& lc)
{
std::cout << "Lvalue& operator=(const Lvalue&& lc) " << endl;
Lvalue&& Rc = const_cast<Lvalue&&> (lc);
c = Rc.c;
delete p;
p = Rc.p;
Rc.p = nullptr;
return *this;
};*/
Lvalue getT() { return Lvalue(); };
Lvalue&& getR() {
auto c = new Lvalue(100);
return static_cast<Lvalue&&>(*c);
};
int get() { return c; };
};
int main(int line, const char* arg[])
{
Lvalue d;//左值
Rvalue r;//左值
std::cout<< sizeof(d)<<endl;//4 继承一个空类 空类所占的1字节被优化掉
std::cout << sizeof(r) << endl;//1
//const 修饰的左值引用 既可以绑定左值又可以绑定右值 Lvalue() 右值 无名的
const Lvalue& b = Lvalue();//绑定右值
const Lvalue& b2 = d;//绑定左值
Lvalue& b3 = d;//绑定左值
Rvalue&& r1 = Rvalue();//绑定右值
//r1 是左值 有名的右值引用 为 左值
const Rvalue&& r2 = static_cast<Rvalue&&>(r1);//绑定右值(实际上是左值转换到右值)
Rvalue&& r3 = static_cast<Rvalue&&>(r);
//std::move(r);
std::cout << "左值"<< endl;
std::cout << "d:"<< & d << endl;
std::cout << "b2:"<< &b2 << endl;
std::cout << "b:" << &b << endl;
std::cout << "b3:" << &b3 << endl;
std::cout << "右值"<< endl;
std::cout << "r:" << &r << endl;
std::cout << "r1:" << &r1 << endl;
std::cout << "r2:" << &r2 << endl;
std::cout << "r3:" << &r3 << endl;
//td::cout << "纯右值" << &Rvalue() << endl; //error
//&d.getT();
decltype(auto) v = d.getR();
v.c = 50;
std::cout << "v:" << &v << endl;
auto str = "dadad";
std::cout << "字符串:" << &"dadad" << endl;
std::cout << "str:" << &str << endl;
std::cout << "" << endl;
Lvalue op(50);
std::cout << "op.c:" << op.c << endl;
Lvalue& Lop = op;
Lop.c = 100;
std::cout << "左值引用修改"<< endl;
std::cout << "op.c:" << op.c << endl;
Lvalue& Rop = op;
Lop.c = 800;
std::cout << "右值引用修改" << endl;
std::cout << "op.c:" << op.c << endl;
int&& i = 10;
i = 80;
Lvalue t1(100);
*t1.p = 80;
Lvalue t2(200);
*t2.p = 800;
std::cout << "" << endl;
std::cout << "初始化时" << endl;
std::cout << "t1.c:" << t1.c << endl;
std::cout << "t1.p:" << t1.p << endl;
std::cout << "*t1.p:" << *t1.p << endl;
std::cout << "" << endl;
std::cout << "t2.c:" << t2.c << endl;
std::cout << "t12.p:" << t2.p << endl;
std::cout << "*t2.p:" << *t2.p << endl;
std::cout << "" << endl;
std::cout << "t1 = t2" << endl;
t1 = t2;//Lvalue& operator=(Lvalue& lc) ;
std::cout << "t1.c:" << t1.c << endl;
std::cout << "t1.p:" << t1.p << endl;
std::cout << "*t1.p:" << *t1.p << endl;
std::cout << "" << endl;
std::cout << "t2.c:" << t2.c << endl;
std::cout << "t12.p:" << t2.p << endl;
std::cout << "*t2.p:" << *t2.p << endl;
std::cout << "" << endl;
std::cout << "t1 = static_cast<Lvalue&&>(t2)" << endl;
t1 = static_cast<Lvalue&&>(t2); //Lvalue& operator=(Lvalue&& lc) 如果没有 就考虑 Lvalue& operator=(const Lvalue&& lc) 如果还没有 就调用Lvalue& operator=(const Lvalue& lc)
std::cout << "t1.c:" << t1.c << endl;
std::cout << "t1.p:" << t1.p << endl;
std::cout << "*t1.p:" << *t1.p << endl;
std::cout << "" << endl;
std::cout << "t2.c:" << t2.c << endl;
std::cout << "t12.p:" << t2.p << endl;
//std::cout << "*t2.p:" << *t2.p << endl; //访问空指针是错误的
using ob = Lvalue&;
typedef Lvalue& ob1;
using ou = Lvalue&&;
typedef Lvalue&& ou1;
Lvalue k;
ob& o = k;//左值引用
ob && o1= k;//左值引用
ou& o2 = k;//左值引用
ou&& o3 = (Lvalue&&)k;//右值引用
std::forward<Lvalue&&>(k);
return 0;
}