[ c++ ] 참조자(reference)

개발하는 곰댕이·2021년 12월 19일
0

cpp

목록 보기
4/11

참조자란?

cpp에는 참조자라는 타입이 있는데 이 녀석은 포인터처럼 하나의 메모리 공간에 많은 변수들이 접근할 수 있도록 해 주는 녀석입니다.
그럼 이제 참조자라는 녀석에 대해서 알아보도록 하죠.

참조자의 선언

참조자는 다음과 같이 선언할 수 있습니다.

int num = 1;
int &ref = num;

새롭게 만드는 변수의 이름 옆에 & 연산자를 붙여주면 참조자로 사용할 수 있습니다.

이 참조자는 참조하는 변수의 별명을 하나 붙여주는 것으로써 참조하는 변수를 사용하는 것과 동일하게 사용이 가능합니다.
그렇기 때문에 모두 같은 값을 갖고있고, 같은 메모리 위치를 참조하고 있습니다.

이렇게 만들 수 있는 참조자는 만들 수 있는 수의 제한이 없고, 참조자를 참조할수도 있습니다.

int num = 1;
int &ref1 = num;
int &ref2 = num; // num1과 동일하게 정수 num을 참조
int &ref3 = ref1; // 참조자 num1을 참조

참조자의 특징

  1. 참조자는 기본적으로 다른 변수의 별명을 붙이는 것이므로 다음과 같이 리터럴 상수를 참조할 순 없습니다.

    int &ref = 20;
  2. 선언과 동시에 초기화가 되어야 합니다.

    int &ref;
     ref = 20; // 안됨!
  3. 한번 초기화가 되면 다른 변수를 참조할 수 없습니다.

    int a;
     int b;
     int &ref1 = a;
     ref = b; // 안됨!
     int &ref2 = NULL;//안됨!
  4. 배열의 선언이 불가능합니다.

    int &ref[3] = {} // 안됨!

참조자와 함수

참조자도 다른 변수처럼 리턴형, 매개변수로도 사용이 가능합니다.

매개변수

여기서 참조자의 특징이 나오는데, 포인터처럼 원본 변수에 접근해서 값을 변경할 수 있습니다.

void	add(int &num)
{
	num += 1;  
}

int	main(void)
{
	int	num = 1;
	add(num);
	std::cout << num << std::endl;
}

값을 보면 num이 2로 증가된걸 볼 수 있습니다.

2

이렇게 참조자는 포인터와는 다르게 원본 변수를 그대로 사용하듯 사용할 수 있습니다.

참조자와 매개변수의 선언

참조자는 선언과 동시에 초기화 되어야 하는데 매개변수는 어떻게 사용할 수 있는 걸까요??
매개변수는 함수가 호출 될 때 선언되며 매개변수로 들어온 인자로 초기화 됩니다.
예를들어

int Add(int &a, int &b);

...

int num1 = 1, num2 = 2;
Add(num1, num2);

num1과 num2를 인자로 넣어주면

int &a = num1;
int &b = num2;

이런 식으로 초기화가 되는 거죠.

반환형

  • 포인터와 마찬가지로 지역변수의 반환에 주의해야합니다.
int	&retNum(void)
{
	int num = 1;
    
	return (num); // 지역변수 num의 참조자를 반환
}

int	main(void)
{
	int &n = retNum(); // 이미 사라진 지역변수 num을 참조하고 있음
}

const 참조자와 임시변수

C++ 초기에는 x + 20과 같은 값도 참조가 가능했습니다.하지만 최근 C++에서는 이런 참조는 에러로 표시될겁니다.
물론 예외는 있습니다. 바로 const 참조자를 사용했을 때 입니다.
const 참조자를 사용해서 리터럴 상수 및 표현식을 참조 한다면 임시변수가 생성되어 임시변수에 해당 값을 복사합니다. 그리고 참조자는 해당 임시변수를 참조하는 거죠.
그래서 아래와 같은 값들이 참조가 가능해지는 겁니다.

const int &a = 20;
const int &b = 10 + 20;

그러면 임시 변수는 언제 언제 생성되는 걸까요?

  • 참조하려는 값이 참조자의 자료형과 동일한 자료형이지만 lvalue가 아닐 때
  • 참조하려는 값이 참조자의 자료형과 다르지만 해당 자료형으로 형변환이 가능할 때

이럴 때 컴파일러는 임시 변수를 생성해서 참조자에 대입하게 됩니다.

왜 최근 C++에서는 상수를 참조할 수 없게 만들었을까?

void MySwap(int &a, int &b)
{
	int temp;
    
    temp = a;
    a = b;
    b = temp;
}

단순히 값을 바꾸는 swap함수입니다. 이 함수에 매개변수로 자료형이 다른 값을 넣으면 어떻게 될까요?

long a = 3, b= 5;
MySwap(a, b);

변수 자료형을 long으로 했습니다.
언뜻 보면 문제가 없을 수도 있지만 위에서 설명했듯 참조자의 자료형으로 형변환이 가능하면 컴파일러는 임시변수를 생성해서 참조자에 대입한다고 했습니다.
그러면 그 임시변수와 변수 a, b는 다른 변수이기에 원래 함수의 의도였던 a와 b를 바꾸는건 불가능해집니다.
이러한 문제들이 있었기 때문에 제한을 하게 됐고 const를 사용하면 이런 문제가 일어나지도 않을 뿐더러 임시 변수를 이용해서 다양한 매개변수를 사용할 수 있기에 제한적으로 허용하게 된 것 입니다.

참조자와 포인터의 차이

참조자와 포인터는 사용처에선 별 반 차이가 없습니다. 딱히 눈에 띄는 것도 연산자의 구분이 될 뿐이죠.
굳이 나누자면 참조자만의 특징이 있을 뿐 그거도 포인터 const와 크게 차이나지 않습니다.
그럼 어떨 때 포인터를 사용하는지, 참조자를 사용하는지 아니면 그냥 값을 전달하던지 할까요??

포인터, 참조자, 값의 전달 각각의 사용처

  • 값에 의한 호출
    • 기본 자료형이나 작은 구조체는 굳이 포인터나 참조자를 사용할 필요가 없습니다. 그렇기 때문에 값의 복사가 이루어져도 그렇게 큰 낭비가 생기지 않습니다.
    • 만약 함수 내부에서 원본 변수의 값을 변경해야 한다면 기본 자료형은 포인터, 구조체는 포인터 혹은 참조자를 이용합니다.
  • 포인터
    • 배열은 포인터가 유일하기 때문에 포인터를 사용합니다.
  • 참조
    • 클래스는 참조로 전달하는 것이 표준이라고 합니다.
  • 참조 혹은 포인터
    • 크기가 큰 구조체는 값의 복사를 방지하기 위해서 참조자 혹은 포인터를 사용합니다.

[ c++ ] namespace
[ c++ ] 클래스, 생성자, 소멸자, 이니셜라이저, this포인터
[ c++ ] c++에서의 const와 static
[ c++ ] 참조자(reference)
[ c++ ] new와 delete
[ c++ ] 함수 오버로딩
[ c++ ] 파일 입출력 (ifstream, ofstream)
[ c++ ] 함수포인터, 멤버 함수포인터
[ c++ ]연산자 오버로딩
[ c++ ] 캡슐화란?
[ c++ ] 상속과 다형성에 대해 알아보자

0개의 댓글