참조
1. 한국어의 단어
2. 프로그래밍 언어의 기능
Reference. 다른 변수를 가리키는 변수로, 포인터와 유사한 기능을 한다.[1]
2.1. Java, C\#
Java와 C#은 클래스 변수를 선언하면 인스턴스가 아닌 참조자를 만들게 되며, 클래스 인스턴스를 만들려면 new로 할당을 해야 한다. 만들어진 참조자는 포인터처럼 쓰면 되며, 포인터와 다른 점은 정수나 다른 종류의 클래스를 강제로 넣는 등의 강제 형변환이 막혀 있다는 점이다.
2.2. C++
C++의 참조는 다른 언어와는 조금 다르다. 만드는 순간에만 참조할 변수를 선택할 수 있고 한번 만들어진 참조는 변경할 수 없다. 이는 다른 언어는 포인터를 참조가 완전히 대체하지만 C++은 그렇지 않기 때문.
예를 들어, int형 변수 var를 참조하는 참조자를 만들려면
int& ref = var;
라고 쓰면 된다.[2] 이로서 var과 ref는 같은 메모리 공간을 나타내게 된다.값에 액세스할 때는 * 표시가 필요 없이 그냥 변수처럼 쓸 수 있다.
ref = 1; //var = 1;과 동일
어떤 함수가 참조자를 리턴하고, 반환형이 참조형일 경우 반환값은 참조자 그 자체이다.int& Example(int& pref) {
pref++;
return pref;
}
int main() {
int num = 20;
int& ref = Example(num); //int& ref = pref;와 동일
printf("%d\n", ref);
return 0;
}
이렇게 하면 pref가 참조하던 변수 num을 ref도 참조하게 된다.함수의 매개변수에 사용하는 것도 가능하다.
template
void swap(T& a, T& b) {
T tmp = a;
a = b;
b = tmp;
}
다만 이 경우 주의할 점이 있다. 아래 '주의할 점' 문단을 참조.참조자는 반드시 '''선언과 동시에 변수를 참조하여야 한다.''' 따라서 아래와 같이 먼저 참조자를 선언해놓고 나중에 변수를 참조하는 건 안 된다.
int& ref; //컴파일 에러
상수를 참조하는 것 역시 안 된다.const int& ref = 20; //컴파일 에러
주소값을 반환받는 것도 불가능하다. 주소값 역시 상수이기 때문이다.int num = 20;
int& ref = # //컴파일 에러
이미 선언된 참조자가 다른 변수를 참조하는 것도 안 된다. 아래와 같은 문장은 num을 참조하는 게 아니라, 원래 참조하던 변수 var의 값을 num의 값으로 바꾸는 것이 된다.int& ref = var;
ref = num;
NULL로 초기화하는 것도 불가능하다.[3]int& ref = NULL; //컴파일 에러
2.2.1. 주의할 점
다음의 코드를 보자.
void func(int& r) {
...
}
int v = 30;
func(v);
printf("%d\n", v);
C의 포인터를 이용하면 v의 값이 100% 30으로 출력되지만, 위처럼 C++의 참조자를 사용하면 얼마가 나올지 알 수 없다. Call by Reference에 의하여 매개변수로 선언된 참조자 r이 func() 함수 내에서 인자 v의 값을 바꿀 수 있기 때문이다. 이러한 참조자의 사용은 코드의 가독성을 해치게 된다.바로 이러한 문제를 해결하기 위해 const 참조자가 사용된다. const 참조자는 자신이 참조하는 변수의 값을 바꿀 수 없다. 즉 아래의 예시처럼 r을 const 참조자로 만들면, func() 함수 내에서 r의 값(=v의 값)을 수정할 수 없고 접근만 허용되므로 v의 값이 100% 30임을 보장할 수 있다.
void func(const int& r) {
...
}
int v = 30;
func(v);
printf("%d\n", v);
const 참조자는 const 변수를 참조할 수 있다.const int num = 20;
int& ref = num; //컴파일 에러
const int& cRef = num;
또한 const 참조자는 상수도 참조할 수 있다. 단, 엄밀히 말하면 상수 자체가 아니라 컴파일러에 의해 내부적으로 생성된 임시변수에 상수를 복사한 다음, 그 임시변수를 참조하는 것이다.const int& ref = 5;
const 참조자의 또다른 기능은 임시 값(temporary value)를 참조할 수 있다는 것이다. 아래 코드에서 ref에 실제로 대입되는 것은 "Temp" 자체가 아닌 "Temp"가 위치한 주소 값이다. 즉 원본이 아니라 원본에서 파생된 임시 값이 전달되므로 일반적인 참조자로는 참조가 불가능하다. 이럴 때 필요한 것이 const 참조자이다. 참고로, &가 두 개 붙은 r-value 참조자(ex: std::string&& ref) 역시 임시 값을 참조할 수 있다.const std::string& ref = "Temp";
2.3. 관련 문서
[1] 참조자도 내부적으로는 포인터로 변환되어 동작하기 때문에, 사실상 동일한 기능이다.[2] & 연산자가 이미 선언된 변수의 앞에 붙으면 해당 변수의 주소값을 반환하라는 의미지만, 새로 선언되는 변수의 앞에 붙으면 참조자를 선언하는 의미를 가진다.[3] NULL은 매크로이며 0의 값을 가진다.