참조자 공부 내용 정리

2023. 3. 17. 20:21개인공부/C++

레퍼런스 선언하기

int형 변수의 참조자를 만들고 싶을 때에는 int&를 double의 참조자를 만들려면 double&이다. 자료형 뒤에 &를 붙여서 선언이 가능하다. 참조자는 컴파일러에게 num1의 또 다른 이름은 num1이라고 알려주는 것이다.

1
2
3
4
5
6
7
8
#include <iostream>
 
int main()
{
    int num1 = 5;
    int& num2 = num1;
    return 0;
}
cs

 레퍼런스 특징

래퍼런스는 반드시 처음에 누구의 별명이 될 것인지 지정해합니다. 

1
2
    int& another_a;
   // 오류: 참조 변수에 이니셜라이저가 필요합니다.
cs

이런 식으로 참조자는 이니셜라이저 없이는 선언이 불가능합니다. 두 번째 특징으로는 레퍼런스는 한 번 별명이 되면 절대로 다른 이의 별명이 될 수 없다.

1
2
3
4
    int a = 1, b = 2;
    
    int& c = a;  // c : 1
    c = b;       // c : 2
cs

위에 처럼 c는 한번 a의 별명이 되었다면 4번째 코드를 실행했을 때 b값을 c에 대입하뿐 b의 다른 별명으로 바뀌지 않는다.

 레퍼런스는 메모리 상에 존재?

레퍼런스는 메모리 상에 존재하지 않을 수 도 있다. 컴파일러의 입장에서 c는 a로 바꿔치기하면 되므로 경우에 따라서 메모리 상에 공간에서 존재하지 않는다. 그러면 레퍼런스가 메모리 상에 존재하는 경우는 무엇일까?  레퍼런스가 함수에 전달될 때에는 객체의 주소값이 마치 포인터처럼 전달되는 식입니다. 따라서 객체의 주솟값을 저장하기 위한 공간이 필요하게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
int& Function(int& num) ->객체의 주소값 전달
{
   num = 4;
    return num;
}
 
int main()
{
    int a = 10;
   Function(a); -> 레퍼런스 삽입
    return 0;
}
cs

지역변수의 레퍼런스 리턴?

지역변수의 레퍼런스를 리턴한다는 것은 어떤 의미 일까? 아래의 함수 Function 함수가 호출 후에 종료를 하면 num은 메모리에서 사라지게 된다. 즉 원래 참조하고 있던 변수가 이미 사라져 버렸으므로 오류가 발생한다. 쉽게 말해 본체는 사라지고 별명만 남은 상황이다. 이와 같이 레퍼런스는 있는데 원래 참조하던 것이 사라진 레퍼런스를 댕글링 레퍼런스라고 부른다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
// 레퍼런스
int& Function(int _num)
{
    int num = _num;
 
    return num;
}
 
int main()
{
    int a = Function(5);
    // 경고: 지역 변수 또는 임시:num 의 주소를 반환하고 있습니다.
    return 0;
}
cs

외부 변수의 레퍼런스를 리턴

이번의 Function 역시 레퍼런스를 리턴하고 있다. 하지만 아까와의 차이점은 인자로 받은 레퍼런스를 그대로 리턴한다. 함수는 종료되었지만 Function이 리턴한 참조자는 아직 살아있는 변수인 a를 계속 참조한다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
int& Function(int& num)
{
    num = 4;
    return num;
}
 
int main()
{
    int a = 10;
   int b = Function(a);   
    return 0;
}
cs

그렇다면 이렇게 참조자를 리턴하는 경우의 장점은?  C 언어에서 엄청나게 큰 구조체가 있을 때 해당 구조체 변수를 리턴하면 전체 복사가 발생해야 해서 시간이 오래 걸리지만, 해당 구조체를 가리키는 포인터를 리턴한다면 그냥 포인터 주소 한번 복사로 매우 빠르게 끝난다. 마찬가지로 레퍼런스를 리턴하게 된다면 레퍼런스가 참조하는 타입의 크기와 상관없이 딱 한 번의 주소 값 복사로 전달이 끝나게 된다. -> 항상 강의 들으면서 레러런스의 복사비용에 대해서 설명하셨는데 이러한 이유 때문이었다. 

참조자가 아닌 값을 리턴하는 함수를 참조자로 받기

컴파일 오류가 발생한다. 상수가 아닌 레퍼런스가 함수의 리턴값을 참조할 수 없다. 아까전과 마찬가지로 댕글링 레퍼런가 되버린다. 하지만 int& b 앞에 const 를 붙이면 상수 레퍼런스로 리턴값을 받게 되면 해당 리턴값의 생명이 연장된다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
int Function()
{
    int num = 4;
    return num;
}
int main()
{
    int& b = Function(); // 오류: 비const 참조에 대한 초기 값은 lvalue여야 합니다.
                         // 오류: '초기화 중' 'int'에서 'int &'로 변환할 수 없습니다.
    return 0;
}
 
cs