提起左右值引用我就头疼
左值:
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;
}