나만의 작은 도서관

[C++] 레퍼런스(Reference) 본문

C++/문법 및 메소드(STL)

[C++] 레퍼런스(Reference)

pledge24 2025. 3. 13. 04:25

레퍼런스란(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;
}

 


레퍼런스의 금지된 조약

  1. 레퍼런스의 레퍼런스
  2. 레퍼런스를 저장하는 배열
  3. 레퍼런스를 가리키는 포인터

레퍼런스 배열 예시

// 레퍼런스를 저장하는 배열
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

 


참고 자료

https://modoocode.com/141