참조자(Reference)

참조자(Reference)

변수를 선언하면 자료형의 크기만큼 메모리 공간을 할당받고, 할당된 공간을 변수명으로 접근이 가능하다. 참조자란 할당된 메모리 공간을 접근할 때 변수명을 대신할 수 있는 또 하나의 이름이다. 즉, 변수에 별명을 하나 붙여 주는것 입니다.

참조자의 선언

참조자는 참조명 앞에 &를 붙여 선언합니다. 참조자의 수는 제한이 없으며, 참조자를 대상으로 참조할 수 있습니다.

#include <iostream>
using namespace std;

int main(void)
{
  int num = 10;
  int &ref = num; // 참조
  int &dref = ref; // 참조
  
  cout << &num << endl; // 주소출력
  cout << &ref << endl;
  cout << &dref << endl;
  return 0;
}

참조자는 변수에 대해서만 선언이 가능하고, 선언과 동시에 참조를 해야만 한다.

int &ref = 20; // 리터럴은 참조할 수 없다.
int &ref; // 참조자는 선언과 동시에 참조해야 한다.
int &ref = NULL // NULL은 참조할 수 없다.

참조자와 함수

이미 배웠던 것들

  • Call-by-value : 값을 인자로 전달하는 호출 방식

  • Call-by-reference : 주소값을 인자로 전달하는 호출방식

복습의 의미에서 다시 코드를 봅시다.

Call-by-value

#include <iostream>
using namespace std;

void swap(int a, int b);

int main(void)
{
  int num1 = 10;
  int num2 = 20;
  cout << "swap 함수 호출전 : " << num1 << ", " << num2 << endl;
  swap(num1, num2);
  cout << "swap 함수 호출후 : " << num1 << ", " << num2 << endl;
  return 0;
}

void swap(int a, int b) {
  int temp = a;
  a = b;
  b = temp;
  cout << "swap 함수 내  부 : " << a << ", " << b << endl;
}

매개변수는 복사되어 전달되므로 swap 함수내부에서 main함수의 변수에 접근할 수 없다.

Call-by-reference

#include <iostream>
using namespace std;

void swap(int *a, int *b); // 포인터로 받음

int main(void)
{
  int num1 = 10;
  int num2 = 20;
  cout << "swap 함수 호출전 : " << num1 << ", " << num2 << endl;
  swap(&num1, &num2); // 주소값 전달
  cout << "swap 함수 호출후 : " << num1 << ", " << num2 << endl;
  return 0;
}

void swap(int *a, int *b) {
  int temp = *a;
  *a = *b;
  *b = temp;
  cout << "swap 함수 내  부 : " << *a << ", " << *b << endl;
}

주소값을 전달하므로 swap 함수 내부에서 main함수의 변수에 접근하여 두 값을 교환할 수 있다.

참조자를 이용한 Call-by-reference

#include <iostream>
using namespace std;

void swap(int &a, int &b); // 참조자로 받음

int main(void)
{
  int num1 = 10;
  int num2 = 20;
  cout << "swap 함수 호출전 : " << num1 << ", " << num2 << endl;
  swap(num1, num2); // 변수 전달
  cout << "swap 함수 호출후 : " << num1 << ", " << num2 << endl;
  return 0;
}

void swap(int &a, int &b) {
  int temp = a;
  a = b;
  b = temp;
  cout << "swap 함수 내  부 : " << a << ", " << b << endl;
}

형식매개변수를 참조자로 받게되면 실매개변수의 참조자가 되므로 별칭이 생긴것과 같으므로 swap함수 내부에서 main 함수의 변수에 접근이 가능하다.

참조자와 const

참조자를 이용한 Call-by-reference는 포인터를 활용하는것 보다 활용하기 쉽기 때문에 참조자를 사용하는것이 좋아보인다. 하지만 참조자를 사용하는것이 무조건 좋은것은 아니다. 다음의 코드를 예상해보자

int num = 20;
func(num);
cout << num;

func 함수 호출후에 num값을 출력했을때 결과가 어떻게 될것인지 생각해보자. func 함수의 원형이 다음과 같다면

void func(int a); // 이렇다면 20이 출력된다.
void func(int &a); // 참조로 받는다면 a값이 변경될 수도 있다.

const를 사용하여 참조로 받지만 a값을 변경할 수 없도록 상수화 한다.

void func(const int &a);

반환형이 참조자료형인 함수

참조자도 반환할 수 있다. 참조자도 하나의 자료형으로 생각하자!

#include <iostream>
using namespace std;

int& add(int&); // 참조자를 반환

int main(void)
{
  int num1 = 10;
  int &num2 = add(num1); // 참조자로 받는다.
   
  cout << &num1 << endl;
  cout << &num2 << endl;
  return 0;
}

int& add(int &a) {
  a++;
  return a;
}

잘못된 참조 반환

#include <iostream>
using namespace std;

int& add(int&);

int main(void)
{
  int num1 = 10;
  int &num2 = add(num1); // 스택에서 제거되므로 쓰레기 값이 들어가짐
   
  cout << num1 << endl;
  cout << num2 << endl; // 쓰레기값 출력
  return 0;
}

int& add(int &a) {
  int num = 20;
  num += a;
  return num; // 반환후 스택에서 제거됨
}

상수화된 변수를 참조하는 const 참조자

const int num = 10; // 상수화된 변수
const int &ref1 = num; // 상수화된 변수 참조 가능
int &ref2 = num; // 컴파일 에러
const int &ref3 = 50; // 리터럴 상수도 참조 가능

리터럴 상수가 참조가 가능한 것은 컴파일러가 리터럴 상수를 임시 변수를 만들고 그것을 참조자가 참조하게 되는것이다.

Last updated