[C++] 참조자(레퍼런스)

muchan
muchan
∙∙조회 6

https://modoocode.com/141

위 사이트에서 공부한 내용 정리입니다

참조자란

C++에서 다른 변수나 상수를 가리키는 방법으로 또 다른 방식을 제공하는데, 이를 참조자라고 부른다

c
#include <iostream>

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

  another_a = 5;
  std::cout << "a : " << a << std::endl;
  std::cout << "another_a : " << another_a << std::endl;

  return 0;
}

해당 코드에서 another_a는 a의 참조자라고 한다

another_a는 a의 또 다른 이름이라고 컴파일러에게 알려주는 것이다

위처럼 int형 변수의 참조자를 만들고 싶을 땐 int&, double의 참조자를 만들고 싶을 땐 double&로 선언하면 된다
int*와 같은 포인터 타입의 참조자를 만드려면 *int&**로 쓰면 된다

포인터와 차이점

참조자는 포인터랑 비슷한 개념이다. 포인터 역시 다른 변수의 주소값을 보관하고 변수에 간접적으로 접근할 수 있기 때문이다. 하지만 레퍼런스와 차이점이 있다

1. 레퍼런스는 반드시 처음에 누구의 별명이 될 것인지 지정해야 한다

레퍼런스는 정의 시에 반드시 누구의 별명인지 명시해야한다

arduino
int& another_a; // 불가능
int* p // 가능

2. 레퍼런스가 한 번 별명이 되면 절대로 다른 이의 별명이 될 수 없다

ini
int a = 10;
int &another_a = a; // another_a 는 이제 a 의 참조자!

int b = 3;
another_a = b; // ?? 

중요) 여기서 another_a는 b의 참조자가 아니라 a에 3을 대입하라는 것이랑 똑같은 의미다

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

ini
// 포인터
int a = 10;
int* p = &a;

// 레퍼런스
int a = 10;
int &another_a = a;

포인터의 경우 p는 8바이트를 차지하지만 레퍼런스의 경우 another_a가 쓰이는 부분에 a로 교체만 하면 돼서 메모리 상에 존재하지 않아도 된다

상수에 대한 참조자

c
#include <iostream>

int main() {
  int &ref = 4;

  std::cout << ref << std::endl;
}

일반적으로 상수 값 자체는 리터럴이기 때문에 위의 코드는 컴파일 에러가 발생한다

csharp
const int &ref = 4;

하지만 상수 참조자로 선언한다면 리터럴도 참조할 수 있다

레퍼런스의 배열과 배열의 레퍼런스

less
// 레퍼런스의 배열
int a, b;
int& arr[2] = {a, b};
ini
// 배열의 레퍼런스
int main() {
  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;
  return 0;
}

솔직히 내가 이해하기로는 레퍼런스의 배열이 아래 같고, 배열의 레퍼런스가 위에 같긴 하지만
이해한 바로는 배열 전체의 레퍼런스 선언은 불가능하고, 각 레퍼런스 변수들의 배열이 가능하다라고 이해했다

ini
int arr[3][2] = {1, 2, 3, 4, 5, 6};
int (&ref)[3][2] = arr;

2차원 배열에서도 마찬가지로 위의 코드처럼 사용할 수 있다

레퍼런스를 리턴하는 함수

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

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

위 코드는 컴파일 오류는 아니지만 런타임 오류가 발생한다

왜냐면 function의 return타입은 int&인데, function안에 a는 함수의 리턴과 함께 사라지기 때문이다

ini
int& ref = a;

// 근데 a 가 사라짐
int b = ref;

이런 느낌이라고 한다

이처럼 레퍼런스는 있는데 원래 참조하던 것이 사라진 레퍼런스를 댕글링 레퍼런스(Dangling reference)라고 한다

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

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

이 코드는 a가 외부 변수를 받아오고 그 변수를 리턴하기 때문에 정상적으로 동작한다

참조자를 리턴하는 장점

레퍼런스를 리턴하게 된다면 레퍼런스가 참조하는 타입의 크기와 상관없이 딱 한번의 주소값 복사로 전달이 끝나게 된다

참조자가 아닌 값을 리턴하는 함수를 참조자로 받

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

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

(말부터 뭔가 어렵다..)
첫번째 케이스와 비슷하게 function이 return이 끝난 후 사라지는 값이기 때문에 컴파일 에러가 발생한다고 이해했다

ini
const int& c = function();

하지만 이런식으로 const 참조자로 받으면 컴파일이 되고 정상적으로 리턴값도 출력이 된다

상수 레퍼런스로 리턴값을 받게되면 해당 리턴값의 생명이 연장된다.

그리고 그 연장되는 기간은 레퍼런스가 사라질 때 까지다