Java나 Python와 C++의 차이에는 여러 가지가 있겠지만 가장 나를 당황시켰던 것은 앰퍼샌드로 참조 변수를 사용하는 방식이었다.
Java와 Python에서는 call by value인 변수가 있고 call by reference인 변수가 있다. 객체 타입에 따라 다른데, 예를 들면 ArrayList(Java)/list(Python)가 mutable, String(Java)/str(Python)이 immutable이다. immutable한 객체를 파라미터로 전달하면 함수 밖에서 변경이 적용되지 않고, mutable한 객체를 파라미터로 전달하면 함수 밖에도 변경이 적용된다.
그런데 C++에서는 call by reference/value를 코드에서 정해줄 수 있다. &를 변수명 앞에 사용해서 레퍼런스로 전달하면 함수 내에서 변경된 내용이 함수 밖에도 적용되고, 값만 전달하면 함수 내의 변경 사항이 함수 밖에 적용되지 않는다.
#include <iostream>
void swapr(int & a, int & b); // a, b are aliases for ints
void swapp(int * p, int * q); // p, q are addresses of ints
void swapv(int a, int b); // a, b are new variables
int main()
{
using namespace std;
int wallet1 = 300;
int wallet2 = 350;
cout << "initial value: " << endl;
cout << "wallet1 = $" << wallet1;
cout << " wallet2 = $" << wallet2 << endl;
cout << "================" << endl;
swapr(wallet1, wallet2);
cout << "after swapr: " << endl;
cout << "wallet1 = $" << wallet1;
cout << " wallet2 = $" << wallet2 << endl;
cout << "================" << endl;
swapp(&wallet1, &wallet2);
cout << "after swapp: " << endl;
cout << "wallet1 = $" << wallet1;
cout << " wallet2 = $" << wallet2 << endl;
cout << "================" << endl;
swapv(wallet1, wallet2);
cout << "after swapv: " << endl;
cout << "wallet1 = $" << wallet1;
cout << " wallet2 = $" << wallet2 << endl;
return 0;
}
void swapr(int & a, int & b)
{
int temp;
temp = a;
a = b;
b = temp;
}
void swapp(int * p, int * q)
{
int temp;
temp = *p;
*p = *q;
*q = temp;
}
void swapv(int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
using namespace std;
cout << "in swapv: " << endl;
cout << "wallet1 = $" << a;
cout << " wallet2 = $" << b << endl;
cout << "===========" << endl;
}
위 코드에서
swapr 함수는 레퍼런스를 전달받고 전달받은 a와 b를 변경한다.
swapp 함수는 포인터를 전달받고 전달받은 p와 q를 변경한다.
swapv 함수는 값을 전달받고 전달받은 a와 b를 변경한다.
실행결과를 보면
initial value:
wallet1 = $300 wallet2 = $350
================
after swapr:
wallet1 = $350 wallet2 = $300
================
after swapp:
wallet1 = $300 wallet2 = $350
================
in swapv:
wallet1 = $350 wallet2 = $300
===========
after swapv:
wallet1 = $300 wallet2 = $350
swapr과 swapp 함수 실행 이후에는 함수 밖에서도 값 변경이 적용되었지만, swapv 함수의 경우 함수 내부에서만 값이 변경되었다가 함수 밖에서는 swapv 함수 실행 전의 값을 가지고 있다.
그렇다면 포인터와 레퍼런스는 뭐가 다른가? 왜 포인터가 있는데 레퍼런스를 또 만든 걸까?
포인터는 Null 값을 갖는 nullptr일 수도 있지만, 레퍼런스는 Null을 허용하지 않는다. 그리고 포인터는 값을 복사/주소를 저장하기 위해 메모리를 소모하는 반면, 레퍼런스는 같은 메모리 방식을 참조한다. 그래서 포인터보다 레퍼런스가 더 안전한 방식이라고 한다.
참고: https://stackoverflow.com/questions/7058339/when-should-i-use-pointers-instead-of-references-in-api-design
함수가 경우에 따라 null을 받아야 하는 상황처럼, 포인터 외의 선택지가 없을 때만 포인터를 써야 한다.