8. [C++] 참조자(Reference)의 이해

2024. 4. 11. 15:11[C++]/C++ 언어 기초

  • "참조자"라는 것은 성격상 "포인터"와 비유되기 쉽다.

🍃 참조자의 이해

변수는 할당된 메모리 공간에 붙여진 이름이다. 그 이름을 통해서 메모리 공간에 접근이 가능하다.
그러면 할당된 하나의 메모리 공간에 둘 이상의 이름을 부여할 순 없을까?
int num1 = 2010;
  • 위 변수 선언으로 인해 2010으로 초기화된 메모리 공간에 num1이라는 이름이 붙게 된다.
int &num2 = num1;
  •  이 코드를 실행하면, num1이라는 이름이 붙어있는 메모리 공간에 num2라는 이름이 하나 더 붙게된다.
  • &연산자는 변수의 주소 값을 반환하는 연산자이기 때문에 헷갈릴 수 있다. 
  • 이미 선언된 변수 앞에 & 연산자가 오면 주소 값의 반환을 명령하는 뜻이 되지만, 새로 선언되는 변수의 이름 앞에 등장하면, 이는 참조자의 선언을 뜻한다.
int *ptr = &num1; //변수 num1의 주소 값을 반환해서 포인터 ptr에 저장해라
int &num2 = num1; //변수 num1에 대한 참조자 num2를 선언해라
num2 = 3047;
  • 변수 num1의 메모리 공간에 3047이 저장된다.
  • 이렇듯 참조자는 자신이 참조하는 변수를 대신할 수 있는 또 하나의 이름인 것이다.

 

Referen.cpp

#include <iostream>
using namespace std;

int main(void) {
    int num1=1020;
    int &num2 = num1;

    num2 = 3047;
    cout<<"VAL: "<<num1<<endl;
    cout<<"REF: "<<num2<<endl;

    cout<<"VAL: "<<&num1<<endl;
    cout<<"REF: "<<&num2<<endl;
    return 0;
}
VAL: 3047
REF: 3047
VAL: 0x16d1bb458
REF: 0x16d1bb458
  • 변수와 참조자는 선언의 방식에 큰 차이를 보인다.
  • 참조자는 변수를 대상으로만 선언이 가능하다.
  • 하지만 일단 선언이 되고나면 변수와 차이가 없다.

 

 

🍃 참조자는 별칭입니다.

C++에서는 참조자를 다음과 같이 설명한다.

"변수에 별명(별칭)을 하나 붙여주는 것입니다."

 

즉, 다음의 선언에서, num1이 변수의 이름이라면, num2는 num1의 별명이라는 것이다.

int &num2 = num1;
  • 예를 들어 "윤성우"라는 사람의 별명이 흑곰이라면 다음 두 문장이 의미하는 바와 이 문장대로 처리했을 때의 결과는 모두 동일하다.
1. 윤성우한테 전화를 걸어서 이리로 오라고 해!
2. 흑곰에게 전화를 걸어서 이리로 오라고 해!

 

 

 

🍃 참조자의 수에는 제한이 없으며, 참조자를 대상으로도 참조자 선언이 가능하다.

참조자의 수에는 제한이 없다. 즉, 여러 개의 참조자를 선언하는 것도 가능하다.

int num1 = 2759;
int &num2 = num1;
int &num3 = num1;
int &num4 = num1;
  • 위의 문장들을 순서대로 실행하면 아래의 그림과 같이 된다.

 

 

 

참조자를 대상으로 참조자를 선언하는 것도 가능하다.

int num1 = 2759;
int &num2 = num1;
int &num3 = num2;
int &num4 = num3;

이를 실행해도 위의 그림과 같이 된다.

 

 

 

🍃 참조자의 선언 가능 범위

1. 참조자는 변수에 대해서만 선언이 가능하고, 선언됨과 동시에 누군가를 참조해야만 한다.

int &ref = 20;                    (x)
  • 위는 불가능하다. 변수에 대해 선언한 것이 아니기 때문이다.

 

2. 상수를 대상으로 참조자를 선언할 수는 없다. 또한 미리 참조자를 선언했다가, 후에 누군가를 참조하는 것은 불가능하다. 대상을 바꾸는 것 역시 불가능하다.

int &ref;                       (x)

 

 

3. 다음과 같이 참조자를 선언하면서 NULL로 초기화하는 것도 불가능하다.

int &ref = NULL;             (x)
  • 참조자는 무조건 선언과 동시에 변수를 참조하도록 해야한다.
  • 그런데 여기서 말하는 변수의 범위에는 배열요소도 포함된다.

RefArrElem.cpp

#include <iostream>
using namespace std;

int main(void) {
    int arr[3] = {1, 3, 5};
    int &ref1 = arr[0];
    int &ref2 = arr[1];
    int &ref3 = arr[2];

    cout<<ref1<<endl;
    cout<<ref2<<endl;
    cout<<ref3<<endl;
    return 0;
}
1
3
5
  • 예제와 실행결과에서 보이듯이 배열의 요소는 변수로 간주되어 참조자의 선언이 가능하다.
  • 포인터 변수도 변수이기에 참조자의 선언이 가능하다. 

 

RefPtr.cpp

#include <iostream>
using namespace std;

int main(void) {
    int num=12;
    int *ptr=&num;
    int **dptr=&ptr;

    int &ref=num;
    int *(&pref)=ptr;
    int **(&dpref)=dptr;

    cout<<ref<<endl;
    cout<<*pref<<endl;
    cout<<**dpref<<endl;
    return 0;
}
12
12
12
 

 

반응형