개발/C, C++

[C++] 참조자 (reference) 의 정의, 사용법 및 주의 사항

growing-dev 2023. 3. 28. 22:11
반응형

C++에서 활용되는 참조자 (reference)에 대해서 알아보도록 하겠다.

 

[C++] 참조자 (reference)의 정의 및 사용법

 

참조자 (reference)의 정의

C언어에서 변수를 가리키는 것으로 포인터를 사용한다.

예를 들어 아래와 같이 포인터 변수 p를 선언하고 변수 a를 가리키게 하고 가리키는 값인 *p를 출력하면 3이 출력된다.

int main(void) {
   int *p;
   int a = 3;
   p = &a;
   std::cout << *p << std::endl;
}

참조자는 포인터 변수와 비슷하게, C++ 에서 활용되는 변수를 가리키는 방법이다.

아래처럼 int& ref = value; 와 같이 ref라는 참조자가 value를 가리키도록 한다. ref는 value를 가리키는 별명 같은 것이다.

int value;
int& ref = value;

 

 

 

참조자 (reference)의 사용법

 

내가 가리키고 싶은 변수가 있고, 그 변수의 별명을 ref 라고 지정하고 ref를 출력하는 코드이다. 포인터와는 다르게 * 같은 별도의 기호를 붙임이 않고도 ref 자체만으로도 a처럼 그대로 출력하면 된다. 즉 ref 가 a 인 셈이다. 이게 가능한 이유는 참조자는 단순히 별명 같은 것이고, 새로운 메모리를 할당하는 것이 아니라 컴파일 시에 ref와 a가 동일하게 취급되기 때문이다.

int main(void) {
   int a = 3;
   int& ref = a;
   std::cout << ref << std::endl;
}

 

아래는 함수의 인자로 전달하는 경우의 예시이다. 포인터를 사용하는 경우와 비교하기 위해 두 가지 함수를 만들어 보았다.

add_one_reference는 인자로 참조자를 받고 add_one_pointer는 인자로 포인터 변수를 받는다. 어차피 인자에는 * 혹은 & 이 붙어야 하는 것은 똑같지만 함수 내에서 값을 접근할 때 참조자는 그대로 사용하면 되지만 포인터는 * 를 붙여서 접근해야 한다. 또한 main 함수에서 호출할 때에 포인터는 변수 a에 &를 붙여서 주소값을 넘겨주어야 하지만 참조자의 경우는 그냥 넘겨주면 된다. 참조자가 훨씬 깔끔하고 직관적이다.

#include <iostream>

void add_one_reference(int& input) {
   input++;
}

void add_one_pointer(int* input) {
   *input = *input + 1;
}

int main(void) {
   int a = 3;

   add_one_pointer(&a);
   std::cout << a << std::endl;

   add_one_reference(a);
   std::cout << a << std::endl;
}

 

 

참조자(reference) 사용 시 주의 사항

1. 참조자는 새로운 변수가 아니라 별명에 불과하다는 사실을 제일 먼저 인지해야 한다.

2. 참조자는 선언과 동시에 초기화가 되어야 한다. 1번에서 말했다 시피 별명에 불과한데, 별명의 주체가 없는 null 별명은 존재할 수 없는 것이다.

3. 참조자는 한 번 지정이 되면 참조하는 대상을 변경할 수 없다. 포인터 변수처럼 이렇게 저렇게 바꿔가면서 사용하는 용도가 아니다. 참조자에 다른 변수를 지정한다면 그것은 원래 가리키던 값의 변수를 변경하는 것이 된다.

int main(void) {
   int a = 3;
   int& ref = a;
   std::cout << ref << std::endl; // ref 츌력결과 : 3
   
   int b = 5; // 새로운 b 등장
   ref = b; // ref 를 b를 가리키도록 함?? 
   // 아니다. ref는 a와 같으므로 a = b 가 되어 a 값을 5로 변경한다.
   
   std::cout << ref << std::endl; // ref 출력결과 : 5
   // b를 가리키도록 했으니 5가 출력되었나?? 아니다. ref인 a가 위에서 5로 변경되었기 때문이다.
   
   std::cout << a << std::endl; // a 출력결과 : 5
}

4. Call by value와 잘 구분해서 사용해야 하면 헷갈리면 안된다. 포인터를 사용하면 Call by value와 Call by reference 가 얼핏 봐도 구분이 되지만 참조자를 사용하면 실수할 수도 있으니 조심해야 한다.

 

 

결론

참조자는 간단한 듯 하면서도 헷갈리는 부분이 있다. 포인터와의 차이점을 명확하게 이해하고 넘어가는게 좋은 것 같다. 코드의 간결성 때문에 C++에서 실제로 굉장히 많이 사용되고 있기 때문이다.

반응형