函数 --参数传递
C++ 传值 传址 传引用 Pass by Reference
java 传值--
值传递(Pass by Value)
基本数据类型--传递的是这些值的拷贝 引用传递的核心在于“直接操作”——传递的是参数的地址,而不是副本
Python --传引用+数据可变性
不可变--传值
可变--传引用
方式:
修改函数传入的参数可以通过两种主要方式:直接在函数内部重赋值给参数、通过对象引用属性的修改
通过返回值的方式
通过在函数体内进行修改的方式
涉及到函数的参数传递的方式了
参数传递的本质
01.传递的方式
内存操作:是否直接操作原始数据。
性能开销:是否复制数据副本。
代码安全性:是否允许意外修改数据
02.函数的返回值+ 函数内修改变量
返回值--返回引用--返回地址
返回引用/指针:避免拷贝大型对象(需确保对象生命周期有效)。
变量修改--内部修改 内部不修改
03.数据在内存的分布方式
内存区域
生命周期:对于传值传参来说, 形参是实参的一个拷贝,也就是说实际上它们是两个变量。在函数执行结束后,形式参数以及函数栈区的变量都会被销毁
对于传引用传参来说 无论是在主函数还是传参时改变它,原来的值都会被改变,因为它的底层也是用指针来实现的
在使用引用传参作为返回值时要注意悬空引用的问题
对应传址传参来说,形参为内存地址的一个拷贝,但是通过解引用可以拿到该内存地址的内容,从而可以直接改变实参所指地址的值。
C
传值: 副本
传值调用就是直接将实参的值传递给形参。这样形参和实参的值是一样的,
但是函数的形参和实参分别占有不同的内存块,实参是一个独立的个体,形参也是一个独立的个体,只是形参的值与实参相同。
传址: 实际是传值的一种特殊方式,只是他传递的是地址,不是普通的赋值,那么传地址以后,实参和行参都指向同一个对象,因此对形参的修改会影响到实参
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量
C++
变量的存储位置直接影响其生命周期、作用域和访问效率。
C++ 程序运行时内存通常分为5个关键区域:栈区、堆区、全局/静态存储区、常量区以及代码区
传值
传引用
C++可以通过将引用传入函数,在函数内修改所引用的对象 C++函数参数被传递参数的一个别名
引用传递(Pass by Reference)
传递别名:引用是变量的别名,直接操作原始数据。
C++ 主要有指针和引用两种与内存关联的概念。
C++ 中的引用一旦初始化就不能再重新绑定到其他对象,必须在初始化时就确定引用的对象
引用是一种别名,它允许我们使用一个已经存在的变量名来访问相同的内存位置 引用一旦绑定就不能重新引用其他变量,c++引用也不能为null
无法重绑定:引用初始化后不能指向其他变量。
无空引用:必须指向有效对象,无法表示“无数据”
传指针:
传递地址:函数通过指针直接访问原始数据的内存地址。 显式操作:需使用 & 取地址符和 * 解引用操作符
直接访问原始数据的地址---java中时复制地址
其他限制:
使用 const 修饰符 保护数据:防止函数内部意外修改原始数据
指针 vs 引用的选择
优先引用:当数据必须存在且无需重绑定时。
使用指针:当参数可能为空或需要动态分配内存时
std::forward 是C++11引入的函数模板,它的作用是实现函数参数的完美转发,通俗的讲就是根据传入的参数,决定将参数以左值引用还是右值引用的方式进行转发
C++ 高阶函数、闭包等概念来接近函数式编程的理念
01.将函数对象化 函数对象(Function Object),也叫作 仿函数(Functor),是指任何可以像函数一样调用的对象。
具体来说,函数对象是重载了 operator() 的类的实例 函数对象是对象而不是普通函数指针
函数对象是一个类的实例,它可以包含成员变量,可以在调用时保持状态。这使得函数对象能够在不同的调用之间记住一些信息。
状态可以被保存: 函数对象与普通函数不同的是,它们可以拥有成员变量,因此可以在调用过程中保存和维护状态
函数对象可以作为参数传递。例如,std::sort 函数可以接受一个函数对象作为比较器
定义函数对象
使用函数对象
C++ STL 提供了许多算法(如 std::sort、std::for_each 等),它们可以接受函数对象作为参数来定制行为
02.函数指针 也可以传递函数作为参数,但函数对象比函数指针更灵活,因为它们不仅可以像函数一样调用,而且还可以维护状态、实现多个方法等
03.Lambda表达式 Lambda表达式的语法是 [捕获列表](参数列表) -> 返回类型 {函数体}
04.函数包装器 头文件:#include<functional> 高阶函数通常以模板和对象的形式存在,而不是简单的函数。高阶函数的概念主要通过std::function来实现
05. bind可以绑定的对象:①普通函数;②lambda表达式;③函数对象;④类的成员函数;⑤类的数据成员
Java案例
Java 通过垃圾回收器自动管理内存,程序员不需要手动释放内存。引用主要用于指向对象,当没有任何引用指向一个对象时,该对象会被垃圾回收器回收。
Java 中有强引用、软引用、弱引用和虚引用四种类型的引用。
不同类型的引用在垃圾回收时的处理方式不同。强引用是最常见的引用类型,只要强引用存在,对象就不会被回收。
软引用、弱引用和虚引用在内存紧张时可能会被回收。
Java引用是一个对象的句柄,它指向对象在堆中的内存地址。JAVA中的引用可以为空,也可以重新指向其他对象
对象的地址值,并不是对象本身
Java中的引用类型变量只在堆内存中存储对象,而引用本身则存储在栈内存中。Java的引用只能指向程序限定的能够访问的现存对象
把用于交换的参数作为对象的一个变量
01.基本类型
02.继承object的类型
Java 基本类型(int、double、long 这种)是直接将存储在栈上的,而引用类型(类)则是值存储在堆上,栈上只存储一个对对象的引用
函数在内存: 函数需要在内存上存储
参数、局部变量
返回地址、ebp 寄存器(基址指针寄存器,指向当前堆栈底部) 以及其它需要保存的寄存器
如果想通过引用类型修改外部传进来的值,一般是采用成员方法。
class Person {
String name;
Person(String name) {
this.name = name;
}
}
public class ReferencePassingExample {
public static void main(String[] args) {
Person person = new Person("Alice");
modifyObject(person); // 传递的是 person 引用的副本
System.out.println("名字是:" + person.name); // 输出:名字是:Boby
// 存储的是对象的内存地址(引用),传递时传递的是这个引用的副本。因此,虽然可以修改对象的内容,但无法改变对象引用本身
Person person2 = new Person("Alice");
changeReference(person2); // 传递的是引用的副本
System.out.println("名字是:" + person2.name); // 输出:名字是:Alice 原始引用person仍然指向Alice对象,因此不会发生任何变化
}
public static void modifyObject(Person p) {
p.name = "Boby"; // 修改了引用对象的内容
}
public static void changeReference(Person p) {
p = new Person("Chart"); // 修改的是 p 的引用 改变了局部变量p的引用,它指向了一个新的Person对象--改变引用的指向
System.out.println("名字是:" + p.name); // 输出:名字是:Chart
}
}
python
Python 有个特点就是 所有变量本身只是一个引用,真正的类型信息都是和对象存储在一起的。
方式:
刨根问底--回头问效
最好的方式是,在一个恰到好处的地方建立一个抽象层,并且认可这个抽象层提供的功能/接口,不去探究这一层下面是什么,怎么实现的。
参考
C++ 参数传递详解:值传递、指针传递、引用传递 http://blog.csdn.net/qq_22734027/article/details/145969095