[C++] 참조자(Reference)
2022. 2. 8. 17:40ㆍLanguages/C++
*<씹어먹는 C++>을 보며 공부하여 정리한 글입니다.
1. 참조자(Reference)란?
- C 언어에서는 함수에 인자 전달 시, 원본을 바꾸는 방법으로는 Call by address밖에 없었다.
- 하지만, C++에서는 Call by address 외에도 다른 변수나 상수를 가리킬 수 있는 참조자(Reference) 방식또한 지원한다.
- 포인터와 달리 &와 * 연산자 사용이 줄기 때문에 코드를 훨씬 간결하게 나타낼 수 있다.
- scanf()와 달리 cin이 &를 사용하여 주소값을 줄 필요가 없는 것도 cin 내부적으로 참조를 받기 때문이다.
int x = 5;
int& referenceX = x; // 참조자 선언
referenceX = 10;
std::cout << "x = " << x << std::endl;
std::cout << "referenceX = " << referenceX << std:endl;
/* 결과
x = 10
referenceX = 10
*/
- 참조자 선언 방법은 가리키고자 하는 타입 뒤에 &를 붙이면 된다. ( Ex. int& )
- 참조자 선언을 통해 컴파일러에게 또 다른 이름이라고 알려주는 것
- 그렇기 때문에 위 코드에서 referenceX의 값을 바꿔도 사실상 x에서 작업을 하는 것과 같다.
참조자는 반드시 초기값을 주어야 한다.
- 포인터와 달리, 참조자는 정의 시에 반드시 누구의 별명인지 명시 (그러지 않을 경우 오류 발생)
int x = 5;
// int& referenceX; 오류
int& referenceX = x; // 초기값 지정 필수
- 허나, 함수의 매개변수로 받을 때는 명시하지 않아도 된다.
void Swap(int& x, int& y) { // 매개변수로 참조를 받을 수 있음
int temp = x;
x = y;
y = temp;
}
int main() {
int x = 10;
int y = 20;
Swap(x, y);
}
- Swap(x, y) 함수를 호출하는 시점에서 매개변수 int& x, int& y가 정의되므로 사실상 int& x = x, int& y = y가 실행됨
- 따라서, 문제가 될 소지가 없음
한 번 별명 설정이 되면, 다른 별명으로 변경이 불가능
- 참조자 별명이 설정되면, 다른 별명으로 설정하려고 해도 단순 대입 연산으로 취급된다.
- 번외로, 포인터는 다른 대상을 가리키도록 자유롭게 변경이 가능하다.
int x = 5;
int& referenceX = x; // 별명 설정 연산
int y = 10;
referenceX = y; // referenceX = 10; 과 같은 연산
// &referenceX = y; 불가능
참조(Reference)는 메모리 상에 존재하지 않을 수도 있다.
- 포인터 변수 역시, 메모리 주소를 담아놓는 변수이기 때문에 메모리 상에 존재한다. (64bit 아키텍처의 경우, 8 byte)
- 허나, 참조의 경우에는 참조가 쓰이는 코드 자리는 참조한 원본 대상으로 교체하면 된다.
- 그렇기에 참조는 메모리 상에 존재할 필요가 없으나, 반드시 그런 것은 아니다.
int x = 10;
int& referenceX = x;
...
referenceX = 20; // x = 20;으로 컴파일러가 바꾸면 된다.
2. 참조자와 관련된 금지 사항
참조의 참조자를 생성하는 경우는 불가능
int x = 5;
int& y = x;
int& z = y; // 참조자의 참조자를 만드는 게 아닌 x의 참조자를 생성하는 연산
std::cout << x << std::endl; // 5
std::cout << y << std::endl; // 5
std::cout << z << std::endl; // 5
단순 리터럴에 대한 참조자 생성은 불가능
- 상수 그 자체인 리터럴에 대한 참조자 생성이 가능하면, 리터럴 값 자체를 바꿀 수 있게 되는 모순이 생기기 때문
- 상수 참조자로 선언한다면 리터럴로 참조가 가능
// int& x = 10; 'initializing' 오류 발생
const int& x = 10; // 상수 참조자로는 리터럴 상수 참조 가능
참조 배열을 생성하는 것은 불가능
/*
참조 배열은 사용할 수 없다.
int x, y;
int& arr[2] = { x, y };
*/
- 참조자의 경우, 특별한 경우가 아닌 이상 메모리 상에서 공간을 차지하지 않는다.
- 배열의 특성상, 배열의 이름은 첫 번째 원소의 주소값으로 변환이 될 수 있어야 하는데 여기에 모순이 생긴다.
- 배열에 대한 참조는 가능하다. 단, 포인터와 달리 반드시 배열의 크기를 명시해야 함
// 배열 참조는 사용할 수 있다.
int arr[3] = {10, 20, 30};
int (&refArr)[3] = arr; // 배열 크기 명시
for (int i = 0; i < 3; i++) {
std::cout << refArr[i] << std::endl;
}
// 이차원 배열 역시 동일
int arr[2][2] = {1, 2, 3, 4};
int (&refArr)[2][2] = arr;
포인터에 대한 참조자는 생성 불가능
int x = 5;
int* ptr = &x;
// int& refPtr = ptr; 오류
참조를 리턴하는 함수에서 지역 변수의 참조를 리턴하지 않도록 조심
- 함수 내부에서 지역변수에 대한 참조를 리턴하고 나면, 지역변수의 생존 시간 특성 때문에 자동으로 메모리 반납이 되고, 결과적으로 별명만 남게 된다.
- 그렇기에 오류가 발생할 수 있다.
- 이렇게 참조만 존재하고 참조하던 게 사라진 참조자를 Dangling reference라고 부른다.
int& f() {
int x = 5;
return x; // 지역변수 참조 리턴
}
int main() {
int x = f(); // 참조만 남고 참조하던 변수는 사라짐
...
}
- 외부 변수에 대한 참조를 리턴하는 방식을 사용하는 것을 괜찮다.
- 참조자를 리턴할 시 장점은 함수에 인자 전달이나 리턴 시, 값 복사가 이루어지는 시간 소요가 없다.
int& f(int& x) {
x = 5;
return x;
}
int main() {
int x = 10;
int y = f(x); // x에 대한 참조를 리턴받음
...
}
- 참조자가 아닌 값을 리턴하는 함수를 참조자로 받을 경우, const 참조자를 사용하여 받으면 가능하다.
- C++에서 예외 규칙으로, 상수 참조자로 리턴값을 받게 되면 해당 리턴값의 생명이 연장된다.
- 연장되는 기간은 참조자가 사라질 때까지
int f() {
int x = 5;
return x;
}
int main() {
// int& y = f(); Dangling reference
const int& y = f(); // 가능
...
}
728x90
반응형
'Languages > C++' 카테고리의 다른 글
[C++] 초기화 리스트(Initializer list), static (0) | 2022.02.09 |
---|---|
[C++] 복사 생성자(Copy constructor), 얕은 복사(Shallow Copy), 깊은 복사(Deep Copy) (0) | 2022.02.09 |
[C++] 생성자(Default Constructor) 사용 시 주의사항 (0) | 2022.02.08 |
[C++] C++ 컴파일러에서 함수를 오버로딩(Overloading)하는 과정 (0) | 2022.02.08 |
[C++] 이름공간(namespace) 사용 시 주의사항 (0) | 2022.02.07 |