CPP/씹어먹는 c++

[c++개념공부]참조자

뜨거운 개발자 2023. 1. 27. 19:24

참조자란

#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;
}

C에는 변수를 지정할 때 포인터만을 사용했다면, c++에서는 참조자(레퍼런스)가 등장했습니다.

레퍼런스의 특징

  1. 선언할 때 반드시 누구의 참조자인지 명시해야만 한다.
  1. 한번 지정이 되면 다른것의 참조자가 될수가 없다.
  1. 레퍼런스는 메모리상에 존재하지 않을 수도 있다.
  1. 레퍼런스의 레퍼런스,레퍼런스의 배열, 레퍼런스의 포인터는 존재할 수 없다.
  1. 배열들의 레퍼런스는 int arr[3] = {1, 2, 3}; int (&ref)[3] = arr; 같이 사용이 가능하다.
  1. 상수는 상수 참조자를 써야하고 다음과 같이 const int &ref = 4; 사용된다.
  1. 지역 변수의 레퍼런스를 리턴하는 것은 어떤 상황에서도 금지되어있다. 댕글링 레퍼런스를 발생시켜서 오류가 발생하고, 제대로 사용하려면 외부변수의 레퍼런스를 리턴해야한다.

사용법

참조자를 정하는 방법은 가리키고 하는 타입 뒤에 &를 붙히면 된다.

int 같은 경우는 int &double 같은 경우는 double & 로 하면 됩니다.

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

따라서 int& another_a; 같은 문장은 불가능합니다.

다만 함수 인자로 레퍼런스 타입을 사용하는 것은 가능하다.

2. 레퍼런스는 한번 별명이 되면 다른이의 별명이 될 수가 없습니다.

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

존재하지 않는경우

int a = 10;
int &another_a = a;

이런식으로 생긴 경우가 할당하지 않는 경우인데 컴파일러가 모든 another_a부분을 a로 바꿔서 사용하면 된다.

존재하는 경우

함수의 매개변수를 레퍼런스로 받을때 매개변수로 전달할 변수의 주소값이 새로운 메모리 공간(스택)에 저장되고 포인터처럼 그 메모리 공간을 레퍼런스가 사용하는 형태이다.

4. 레퍼런스의 레퍼런스,레퍼런스의 배열, 레퍼런스의 포인터는 존재할 수 없다.

int& arr[2] = {a, b}; 다음과 같은 코드는 안된다.

왜냐면 c++에서 배열을 처리하는 방식은 문법 상 배열의 이름은 (arr) 첫 번째 원소의 주소값으로 변환이 될 수 있어야 합니다.

이 때문에 arr[1]과 같은 문장이 *(arr + 1)로 바뀌어서 처리될 수 있기 때문이죠.

주소값이 존재한다라는 의미는 해당 원소가 메모리 상에서 존재한다.라는 의미와 같습니다. 하지만 레퍼런스는 특별한 경우가 아닌 이상 메모리 상에서 공간을 차지 하지 않습니다.

5. 배열들의 레퍼런스

#include <iostream>

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;
}

이차원 배열의 경우

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

6. 상수 참조자

#include <iostream>

int main() {
  int &ref = 4;

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

다음과 같은 코드는 컴파일러가 에러를 출력한다.

상수(리터럴 값)를 일반적인 참조자가 참조하는 것은 금지된다.

다음과 같이 const int &ref = 4; 상수 참조자로 선언한다면 리터럴 값도 참조할 수가 있다.

7. 댕글링 레퍼런스

지역변수의 레퍼런스를 리턴하는 것은 댕글링 레퍼런스를 발생시키기 때문에 사용할 수 없다.

오류코드

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

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

외부변수의 레퍼런스 리턴

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

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

다음과 같이하면 가능하다.

또한 함수에서 참조자로 리턴을 하고 값 타입으로 받게되면 그 값이 복사가 된다.

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

오류코드

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

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

만약 다음과 같은 코드를 사용하면 지역변수를 리턴해버려서 지역변수의 메모리를 함수를 빠져나가자마자 사라지게 되서 즉시 댕글링 레퍼런스가 됩니다.

다만 C++에서는 상수레퍼런스를 사용하면 이것을 받을 수 있도록 했습니다.

#include <iostream>

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

int main() {
  const int& c = function();
  std::cout << "c : " << c << std::endl;
  return 0;
}

상수 레퍼런스로 리턴값을 받게 되면 해당 리턴값의 생명이 연장됩니다. 그리고 그 연장되는 기간은 레퍼런스가 사라질 때 까지 입니다.


Uploaded by N2T

728x90