나만의 작은 도서관
[C++] 레퍼런스(Reference) 본문
레퍼런스란(Reference)?
- 변수 선언 시 타입 뒤에 &를 붙여주는 것으로, 어느 변수의 또 다른 이름, 즉, 이명(alias)을 컴파일러에게 알려주는 것이다.
레퍼런스의 특징
레퍼런스는 선언만 할 수 없다.
- 레퍼런스는 선언 시 반드시 정의까지 해야 한다. 선언만 하는 경우 오류가 발생한다.
int a = 10;
int& another_a1; // 정의가 되어있지 않으므로, 오류
int& another_a2 = a; // 이처럼 반드시 정의까지 해야한다.
레퍼런스는 재정의를 할 수 없다
- 레퍼런스가 정의되었다면, 해당 레퍼런스가 소멸될 때까지 다른 변수의 레퍼런스가 될 수 없다.(재정의 불가)
int a = 10;
int &another_a = a; // another_a은 a의 레퍼런스
int b = 3;
another_a = b; // 레퍼런스가 아닌, 값 복사가 이루어짐(a = b; 와 동일)
&another_a = b; // &another_a = a의 주소값. 따라서, 주소값이 왼쪽에 오는 문장이므로 오류
레퍼런스는 대게 별개의 메모리를 가지지 않는다.
- 레퍼런스는 해당 변수의 이명이기 때문에 별개의 메모리 공간을 가지지 않는다. (아닌 경우도 있긴 하다) 따라서 레퍼런스로 함수에 넘겨줬다면 해당 레퍼런스 변수에 대해 메모리를 추가 할당하지 않는다.
void func(int& a/*변수 a에 대한 추가 메모리 할당 X*/){
return;
}
포인터(T*) vs 레퍼런스(T&)
- 성능 : 포인터와 레퍼런스는 성능이 완벽하게 똑같다.
- 편의성 : 레퍼런스가 더 좋다.
- 정의 방식 : 포인터는 선언만 해도 되지만, 참조는 반드시 선언 시 정의를 해야 한다.
- 값이 없는 경우도 고려한다면 포인터, readonly만 한다면 const ref&
자주 보이는 패턴
const ref&
- vector와 같은 자료구조에서 추가적인 메모리 할당 없이 데이터를 readonly로 읽고 싶을 때 자주 사용된다.
vector<int> v1 = {1, 2, 3, 4, 5};
for(const int& elem : v1){
cout << elem << ' '; // 1 2 3 4 5
}
cout << '\n';
OUT 키워드 사용
- 함수에 넘겨준 변수 값이 수정될 수 있음을 명시하기 위해 매개변수에 OUT이라고 적어주는 패턴이 있다.
#include <iostream>
#define OUT
using namespace std;
void Add(int a, OUT int& num) { num += a;}
int main(void){
int num = 5;
Add(10, OUT num); // 15
return 0;
}
레퍼런스의 금지된 조약
- 레퍼런스의 레퍼런스
- 레퍼런스를 저장하는 배열
- 레퍼런스를 가리키는 포인터
레퍼런스 배열 예시
// 레퍼런스를 저장하는 배열
int a = 10;
int b = 20;
int& arr[2] = {a, b}; // 불법!
// 배열에 대한 레퍼런스는 가능.
int arr[3] = {1, 2, 3};
int(&ref)[3] = arr; // 배열의 크기 명시 필요
ref[0] = 2;
ref[1] = 3;
ref[2] = 1;
std::cout << arr[0] << arr[1] << arr[2] << std::endl;
+) 참고로 레퍼런스의 레퍼런스를 의도하고 아래 코드처럼 적었다면, 그저 레퍼런스가 2개로 늘어나기만 한다.
int x = 10;
int& y = x; // y와 x는 정확히 일치하는 변수
int& z = y; // z와 y는 정확히 일치하는 변수
// => x = y = z
레퍼런스와 리턴값
레퍼런스를 리턴할 경우
- 함수를 벗어났을 때 소멸되는 변수(지역변수, 레퍼런스가 아닌 매개변수 등)는 레퍼런스로 리턴할 수 없다.
- 레퍼런스로 받은 매개변수는 가능하다.
int& func1(int& param1) { return param1; }
int& func2(int param1){ return param1; }
int& func3(){ int x = 20; return x; }
int main(void){
int a = 10;
cout << func1(a) << '\n'; // 가능
cout << func2(a) << '\n'; // warning: reference to local variable 'param1' returned
cout << func3() << '\n'; // warning: reference to local variable 'x' returned
return 0;
}
레퍼런스가 아닌 리턴값을 레퍼런스로 받는 경우
- 함수를 벗어났을 때 소멸되는 리턴값은 레퍼런스로 받을 수 없다.
- 하지만 const 레퍼런스는 예외적으로 저장이 가능하다. 이 경우 리턴값의 생명 주기가 연장된다.
#include <iostream>
using namespace std;
int func(){
int a = 5;
cout << &a << '\n';
cout << "----- func end -------" << '\n';
return a;
}
int main(void){
int x = 2;
// int& another_a = func(); // 비const 참조에 대한 초기 값은 lvalue여야 합니다
const int& another_a = func(); // 가능!
int y = 8;
cout << &x << '\n';
cout << &another_a << '\n';
cout << &y << '\n';
return 0;
}
실행 결과
왜인지는 모르겠으나 리턴값을 const 레퍼런스로 받으면, 레퍼런스임에도 불구하고 주소가 바뀐다.
0x61fdcc
----- func end -------
0x61fe10
0x61fe14
0x61fe0c
참고 자료
'C++ > 문법 및 메소드(STL)' 카테고리의 다른 글
[C++][STL] 컨테이너(Container) (0) | 2025.03.14 |
---|---|
[C++][STL] 반복자(Iterator) (0) | 2025.03.13 |
[C++] 포인터(Pointer) (0) | 2025.03.13 |
[C++][Keyword] 열거형(enum, enum class) (0) | 2025.02.19 |
[C++][Class] struct와 class의 차이 (0) | 2025.02.19 |