前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从4行代码看引用

从4行代码看引用

作者头像
程序员小王
发布2021-04-28 14:31:30
5240
发布2021-04-28 14:31:30
举报
文章被收录于专栏:架构说

http://mpvideo.qpic.cn/0bf2jeabmaaa5mamohecafqfasodczeqafqa.f10002.mp4?dis_k=d8f7c67a37d41e17ebd38a4bad34f671&dis_t=1619591396&spec_id=MzA3OTY3OTE1MQ%3D%3D1619591396&vid=wxv_1838679048772730883&format_id=10002

一直有一个困惑,引用就是对象一个别名,别人问引用和指针区别?隐藏引用就不是指针

并且很多例子,参数传递和构造函数,看很多八股文, 到底一样不一样,课本上没有说,很模糊,不分配怎么操作。

直觉,感觉上判断,肯定有,如果没有。怎么被使用呢

这样认知陷入了矛盾:感觉上判断,肯定有,但是课本上没说,感觉痛苦难受!!!

为此展开了调查。

大胆猜测

------->>指针-->>-----引用------->

从c++发展历史来看,先出现指针( go也有指针类,没有引用类),后又引用,

  • 一个高级概念不会凭空出现,现在技术是不支持的,肯定是指针基础上发展起来的。

因此引用这个概念技术对指针高级封装。

  • 基本操作:对指针操作分为 ptr(对象本身), *ptr(指向对象)
  • 基本操作:指针++

我推测对引用操作,就是对对象的操作,一定是做一封封装,就像写函数一样。

  • 也就是说操作引用,就是隐藏操作*ptr。

这样合情合理,顿时感觉完美来了!

  • 会有那么神奇吗?操作引用,最后变成操作引用对象,神奇魔法 不存在
  • ra++ === (*pa)++

小心求证1- 看汇编

  • 代码:https://godbolt.org/z/xrcobvar3

从汇编角度看 指针和引用变量初始化产生汇编代码是一样的【都是三行】

  • 代码:
代码语言:javascript
复制
int a = 3;
int &ra = a;

int b = 4;
int *pa = &b;
  • 汇编:
代码语言:javascript
复制
int a = 3;
int &ra = a;
012E33F8  mov         dword ptr [a],3
012E33FF  lea         eax,[a]
012E3402  mov         dword ptr [ra],eax

int b = 4;
int *pa = &b;
012E3405  mov         dword ptr [b],4
012E340C  lea         eax,[b]
012E340F  mov         dword ptr [pa],eax

  • 解释
代码语言:javascript
复制
而 b 和 pa 也同样是这样步骤。
int a = 3;
012E33F8  mov          dword ptr [a],3 //把 3 放入地址为 [a] 的内存

int &ra = a; 

012E33FF  lea         eax,[a]   //a 的地址放入 eax
012E3402  mov         dword ptr [ra],eax ,//最后把 eax 的值放入地址为 [ra] 的内存
                      //实际上,就是把 a 的地址放入了 ra 里。
int b = 4;             
012E3405  mov         dword ptr [b],4  // 把 4放入地址为 [b] 的内存
int *pa = &b;
012E340C  lea         eax,[b] //b 的地址放入 eax
012E340F  mov         dword ptr [pa],eax ,//最后把 eax 的值放入地址为 [pa] 的内存

从汇编的角度来看,引用是通过指针来实现的

  • 代码
代码语言:javascript
复制
ra++;
(*pa)++;
  • 汇编
代码语言:javascript
复制
(*pa)++;
013F4498  mov         eax,dword ptr [pa]
013F449B  mov         ecx,dword ptr [eax]
013F449D  add         ecx,1
013F44A0  mov         edx,dword ptr [pa]
013F44A3  mov         dword ptr [edx],ecx
代码语言:javascript
复制
ra++;
013F4475  mov         eax,dword ptr [ra]
013F4478  mov         ecx,dword ptr [eax]
013F447A  add         ecx,1
013F447D  mov         edx,dword ptr [ra]
013F4480  mov         dword ptr [edx],ecx
  • 解释:多了2个寄存器帮助完成计算 ecx,edx
代码语言:javascript
复制
ra++;
013F4475  mov         eax,dword ptr [ra] //获取rad地址 eax 
013F4478  mov         ecx,dword ptr [eax] //获取rad地址里的值:对象的地址 ecx
013F447A  add         ecx,1 //在寄存器完成计算 ecx
013F447D  mov         edx,dword ptr [ra] //获取rad地址里的值:对象的地址 edx 
013F4480  mov         dword ptr [edx],ecx //写回到 原来对象里面

小心求证2- sizeof 计算大小

代码语言:javascript
复制
#include <iostream>
#include <string.h>
using namespace std;
class Base
{
public:
 virtual  ~Base(){}
private:
   int data;
};
class Test
{
public:
 Base* m_ptr;
 Base &m_base;
  
public:
 Test( Base& base):m_base(base),m_ptr(&base)
 { 
   cout<< m_ptr<<endl;
   cout<< &m_base<<endl;
   //Test( Base &base):m_base(&base),m_ptr(&base)
   //invalid initialization of non-const reference of type ‘Base&’ from a temporary of type ‘Base*’
 }

};

void test_ref()
{
    Base base;
    Test a(base);
    cout << "sizeof(base)=" <<sizeof(base) <<endl; //16
    cout << "sizeof(a)=" <<sizeof(a) <<endl;//16 神奇 为是不是24呢?表现出来是指针呢
    cout << "sizeof(a._base)=" <<sizeof(a.m_base) <<endl; //16
}
int main()
{    
 test_ref();
  return 0;
}
  • gdb
代码语言:javascript
复制
int a= 10;
int*ptr = &a;
int& b= a;

(gdb) p &a
$11 = (int *) 0x7fffffffe07c
(gdb) p ptr
$12 = (int *) 0x7fffffffe07c
(gdb) p b
$13 = (int &) @0x7fffffffe07c: 10
  • 引用是通过指针来实现的,如何打印出来 这个"指针"存放地址呢

还有区别吗?指针不能指向右值,因为右值地址不在栈上。

  • const & listenAddr
代码语言:javascript
复制
SudokuServer(EventLoop* loop, const InetAddress& listenAddr)
    : server_(loop, listenAddr, "SudokuServer"),
      startTime_(Timestamp::now())
  {
    server_.setConnectionCallback(
        std::bind(&SudokuServer::onConnection, this, _1));
    server_.setMessageCallback(
        std::bind(&SudokuServer::onMessage, this, _1, _2, _3));
  }

扩展 :占用了位置--确不能直接访问/修改

  • const变量无法修改,但是如果获取cosnt变量地址就可以了

理解 C/C++ 中的左值和右值

http://thbecker.net/articles/rvalue_references/section_01.html
  1. Rvalue references solve at least two problems:
  • Implementing move semantics
  • Perfect forwarding

引用至少解决了两个问题:

  • 实现 move 语义
  • 完美转发
  1. 右值引用不一定是右值 判断依据是如果右值引用修饰变量有名字 就是左值

Is an Rvalue Reference an Rvalue?

代码语言:javascript
复制
hings that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue.
 
In the example above, the thing that is declared as an rvalue reference has a name, and therefore, it is an lvalue:


//参数有名字,调用X(X const & rhs)
void foo(X&& x)
{
  X anotherX = x; // calls X(X const & rhs)
}
Here is an example of something that is declared as an rvalue reference and does not have a name, and is therefore an rvalue:

//返回值没有名字,调用X(X&& rhs)
X&& goo();
X x = goo(); // calls X(X&& rhs) because the thing on
             // the right hand side has no name
             

X& X::operator=(X&& rhs)
{

  // Perform a cleanup that takes care of at least those parts of the
  // destructor that have side effects. Be sure to leave the object
  // in a destructible and assignable state.

  // Move semantics: exchange content between this and rhs
  
  return *this;
}

             
左值和右值的主要区别是,左值可以被修改,而右值不能。不过,C++11 改变了这一区别。在一些特殊的情况下,我们可以使用右值的引用,并对右值进行修改。
Rvalue 并不意味着对象是不可变的

塔山

  1. https://www.zhihu.com/question/40720890
  • mov eax,[ebx+8]则是把内存地址为ebx+8处的数据赋给eax。
  • lea eax,[ebx+8]就是将ebx+8这个值直接赋给eax。load effective address
  • lea取地址的 mov仅仅赋值
  1. http://thbecker.net/articles/rvalue_references/section_01.html
  • values and rvalues:
  • rvalue is an expression that can only appear on the right hand side of an assignment.
  • Rvalue 并不意味着对象是不可变的
  • Move Semantics?深度拷贝 vs swap vs move
  • This is called move semantics
  • Rvalue References void foo(X& x); // lvalue reference overload void foo(X&& x); // rvalue reference overload
  • a = std::move(b);
  • X& X::operator=(X&& rhs) any part of an object's destruction that has side effects should be performed explicitly in the rvalue reference overload of the copy assignment operator:
  • move 不是swap ,move 负责原来数据的清理工作
  1. 栈是操作系统自动管理的,这句话没错,需要进一步解释
  • 函数产生汇编
  • 移动方向
  • https://nettee.github.io/posts/2018/Understanding-lvalues-and-rvalues-in-C-and-C/
  1. 看10遍
  • 深刻理解引用、const引用、右值引用的本质 https://blog.csdn.net/z914022466/article/details/76851363
    1. 例子必须运行一次,看是看不懂,对指针操作太复杂了。addr=(int *)((int)pj+dis);谁看的懂 dis=(int)pj-(int)pi;//地址差
    2. int &&a=1;和const int &a=1;
    3. 是完全一样的操作,先在数据区开辟一个值为1的无名整型量,再将引用a与这个整型量进行绑定
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Offer多多 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 大胆猜测
  • 小心求证1- 看汇编
  • 小心求证2- sizeof 计算大小
  • 还有区别吗?指针不能指向右值,因为右值地址不在栈上。
  • 扩展 :占用了位置--确不能直接访问/修改
    • 理解 C/C++ 中的左值和右值
      • http://thbecker.net/articles/rvalue_references/section_01.html
  • 塔山
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档