레퍼런스 (Reference)
레퍼런스(Reference)는 변수의 또 다른 이름(별명, Alias)이다.
레퍼런스는 새로운 메모리를 생성하는 것이 아니라
기존 변수의 메모리를 그대로 사용한다.
즉, 같은 메모리를 다른 이름으로 접근하는 방식이다.
포인터와 다르게 주소를 저장하는 변수가 아니며
반드시 이미 존재하는 변수와 연결되어야 한다.
그렇기 때문에 선언과 동시에 초기화가 필요하며
참조할 변수가 없으면 문법 오류가 발생한다.
레퍼런스 선언
int a = 10;
int& r = a;
여기서
a: 원본 변수r: a의 레퍼런스(별명)
잘못된 예시
int& r;
컴파일 오류 발생
레퍼런스는 반드시 참조할 대상이 필요하기 때문이다.
메모리 관점에서 보기
int a = 10;
int& r = a;
메모리 구조
주소 변수 값
1000 a 10
실제로는 메모리에 a만 존재한다.
주소 변수 값
1000 a 10
r
r은 새로운 메모리를 갖는 변수가 아니라
a의 또 다른 이름
이다.
값 변경하기
int a = 10;
int& r = a;
r = 20;
결과
cout << a;
출력
20
반대로
a = 30;
을 실행하면
cout << r;
출력
30
이 된다.
왜 같이 변경될까?
a와 r은 서로 다른 변수가 아니기 때문이다.
a
와
r
은 같은 메모리를 사용한다.
즉,
r = 20;
은 사실상
a = 20;
과 같다.
레퍼런스는 재참조가 불가능하다
레퍼런스는 선언 시 참조할 대상이 결정된다.
한 번 연결되면 다른 변수로 변경할 수 없다.
int a = 10;
int b = 20;
int& r = a;
현재
r → a
상태이다.
다음 코드를 보자.
r = b;
많은 사람들이
r → b
로 바뀐다고 생각하지만 그렇지 않다.
실제로는
a = b;
가 실행된다.
즉,
a = 20;
과 같은 의미이다.
실행 결과
cout << a;
출력
20
cout << b;
출력
20
하지만 여전히
r → a
이다.
참조 대상은 변경되지 않는다.
포인터와 비교
포인터
int a = 10;
int b = 20;
int* p = &a;
p = &b;
가능
p → a
에서
p → b
로 변경 가능
레퍼런스
int& r = a;
가능
r → a
하지만
r = b;
를 해도
r → b
가 되는 것이 아니라
a = b;
가 수행된다.
포인터와 레퍼런스 차이
| 구분 | 포인터 | 레퍼런스 |
|---|---|---|
| 선언 | int* p |
int& r |
| 주소 저장 | O | X |
| nullptr 가능 | O | X |
| 선언 후 대상 변경 | O | X |
| 역참조 필요 | O (*p) |
X |
| 선언과 동시에 초기화 | 선택 | 필수 |
레퍼런스를 사용하는 이유
함수 호출 시 원본 데이터를 수정하기 위해 자주 사용한다.
void SetValue(int& value)
{
value = 100;
}
int a = 10;
SetValue(a);
결과
a == 100
또한 큰 객체를 복사하지 않기 위해 사용한다.
void Print(const std::string& str)
{
std::cout << str;
}
복사 비용 없이 데이터를 전달할 수 있다.
핵심 정리
레퍼런스는 변수의 또 다른 이름(Alias)이다.
int a = 10;
int& r = a;
새로운 메모리를 생성하지 않는다.
a와 r은 같은 메모리를 사용한다.
레퍼런스는 반드시 선언과 동시에 초기화해야 한다.
int& r = a;
레퍼런스는 한 번 연결되면 다른 변수를 참조하도록 변경할 수 없다.
r = b;
는
a = b;
와 같은 의미이다.
레퍼런스는 원본 데이터를 수정하거나
복사 비용 없이 데이터를 전달하기 위해 사용된다.