
همیشه این مفهوم Argument Passing در زبان های مختلف برام گیج کننده بود و جالبه که از زبانی به زبانی دیگه تفاوت داره. اینجا اومدیم برای بیشتر زبان های رایج بررسیش کردیم و در نهایت توی یه جدول خلاصشو نوشتیم. یک بار برای همیشه!
از قدیما یادمونه که در زبان هایی مثل C و Java دوتا مفهوم داشتیم وقتی میخواستیم یه چیزی رو به یه تابع پاس بدیم.
- Pass by value
وقتی یه آبجکتی رو به یه تابع پاس میدیم یه کپی از اون آبجکت در scope تابع ساخته میشه و تغییراتی که در اون شی داخل تابع اعمال میشه هیچ تاثیری توی آبجکت اصلی نمیذاره.
- Pass by reference
وقتی یه آبجکتی رو به یه تابع پاس میدیم پوینتر مربوط به اون آبجکت به تابع داده میشه و هر تغییری که داخل تابع روی آبجکت صورت بگیره مستقیما روی آبجکت اصلی اثر میذاره.
توی C فقط pass by value داریم و برای شبیه سازی pass by reference از مفهوم Pointer استفاده می کنیم.
اگه primitive type پاس بدیم که کپی میشه میره.
اگه آبجکت(Struct) باشه هم یه کپی ازش میره توی تابع.
اگرم بخوایم pass by reference باشه که پوینتر پاس میدیم.
// pass by reference using pointer in C
void move(Point *p) {
p->x += 1;
}
Point a = {1, 2};
move(&a);
printf("%d", a.x); // 2
این اصطلاح pass by reference اصلا از زبان ++c نشات گرفته! دلیلش این بوده که C فقط pass by value رو ساپورت میکرده برای اینکه کارو راحت کنن و مجبور نشن پوینتر بسازن با گذاشتن یه & کارو در آوردن!
بنابراین این زبون جفت pass by value و pass by reference رو بدون نیاز به شبیه سازی داره.
void foo(int& x) { // real pass-by-reference
x = 10;
}
int a = 5;
foo(a);
cout << a; // 10
توی جاوا هم pass by value هستش و برای شبیه سازی pass by reference از مفهوم
pass by value of the reference
استفاده میشه!
اگه primitive type پاس بدی به تابع که کپی اش میره و تغییر توی تابع هیچ اثری توی مقدار اصلی نمیذاره.
اگه آبجکت پاس بدی آدرس حافظه اون آبجکت by value کپی میشه! ینی توی تابع یه متغیر دیگه ساخته میشه که اون اشاره میکنه به اون آدرس حافظه کپی شده! بنابراین اگه روی اون متغیر داخل تابع تغییر بدی انگار آبجکت اصلی رو تغییر دادی مثلا بیای مقدار یه فیلدشو عوض کنی. اما اگه آدرسشو عوض کنی مثلا بیای یه آبجکت جدید بسازی و به اون اشاره کنه فقط داخل تابع عوض میشه و تاثیر بیرونی نداره.
با یه مثال قضیه رو بفهمیم!
public class CustomObject {
private int data;
public CustomObject(int data) {
this.data = data;
}
public void printInfo() {
System.out.println(data);
}
public void setData(int value) {
this.data = value;
}
}
یه کلاس ساده تعریف کردیم تا الان.
public class Main {
public static void main(String[] args) {
CustomObject a = new CustomObject(5);
CustomObject b = a;
doSomething(a);
a.printInfo(); // Prints: 10
b.printInfo(); // Prints: 10 (same object)
}
public static void doSomething(CustomObject temp) {
temp.setData(10);
}
}
اینجا چه اتفاقی افتاد؟ متغیر a شد یه آبجکت جدید. b به آدرس حافظه a اشاره میکنه و فانکشن doSomething فراخوانی شد که در نتیجه یه کپی از آدرس حافظه متغیر a به فانکشن پاس داده شد. داخل فانکشن متغیر temp هم به کپی آدرس حافظه a اشاره میکنه. هر تغییری که توی فیلدهای temp بدیم روی آبجکت اصلی اعمال میشه.

خب حالا میایم داخل تابع یه آبجکت جدید میسازیم ببینیم اعمال میشه یا نه.
public class Main {
public static void main(String[] args) {
CustomObject a = new CustomObject(5);
CustomObject b = a;
doSomething(a); // Assigns new object to temp
a.printInfo(); // Prints: 5
b.printInfo(); // Prints: 5 (original object unchanged)
}
public static void doSomething(CustomObject temp) {
temp = new CustomObject(10); // New reference; original unchanged
}
}
هیچ تغییری توی آبجکت اصلی مشاهده نمیشه! چرا؟چون اینجا این شکلی میشه تصویر ما:

اینجا متغیر temp به یه آدرس جدید اشاره میکنه واسه همینه که آبجکت اصلی تغییر نمیکنه.
حالا یه حالت دیگه هم تست کنیم!
public class Main {
public static void main(String[] args) {
CustomObject a = new CustomObject(5);
CustomObject b = a;
b = doSomething(a); // b now points to new object
a.printInfo(); // Prints: 5
b.printInfo(); // Prints: 10 (different references)
}
public static CustomObject doSomething(CustomObject temp) {
temp = new CustomObject(10);
return temp; // Returns new reference
}
}
اینجا تابع رو تغییر دادیم تا آبجکت جدید رو برگردونه.

خب الان توی تابع یه آبجکت جدید ساخته شد و temp بهش اشاره میکنه. بعد از اتمام کار تابع یه assignment داریم که b به اون آبجکت جدید اشاره میکنه و a تغییری نمیکنه.
نکته:
پس اگه توی جاوا خواستیم مثلا یه تابع swap درست کنیم برای int مثلا (primitive type ها در کل) چون pass by reference نداره تغییری اعمال نمیشه ینی این کار نمیکنه:
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
int x = 5, y = 10;
swap(x, y);
System.out.println(x + " " + y); // 5 10
برای اینکار یا باید اون int بره توی یه آبجکت بعد اون آبجکت رو پاس بدیم و تغییرش بدیم یا بریزیمش توی یه آرایه و اون آرایه رو تغییر بدیم. اینجا باز هم pass by value of the reference خواهد بود چون کپی آدرس حافظه اون آبجکت یا آرایه پاس داده میشه به تابع.
یکی از این دوتا راه جوابه:
Using Wrapper class
class IntWrapper {
int value;
IntWrapper(int value) { this.value = value; }
}
void swap(IntWrapper a, IntWrapper b) {
int temp = a.value;
a.value = b.value;
b.value = temp;
}
IntWrapper x = new IntWrapper(5);
IntWrapper y = new IntWrapper(10);
swap(x, y);
System.out.println(x.value + " " + y.value); // 10 5
Using array
public class SwapExample {
static void swap(int[] arr) {
int temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;
}
public static void main(String[] args) {
int[] nums = {5, 10};
swap(nums);
System.out.println(nums[0] + " " + nums[1]); // 10 5
}
}
پایتون نه pass by value هست نه pass by reference بلکه خودش یه چیزی داره با این عنوان:
pass by object reference / pass by assignment
اینجا اگر متغیر immutable باشه مقدارش تغییری نمیکنه مثلا اگه int, str باشه اما اگه mutable باشه مثل list, dict, set مقدارش تغییر میکنه!
def modify(a, b):
a += 1
b.append(99)
x = 10
y = [1, 2]
modify(x, y)
print(x, y)
# Output:
10 [1, 2, 99]
گولنگ مثل C میمونه ینی pass by value هستش و میتونه با پوینتر pass by reference رو هندل کنه به علاوه اینکه یه سری چیزا مثل slice, map, channel هم مثل پوینتر عمل میکنن (reference-like هستن) و تغییر در تابع شی اصلی رو تغییر میده.
pass by reference using pointer
func modify(p *int) {
*p = 99
}
func main() {
a := 10
modify(&a)
fmt.Println(a) // 99
}
pass by reference using reference-like objects (slice)
func modifySlice(s []int) {
s[0] = 99
}
func main() {
nums := []int{1,2,3}
modifySlice(nums)
fmt.Println(nums[0]) // 99
}
دقیقا مثل Java میمونه ینی برای primitive type ها pass by value هستش ولی برای آبجکت ها
pass by value of the reference
عمل میکنه ینی تغییرات روی آبجکت داخل تابع اعمال میشه اما reassign کردنش اعمال نمیشه!
البته این مورد هنوز برام کمی ابهام داره اگه کسی از اساتید تعریف درست تری داره خوشحال میشم اصلاحش کنم.
