前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Dart 点将台 | 你真的明白参数传递吗?

Dart 点将台 | 你真的明白参数传递吗?

作者头像
张风捷特烈
发布2024-05-25 08:36:45
590
发布2024-05-25 08:36:45
举报

参数传递,是编程开发中最最最常见的一种行为。我们将一个 对象 传入到函数中作为输入,参与函数逻辑运算,得到输出值。可能很多人被值传递、引用传递、指针传递这些弯弯绕绕的跟困住了。其实面向对象的高级语言中,没有所谓的指针,对象本身 是值,也是一块 内存区域的地址引用

在高级语言中,参数传递的是对象,也只能是对象,别无其他。

level1: 为什么数字没改变

如下所示,在 chang 方法前后,x 的值 未发生变化

代码语言:javascript
复制
void main() {
  int x = 0;
  print("::before:: $x"); // 0
  chang(x);
  print("::after:: $x"); // 0
}

void chang(int a){
  a = 3;
}

但如果在 moveX 中传入 Point 对象,修改其中的 x 属性,可以发现传入的 p0 会发生变化

代码语言:javascript
复制
void main() {
  Point p0 = Point();
  print("::before:: $p0"); // Point{x: 0, y: 0}
  moveX(p0);
  print("::after:: $p0"); // Point{x: 1, y: 0}
}

void moveX(Point p){
  p.x =1;
}

class Point {
  int x = 0;
  int y = 0;

  @override
  String toString() {
    return 'Point{x: $x, y: $y}';
  }
}

对于这个现象,可能有人会解释为:int 是基本数据类型,所以会拷贝一份;Point 是自定义的类型,传入的是引用。其实从内存地址的角度来看:

左图: p0 对象在 0x0001 地址,存储着两个值: x,y 中图: moveX 函数中,入参 p 对指向 p0 的内存地址 右图: moveX 执行 p.x =1 ,就是访问内存地址,将其中的 x 改为 1.

期间 p0 始终指向 0x0001 ,所以该地址中的值的变化,会影响 p0 对象的值。而 p 对象作为函数内的临时变量,在函数出栈时被释放:

image.png
image.png

level2: 请关注内存地址

现在来使个坏,在 moveX 中,先将 p 赋值为 Point() ,然后再改变 p.x 的值。你觉得 p0 在 moveX 后会不会改变呢?

代码语言:javascript
复制
void main() {
  Point p0 = Point();
  print("::before:: $p0");
  moveX(p0);
  print("::after:: $p0");
}

void moveX(Point p){
  p = Point();
  p.x =1;
}

答案是 p0 不会变,其实从内存的角度来看,是非常好理解的。

左图: p0 对象在 0x0001 地址,存储着两个值: x,y 中图: moveX 函数中,入参 p 对指向 p0 的内存地址 右图: p = Point(); 这个动作,会让 p 对象的内存地址指向新的对象。

image.png
image.png

所以接下来对 p 对象的修改,就不管 0x0001 的事了。就像你在 0x0002 家搞装修,0x0001 家肯定不会发生变化。由于 p 是局部变量,在 moveX 方法出栈时,将被销毁。这就是 p0 为什么没有变的原因:

image.png
image.png

现在我们再从内存的角度来看待,为什么上面的 change 方法没有改变 x 。对于 Dart 而言一切皆为对象,占据内存空间,int 类型对象也一样。 当 a=3 时,就像 p = Point(); 一样,指向了另一个内存空间

image.png
image.png

大家可以思考一下,将 moveX 改为如下形式,会得到什么结果,为什么?

代码语言:javascript
复制
void moveX(Point p){
  p.x =1;
  p = Point();
  p.x =7;
}

level3: 赋值为空能成功吗?

如下所示,moveX 中将 p 赋值为 null,后续的输出打印是空吗?

代码语言:javascript
复制
void main() {
  Point p0 = Point();
  print("::before:: $p0");
  moveX(p0);
  print("::after:: $p0");

}

void moveX(Point? p){
  p = null;
}

如果明白了内存的分析方式,很容易理解:局部变量 p 指向 null 并不会影响到 p0 家里的数据。

image.png
image.png

level4: 回调函数

现在再变态一点,如果 moveX 中有一个回调,可以将函数内的局部变量回调出去,此时在回调在 p0 赋值为回调值 p ,在内存中发生了什么呢?

代码语言:javascript
复制
void main() {
  Point p0 = Point();
  print("::before:: $p0");
  moveX(p0, (p) => p0 = p);
  print("::after:: $p0");
}

void moveX(Point a, Function(Point p) callback) {
  a = Point();
  a.x = 4;
  callback(a);
}

moveX 中的 a 对象会作为 callback 函数的参数,也就是说 a 和 回调处理中的 p 指向同一个内存地址,当 p0 = p ,就相当于将 p0 的搬到了 a 的家里,p0 原先的家就没有任何对象指向他,也就是没有引用,将会被 gc 回收。


小结:

高级语言的对象并没有能力直接访问指针来修改内存地址中的数据。对象表面是一个值,背后指向一块内存地址。就像大古既是光也是人类,对象既是值,也是地址引用。参数传递过程中:

只是通过 函数局部变量 ,记录入参对象。 局部变量修改入参对象指向的内存地址数据,相当于你在我家装修,我家的表现肯定会变。 局部变量的内存地址的指向改变,相当于你到别家装修,关我屁事。

高级语言中函数入参的传递,是 对象传递,对象的正反两面兼具 地址 的特征。所以分析参数传递,最重要的是把握对象地址的指向,对象指向地址的数据就是该对象的

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • level1: 为什么数字没改变
  • level2: 请关注内存地址
  • level3: 赋值为空能成功吗?
  • level4: 回调函数
  • 小结:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档