Reference Operator 란?
Refercece Operator는 C의 Pointer 와 동일한 역할을 하는 것으로
A Reference allows to declare an alias to another variable.
C++ 에서는 함수가 value를 리턴할 때, return 할 value를 stack에 카피하는데 calling function은 stack에 저장된 변수를 읽어서 그걸 다시 변수에 copy 한다.
하지만 Reference Operator는 함수를 부를 때 스택으로 Parameter를 Copy 하는 비용을 줄여준다!!
예제로 살펴보도록 하자!
Code ( Pointer vs Reference )
// using pointer
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
// using reference
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
// find max elements
int& max(int a[], int n){
int x = 0;
for(int i = 0; i < n; i++)
if ( a[i] > a[x] ) x = i;
return a[x];
}
int main(){
int a[] = {12, 42, 33, 99, 63};
int n = 5;
max(a,5) = 0;
for(int i = 0; i < n; i++){
cout << a[i] << " ";
}
}
Potiner와 같은 기능을 하지만 Reference의 경우가 훨씬 간단한 것을 알 수 있다.
max(a,5) = 0; 부분을 보면 변수에 함수의 리턴 값을 따로 할당하지 않고 Reference Operator를 이용해서 비용을 줄인 것을 알 수 있다.
✔️ 만약 참조형을 변수로 선언할 경우 선언과 동시에 초기화 해줘야한다.
- 참조형 변수는 pointer 변수와 다르게 null 참조를 할 수 없기 때문이다.
const Reference
struct Person {
char name[40];
int age;
};
void print(const Person& k) {
cout << "Name: " << k.name << endl;
cout << "Age: " << k.age << endl;
}
int
main(){
Person man{"Adam", 316}; print(man);
return 0;
}
const 형을 같이 넘겨줌으로써 바뀌지 않아야 할 변수를 보호할 수 있다.
또한 44 bytes 대신 오직 4 btyes 만 function으로 보낼 수 있다. ( 메모리 아낄 수 있음 )
Detail about Reference Operator
#include <iostream>
int main() {
int a = 3;
int& another_a = a;
another_a = 5;
std::cout << "a : " << a << std::endl;
std::cout << "another_a : " << another_a << std::endl;
return 0;
}
// a : 5
// another_a : 5
int& another_a = a;
a 의 참조자 another_a를 정의하였다. 위와 같이 선언함으로써 another_a 는 a의 또 다른 이름이라고 컴파일러에게 알려주는 것과 동일하다. 즉, another_a 는 a의 별명인 셈이다.
고로 레퍼런스는 반드시 처음에 누구의 별명이 될 것인지를 지정해줘야한다.
int& another_a; // 불가능
int* p // 가능
이러한 것은 불가능하다!!
반면에 pointer의 경우는 null을 참조할 수 있어서 가능하다!
예를 들어서 아래와 같은 코드를 살펴보자!
int a = 10;
int &another_a = a; // another_a 는 이제 a 의 참조자!
int b = 3;
another_a = b; // ??
another_a = b 를 뭘 의미할까?
여기서 another_a 는 a의 별명이 됐기 때문에 이는 a = b 와 동치라고 보면 된다.
Reference가 한 번 별명이 된다면 *절대로* 다른 이의 별명이 될 수 없다. (중요)
다음 코드를 한번 봐보자!
// 참조자 이해하기
#include <iostream>
int main() {
int x;
int& y = x;
int& z = y;
x = 1;
std::cout << "x : " << x << " y : " << y << " z : " << z << std::endl;
y = 2;
std::cout << "x : " << x << " y : " << y << " z : " << z << std::endl;
z = 3;
std::cout << "x : " << x << " y : " << y << " z : " << z << std::endl;
}
이 코드를 돌렸을 때 x, y, z 의 값은 어떻게 될까??
x : 1 , y : 1 , z : 1 그 다음은 2 , 3 으로 전부 동일 할 것이다.
왜 이런 결과가 나오는 것일까?
여기서 z 는 y의 참조자인데 그럼 z는 참조자의 참조자가 되는 셈이다. 하지만 C++ 문법 상 참조자의 참조자를 만드는 것은 금지되어 있다.
고로 z, y 는 전부 x 의 참조자라고 볼 수 있다.
다음 코드를 한번 봐보자!
#include <iostream>
int main() {
int &ref = 4; // wrong
const int &ref = 4; // okay
int a = ref; // a = 4;
std::cout << ref << std::endl;
}
Reference는 상수 값을 참조할 수 없다. 상수 값 자체는 literal 이기 때문이다.
고로 상수 참조자로 선언하면 참조할 수 있다.
배열과 Reference
이러한 코드를 컴파일했다고 하자. 어떤 결과가 나올까?
컴파일을 해보면 컴파일 오류가 뜰 것이다. 왜?
int a, b;
int& arr[2] = {a, b};
C++ 에서 배열은 첫 번째 원소의 주소값에 + 되어서 다음 Element를 참조하는 구조인데,, Reference 같은 경우 특별한 경우가 아니면 메모리 상에서 공간을 차지 하지 않기 때문에 이같은 선언은 불가능한 것입니다.
""공간을 차지 하지 않는 이유"" 는 굳이 별명을 위해서 메모리 상에 공간을 할당할 필요가 있을까?
왜냐면 별명이 쓰이는 자리를 원래 주인으로 전부 교체하면 되니까! 물론 그렇다고 해서 항상 존재하지 않는 것은 아닙니다!
그럼 어떻게 배열을 참조하지??
#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;
return 0;
}
이와 같이 참조를 하면 가능합니다.
이때 중요한 것은 배열을 Reference 하려면 반드시 배열의 길이를 명시해야 합니다!
Reference를 리턴하는 함수
// 지역변수 리턴
int& function() {
int a = 2;
return a;
}
int main() {
int b = function();
b = 3;
return 0;
}
만약 다음과 같은 Function을 int 타입 b로 받는다면 어떻게 될까요??
아마도 런타임 에러가 날 것입니다.
왜? Function 안의 a는 지역변수라서 함수가 종료되면 메모리 상에서 사라지게 됩니다.
그런데 a의 별명을 b 에 복사하려고 했더니 a 는 사라지고 결국 a 의 별명만 남은 셈입니다.
이를 Dangling 이라고 하는데 위 처럼 레퍼런스를 리턴하는 함수에서 지역변수를 리턴하지 않도록 해야합니다.
int& function(int& a) {
a = 5;
return a;
}
int main() {
int b = 2;
int c = function(b);
return 0;
}
이와 같은 코드를 볼까요?
눈치를 채셨겠지만 " c = b " 와 동치라는 것을 알 수 있습니다.
이렇게 참조자를 리턴할 때 좋은 점은 만약에 데이터의 크기가 엄청 큰 구조체가 있을 때 해당 구조체 변수를 리턴하면
전체 복사가 발생해서 시간이 오래걸리지만 레퍼런스를 리턴해주면 그냥 포인터 주소 한 번 복사로 매우 빠르게 끝낼 수 있는 장점이 있습니다.
마지막 코드는 바로 상수를 리턴 받는 레퍼런스 변수입니다. 다음 코드를 함께 보시죠!
#include <iostream>
int function() {
int a = 5;
return a;
}
int main() {
const int& c = function();
std::cout << "c : " << c << std::endl;
return 0;
}
function의 리턴값은 상수이고, 본래 reference는 상수 리터럴을 참조할 수 없지만 const 키워드를 붙여줌으로써 참조할 수 있습니다.
여기까지 C++ 의 중요한 문법 중 하나인 Reference 를 다뤘습니다.
'C++ > C.S 필요한 C++ 문법!' 카테고리의 다른 글
[중요] C++ 0.07 - new, delete (0) | 2021.10.26 |
---|---|
C++ 0.05 - Default Function Arguments (0) | 2021.10.26 |
C++ 0.04 - Inline functions (0) | 2021.10.26 |
C++ 0.03 - Input & Output (0) | 2021.10.25 |
C++ 0.02 - Namespaces (0) | 2021.10.25 |