변수명은 할당된 메모리 공간에 이름을 붙이는 것과 같다. 참조자는 이미 변수명이 붙어있는 메모리 공간이 제 2의 별칭을 붙이는 것과 같다. 변수명을 통해 메모리 공간의 데이터를 조작할 수 있는 것과 마찬가지로, 참조자를 통해서도 메모리 공간의 데이터를 조작할 수 있다.
변수를 이용해 참조자를 초기화하면, 변수를 참조할 수 있는 참조 값이 참조자에 저장된다.
참조자는 아래와 같이 선언될 수 있다.
#include <iostream>
using namespace std;
int main(void)
{
int num1=1020;
int &num2=num1;
num2=3047;
cout<<"VAL: "<<num1<<endl;
cout<<"REF: "<<num2<<endl;
cout<<"VAL: "<<&num1<<endl;
cout<<"REF: "<<&num2<<endl;
}
그리고 참조자는 아래와 같은 특징을 가진다.
- 참조자의 수에는 제한이 없다.
int num1=2749;
int &num2=num1;
int &num3=num1;
int &num4=num1;
- 참조자를 대상으로 참조자를 선언하는 것도 가능하다.
int1=2759;
int &num2=num1;
int &num3=num2;
int &num4=num3;
- 참조자는 변수에 대해서만 선언이 가능하고, 선언됨과 동시에 누군가를 참조해야만 한다. 즉, 다음과 같은 선언은 유효하지 않다.
int &ref=20; //error
- 참조자를 미리 선언했다가 후에 누군가를 참조하는 것은 불가능하며, 참조의 대상을 바꾸는 것도 불가능하다.
int &ref; //error
- 참조자를 선언하면서 NULL로 초기화하는 것도 불가능하다.
int &ref=NULL; //error
- 참조자를 사용하면 Call-by-reference의 방식으로 변수를 조작할 수 있다.
#include <iostream>
using namespace std;
void SwapByRef2(int &ref1, int &ref2)
{
int temp=ref1;
ref1=ref2;
ref2=temp;
}
int main(void)
{
int val1=10;
int val2=20;
SwapByRef2(val1, val2);
cout<<"val1: "<<val1<<endl;
cout<<"val2: "<<val2<<endl;
return 0;
}
c++은 참조자를 사용하여 Call-by-reference를 할 수 있으므로 개발자는 코드리뷰를 할 때 함수 외부에 변수가 변경되는지 아닌지를 확인해야 하는 불편함이 있다.
따라서, 함수 내에서, 참조자를 통한 값의 변경을 진행하지 않을 경우, 참조자를 const로 선언해서, 함수의 원형만 봐도 값의 변경이 이뤄지지 않음을 알 수 있게 하는 것이 좋다.
//const -> 함수 HappyFunc 내에서 참조자 ref를 이용한 값의 변경을 하지 않음을 명시
void HappyFunc(const int &ref) { . . . . }
- 참조자는 반환형으로도 사용될 수 있으며, 보통 참조자의 초기화에 함수의 반환을 사용할 때 사용된다.
//RefReturnOne.cpp
#include <iostream>
using namespace std;
//참조자에서 사용된 지역변수는 함수의 반환과 함께 소멸된다.
//하지만 참조사가 소멸된다고 해서 참조자가 참조하던 변수가 소멸하지는 않는다.
int& RefRetFuncOne(int &ref)
{
ref++;
return ref;
}
int main(void)
{
int num1=1;
int &num2=RefRetFuncOne(num1);
num1++;
num2++;
cout<<"num1: "<<num1<<endl;
cout<<"num2: "<<num2<<endl;
return 0;
}
//RefReturnTwo.cpp
//참조자 반환을 일반 자료형으로 받는 것 또한 가능하다.
//단, 그러한 경우 참조자를 받은 일반 변수와 참조자로 참조한 변수는 서로 별개이다.
#include <iostream>
using namespace std;
int& RefRetFuncOne(int &ref)
{
ref++;
return ref;
}
int main(void)
{
int num1=1;
int num2=RefRetFuncOne(num1);
num1+=1;
num2+=100;
cout<<"num1: "<<num1<<endl;
cout<<"num2: "<<num2<<endl;
return 0;
}
//RefReturnThree.cpp
//함수가 인자를 참조형으로 받지만 반환은 일반형으로 하는 경우
#include <iostream>
using namespace std;
int RefRetFuncTwo(int &ref)
{
ref++;
return ref;
}
int main(void)
{
int num1=1;
int num2=RefRetFuncTwo(num1);
num1+=1;
num2+=100;
cout<<"num1: "<<num1<<endl;
cout<<"num2: "<<num2<<endl;
return 0;
}
- const를 통해 상수화된 변수는 마찬가지로 const 참조자로만 참조할 수 있다.
const int num=20;
//int &ref=num; -> 컴파일 에러
const int &ref=num;
그리고 const 참조자를 상수화된 변수가 아닌, 상수로 초기화할 경우, 임시변수를 선언하여 참조자가 임시변수를 참조하게 한다.
const int &ref = 30; //임시변수를 선언하여 30을 저장 후 ref가 임시변수를 참조
따라서 아래와 같은 경우가 가능해진다.
int Adder(const int &num1, const int &num2)
{
return (num1 + num2);
}
int main()
{
using namespace std;
cout<<Adder(3, 4)<<endl;
}
- 다음과 같이 참조자를 사용하여 포인터를 사용하지 않고 힙 영역에 접근할 수 있다.
int *ptr = new int;
int &ref = *ptr; //힙 영역에 할당된 변수에 대한 참조자 선언
ref = 20;
cout<<*ptr<<endl; //출력 결과는 20!
참고자료