int iNum = 10;
// int 형 포인터 변수
int* pPtr = &iNum;
// iNum의 레퍼런스인 rRef
int& rRef = iNum;
포인터 = 메모리의 주소를 가지고 있는 변수
참조자 = 자신이 참조하고 있는 변수를 대신할 수 있는 또 하나의 이름
무슨 차이인가 하면
포인터 변수는 iNum을 가리키는 주소를 담고있는 그릇이고,
레퍼런스는 별도의 그릇(공간)이 생성되지않고 iNum 이라는 이름 대신 다른 이름(rRef)을 사용할 수 있다는 뜻 이다.
따라서 포인터는 별도의 메모리 공간을 소모 하고 (64비트 기준으로 8byte)
레퍼런스는 같은 메모리 공간을 참조 하므로 메모리 공간을 소모하지 않는다.
int a = 5;
int& b = a;
int* c = &a;
cout << a << endl; // 5
cout << b << endl; // 5
cout << *c << endl; // 5
cout << &a << endl; // 00000068A30FF5D4
cout << &b << endl; // 00000068A30FF5D4
cout << c << endl; // 00000068A30FF5D4
또 다른 차이점으로 포인터는 nullptr 초기화 할 수 있지만, 레퍼런스는 선언과 동시에 초기화가 이루어져야 한다.
int iNum = 10;
// int 형 포인터 변수
int* pPtr = nullptr;
// iNum의 레퍼런스인 rRef
int& rRef = nullptr; // error
레퍼런스가 주소가 없는 0x0 을 가리키면 안되므로
#include <iostream>
using namespace std;
int& AllRefTest(int &ref) {
++ref;
return ref;
}
int ParamRefTest(int &ref) {
++ref;
return ref;
}
int NonRefTest(int ref) {
++ref;
return ref;
}
int main() {
int AllRefNum = 10;
int ParamRefNum = 10;
int NonRefNum = 10;
int &num1 = AllRefTest(AllRefNum);
int num2 = ParamRefTest(ParamRefNum);
int num3 = NonRefTest(NonRefNum);
++num1;
++num2;
++num3;
cout << "AllRefNum: " << AllRefNum << endl;
cout << "ParamRefNum: " << ParamRefNum << endl;
cout << "NonRefNum: " << NonRefNum << endl;
cout << "num1: " << num1 << endl;
cout << "num2: " << num2 << endl;
cout << "num3: " << num3 << endl;
}
처음에 각 변수에 10이라는 값이 할당 된다.
그리고 각 함수에서 받은 Ref 값은 다음과 같이 가리킨다.
또한 각 함수에서 ++Ref를 해주면 다음과 같아진다.
그리고 각 함수에서 값을 반환하면 다음과 같다. (ref 는 함수 파라미터이므로 함수 종료 후 사라짐)
마지막으로 각 num들을 ++ 해주면 다음과 같아진다.
AllRefNum: 12
ParamRefNum: 11
NonRefNum: 10
num1: 12
num2: 12
num3: 12
cout << "AllRefNum: " << &AllRefNum << endl;
int &num1 = AllRefTest(AllRefNum);
cout << "num1: " << &num1 << endl;
cout << "ParamRefNum: " << &ParamRefNum << endl;
int num2 = ParamRefTest(ParamRefNum);
cout << "num2: " << &num2 << endl;
cout << "NonRefNum: " << &NonRefNum << endl;
int num3 = NonRefTest(NonRefNum);
cout << "num3: " << &num3 << endl;
// ---Output---
// AllRefNum: 0x61fe14
// AllRefTest: 0x61fe14
// num1: 0x61fe14
// ParamRefNum: 0x61fe10
// ParamRefTest: 0x61fe10
// num2: 0x61fe08
// NonRefNum: 0x61fe0c
// NonRefTest: 0x61fde0
// num3: 0x61fe04
함수 내부에서 사용되는 지역변수는 함수가 끝나면 스택 메모리 영역에서 사라진다.
그럼 만약에 함수내부에서 사용되는 지역변수를 참조 리턴값으로 반환하면 어떻게 될 까?
int& RefTest(int ref) {
int num = ref;
return num;
}
위 함수가 반환될 때 소멸할 num 에 대한 참조를 반환한다.
이것은 호출자가 쓰레기에 대한 참조를 받는다는 것을 의미한다.
다행히도, 컴파일러는 이것을 시도하면 에러를 띄어준다.
참조로 반환을 사용해야 하는 경우:
- 참조 매개 변수를 반환할 때
- 함수에 전달된 배열의 요소를 반환할 때
- 함수의 끝에서 소멸하지 않는 구조체나 클래스를 반환할 때
참조로 반환을 사용해야 하지 않아야 하는 경우:
- 함수 내에서 선언된 (지역)변수를 반환할 때 (값으로 반환 사용)
- 기본 배열이나 포인터 값을 반환할 때 (주소로 반환 사용)
Use references when you can, and pointers when you have to
사용할 수 있다면 참조자를, 어쩔 수 없다면 포인터를 써라