참조자(reference)
- C++에서 다른 변수나 상수를 가리키는 또 다른 방법
- C 에서는 어떠한 변수를 가리키고 싶을 땐 반드시 포인터를 사용했음
- 참조자는 가리키고자 하는 타입 뒤에 &를 붙여 표현
- 참조자는 정의 시에 반드시 누구의 별명인지를 명시해야 함
int& // int형 변수의 참조자
double& // double형 변수의 참조자
int*& // int* (int형 포인터) 타입의 참조자
int& another_a; // 불가능
int& another_a = a; // 가능
int* p; // 포인터의 경우는 가능
- 한번 어떤 변수의 참조자가 되버린다면, 더이상 다른 변수를 참조할 수 없음
int a = 10;
int &another_a = a; // another_a 는 이제 a 의 참조자!
int b = 3;
another_a = b; // b를 가리키라는 것이 아닌, b의 값을 대입하라는 의미!
// 포인터는 가리키는 대상을 변경 가능
int a = 10;
int* p = &a; // p 는 a 를 가리킴
int b = 3;
p = &b // 이제 p 는 a 를 버리고 b 를 가리킴
- 참조자의 참조자를 만드는 것은 문법상 금지되어 있음
- 아래의 경우 y와 z 모두 x의 참조자!!
int x;
int& y = x;
int& z = y;
함수 인자로 레퍼런스 받기
#include <iostream>
int change_val(int &p) {
p = 3;
return 0;
}
int main() {
int number = 5;
std::cout << number << std::endl; // 5
change_val(number);
std::cout << number << std::endl; // 3
}
상수에 대한 참조자
- 상수 리터럴을 일반적인 레퍼런스가 참조하는 것은 불가능
- error : 'initializing' : cannot convert from 'int' to 'int &'
#include <iostream>
int main() {
int &ref = 4; // error : 'initializing' : cannot convert from 'int' to 'int &'
// ref = 5; // 이와 같이 리터럴의 값을 바꾸는 말도 안되는 행위가 가능하기 때문!!
std::cout << ref << std::endl;
}
- 단, 상수 참조자로 선언한다면 리터럴도 참조 할 수 있음
const int &ref = 4;
int a = ref; // a = 4와 동일
레퍼런스의 배열과 배열의 레퍼런스
- 레퍼런스의 레퍼런스,레퍼런스의 배열, 레퍼런스의 포인터는 존재할 수 없음
- error : 'arr' : arrays of references are illegal
int a, b;
int& arr[2] = {a, b};
문법 상 배열의 이름은 (arr) 첫 번째 원소의 주소값으로 변환이 될 수 있어야 함.
arr[1] 이 *(arr + 1) 로 바뀌어서 처리될 수 있기 때문!!
주소값이 존재 = 해당 원소가 메모리 상에 존재 인데,
레퍼런스는 특별한 경우가 아닌 이상 메모리 상에서 공간을 차지 하지 않음.
따라서 이러한 모순 때문에 레퍼런스들의 배열을 정의하는 것은 언어 차원에서 금지가 되어 있음.
* 레퍼런스가 스코프 밖에서 사용되는 경우, 메모리에 존재
- 배열의 레퍼런스는 가능
- ref가 arr을 참조 ( ref[0] 부터 ref[2] 가 각각 arr[0] 부터 arr[2] 의 레퍼런스가 됨 )
#include <iostream>
int main() {
int arr[3] = {1, 2, 3};
int(&ref)[3] = arr;
ref[0] = 2;
ref[1] = 3;
ref[2] = 1;
std::cout << arr[0] << arr[1] << arr[2] << std::endl; // 231
return 0;
}
int arr[3][2] = {1, 2, 3, 4, 5, 6};
int (&ref)[3][2] = arr;
레퍼런스를 리턴하는 함수
- function안에 정의된 a라는 변수의 값이 b에 복사됨
- function이 종료되고 나면 a는 메모리에서 사라짐
int function() {
int a = 2;
return a;
}
int main() {
int b = function();
return 0;
}
지역변수의 레퍼런스를 리턴
레퍼런스를 리턴하는 함수에서 지역 변수의 레퍼런스를 리턴하지 않도록 조심해야 함 (Dangling reference)
int& function() {
int a = 2;
return a;
}
int main() {
int b = function();
b = 3;
return 0;
}
// 아래와 동일한 의미
// 원래 참조하고 있던 변수가 이미 사라져 오류가 발생
int& ref = a;
int b = ref; // a가 사라짐
외부 변수의 레퍼런스를 리턴
- function이 리턴한 참조자는 아직 살아있는 변수인 b를 계속 참조
- c에 b의 값인 5를 대입한 것
int& function(int& a) {
a = 5;
return a;
}
int main() {
int b = 2;
int c = function(b);
return 0;
}
참조자를 리턴하는 경우 장점
큰 구조체의 경우, 해당 구조체 변수를 그냥 리턴하면 전체 복사가 발생해서 시간이 오래걸리지만,
해당 구조체를 가리키는 포인터를 리턴한다면 포인터 주소 한 번 복사로 매우 빠르게 동작함.
참조자가 아닌 값을 리턴하는 함수를 참조자로 받기
- 원칙상 함수의 리턴값은 해당 문장이 끝나면 소멸
- 하지만 예외적으로 상수 레퍼런스로 리턴값을 받게 되면, 레퍼런스가 사라질 때 까지 해당 리턴값의 생명이 연장됨
#include <iostream>
int function() {
int a = 5;
return a;
}
int main() {
const int& c = function();
std::cout << "c : " << c << std::endl;
return 0;
}
정리
함수에서 값 리턴 (int f()) | 함수에서 참조자 리턴 (int& f()) | |
값 타입으로 받음(int a = f()) | 값 복사됨 | 값 복사됨. 다만 지역 변수의 레퍼런스를 리턴하지 않도록 주의 |
참조자 타입으로 받음 (int& a = f()) | 컴파일 오류 | 가능. 다만 마찬가지로 지역 변수의 레퍼런스를 리턴하지 않도록 주의 |
상수 참조자 타입으로 받음 (const int& a = f()) | 가능 | 가능. 다만 마찬가지로 지역 변수의 레퍼런스를 리턴하지 않도록 주의 |
'언어 > C, C++' 카테고리의 다른 글
[C++] Standard Template Library - STL (0) | 2023.08.24 |
---|---|
[C++] 객체 지향 프로그래밍 (0) | 2023.08.16 |
[C++] new, delete (0) | 2023.08.14 |
[C++] namespace (0) | 2023.08.11 |
[C] 포인터 (0) | 2023.06.29 |