C, C++ & Linux/C++

[C++] 참조자(reference)

서노리 2022. 6. 24. 22:24
반응형

참조자(reference)

C언어에서 어떠한 변수를 가리키는 방법 중 하나로 포인터의 사용이 있었다.

C++에서는 다른 변수나 상수를 가리키는 방법으로 참조자(레퍼런스, reference)라는 또 다른 방식을 제공한다.

#include <iostream>
using namespace std;

int main(){
    int a = 3;
    int &ref_a = a;

    ref_a = 5;
    cout << "a : " << a << endl;
    cout << "ref_a : " << ref_a << endl;
}

참조자를 선언하는 방법은 가리키고자 하는 타입 뒤에 &를 붙인다.

심지어 int*와 같은 포인터 타입의 참조자를 만드려면 int*&와 같이 만들어주면 된다.

 

이렇게 선언한 ref_a는 a의 참조자가 되어 컴파일러에게 ref_a는 a의 또다른 이름이라고 알려주는 것과 같다.

따라서 ref_a에 어떠한 작업을 수행하는 이는 a에 그 작업을 하는 것과 같게 된다.

 

포인터와의 차이점

  • 레퍼런스는 반드시 처음에 누구의 별명이 될 것인지 지정해야 한다.
int &ref_a; // 불가능
int *p; // 가능

 

  • 레퍼런스는 한 번 지정되면 절대로 다른 변수를 참조할 수 없다.
int a = 10;
int &ref_a = a; // ref_a는 a의 참조자가 됨

int b = 3;
ref_a = b; // 그냥 a에 b의 값을 대입하라는 의미

 

  • 레퍼런스는 메모리 상에 존재하지 않을 수 있다.

    선언된 포인터 변수는 메모리 상에서 8바이트의 크기를 차지하게 되지만 레퍼런스는 굳이 메모리를 차지할 필요가 없다. 왜냐하면 ref_a가 쓰이는 모든 자리는 모두 a로 대치하면 되기 때문이다.

    다만 모든 경우에서 메모리를 차지하지 않는 것은 아닌데  바로 함수 인자를 참조자로 전달하는 경우이다. 즉, call by reference를 하게 되면 함수의 stack 영역에 메모리가 할당되어 사용된다.

함수 인자로 레퍼런스 전달

#include <iostream>

void change_val(int &p) {
  p = 3;
}

int main() {
  int number = 5;

  std::cout << number << std::endl;
  change_val(number);
  std::cout << number << std::endl;
}

포인터가 인자일 때와는 다르게 chage_val(number)에서 number 앞에 &을 붙일 필요가 없다는 점이 특징이다.
이는 레퍼런스를 정의할 때 int &p = number와 같이 한 것과 같다고 볼 수 있다.

 

레퍼런스를 리턴하는 함수

#include <iostream>

int& function(){
    int a = 2;
    return a;
}

int main(){
    int b = function();
    b = 3;
}

위 코드를 실행하면 런타임 오류가 발생한다. 그 이유는 무엇일까?
function함수는 int& 타입을 리턴하는 함수이다. 하지만 문제는 function 함수에 정의된 a는 함수의 리턴과 함께 사라진다는 점이다.


즉, 레퍼런스는 있지만 그 레퍼런스가 참조하는 변수가 사라진 Dangling reference 현상이 나타나게 된다.

따라서 위처럼 레퍼런스를 리턴하는 함수에서는 지역 변수의 레퍼런스를 리턴하지 않도록 조심해야한다.

 

#include <iostream>

int& function(int& a) {
  a = 5;
  return a;
}


int main(){
    int b = 2;
    int c = function(b);
}

그렇다면 이처럼 지역 변수가 아닌 레퍼런스를 리턴하는 경우에는 어떻게 될까?

function이 리턴한 레퍼런스가 계속 b를 참조하게 되어 결국 c에 5를 대입하는 것과 같은 문장이 된다.

 

레퍼런스를 리턴하는 경우 레퍼런스가 참조하는 타입의 크기와 상관없이 주소값 복사가 이루어져 매우 효율적이다.

 

 

상수에 대한 참조자

상수를 참조하기 위해서는 참조자 선언 앞에 const를 붙혀주어야한다.

#include <iostream>

int main() {
  const int &ref = 4;
  std::cout << ref << std::endl;
}

또한 함수가 리턴한 값을 참조하는 경우에도 const가 사용된다.

 

#include <iostream>

int function() {
  int a = 5;
  return a;
}

int main() {
  int& c = function();
}

이 경우도 위에서 봤던 dangling reference가 발생하기 때문에 오류가 발생한다.

하지만 const int& c로 선언해주면 해당 레퍼런스가 사라지기 전까지 리턴값의 생명이 연장된다.


참고 - https://modoocode.com/141

반응형

'C, C++ & Linux > C++' 카테고리의 다른 글

[C++] STL - next_permutation  (0) 2022.08.01
[C++] STL - pair, tuple  (1) 2022.07.01
[C++] STL - list  (0) 2022.06.29
[C++] STL - deque  (0) 2022.06.29
[C++] STL - vector  (0) 2022.06.29