참조자(Reference)
-
참조자
참조자란 할당된 메모리 공간에 둘 이상의 이름을 부여하는 것으로 다시 말하면 참조자는 자신이 참조하는 변수를 대신할 수 있는 또 하나의 이름
다음은 참조자를 사용하는 예제이다.
10번 라인에서 & 연산자를 이용하여 참조자(레퍼런스)를 선언하고, 변수 num에 대해서 참조를 진행한다. 참조자 선언과 참조를 마친 후 13번 라인에서 일반 변수 num과 레퍼런스 ref의 주소를 출력해보았을 때 완전히 같은 주소를 출력하는 것을 확인할 수 있는 것으로 미루어 보아, 기존의 대상이 되는 변수에 이름만 부여되는 것임을 확인할 수 있다. 즉, 참조자는 이름을 하나 더 부여하는 별칭인 셈이다.
그리고 하나의 변수에 대해 여러 참조자를 선언하고 참조할 수 있으며, 참조자를 대상으로 참조자를 선언할 수 있다.
9번 라인부터 13번 라인까지 대상 변수에 대한 참조자의 수, 참조자를 대상으로 하는 거듭된 참조까지 가능함을 알 수 있다. 그리고 19번 라인에서 대상 변수 그리고 참조자들의 주소를 출력했을 때 모두 대상 변수의 주소로 같게 나왔음을 확인할 수 있다.
참조자의 잘못된 선언 형태에 대한 간단한 예제이다.
먼저, 10번 라인에서는 참조자를 선언만 하고, 참조를 하지 않은 상태인데 참조자는 선언과 동시에 어떤 대상을 꼭 참조해야만 한다. 그리고 11번 라인에서는 선언과 동시에 참조를 진행했지만 참조의 대상은 참조자의 타입이 맞는 변수가 대상이어야 한다. 마지막 12번 라인에서는 NULL로 선언과 동시에 참조를 하고 있지만 역시 마찬가지로 변수가 대상이 아니기 때문에 참조가 불가능하다. 이와 더불어 한 번 참조가 된 이후에는 참조의 대상 변경이 불가능하다.
참조의 가능 범위는 다음과 같이 크게 3가지가 존재한다.
일반 변수
배열 요소
포인터 변수
-
참조자와 함수
Call-By-Address와 Call-By-Reference 모두 주소 값을 인자로 전달하는 형태의 함수 호출은 뜻하는데, 그 사실 보다는 주소 값을 전달하되, 해당 주소를 이용하여 참조 혹은 값의 변경이 일어났는가를 볼 수 있어야 함. (결국, call-by-address와 call-by-reference 방법은 서로 비슷한 방법이라고 보일 수 있지만 전자는 단지 주소만을 전달하느냐 후자는 주소를 전달하고 참조를 행하느냐를 보는 것인데 전자의 경우는 주소만 전달될 뿐 참조가 일어나지 않기 때문에 call-by-value로 볼 수도 있음 그렇기 때문에 두 용어는 구분이 필요함을 이야기함.)
다음은 참조자를 이용한 swap 함수의 구현이다.
-
Const 참조자
참조자를 이용하여 call-by-reference를 구현할 때, 매개 변수에 참조자를 const화 시켜서 넣게 되면 해당 참조자 매개 변수를 이용하여 값의 변경을 허용하지 않겠다 라는 의미를 갖는다.
이러한 의미가 왜 필요한가?
위와 같은 예제를 마주쳤을 때, C언어에서는 num의 값이 바뀌지 않는 call-by-value 방식의 호출이라는 것을 위 예제만 보고도 알 수 있으나, C++언어에서는 참조자의 개념 때문에 call-by-reference인지 call-by-value인지 알 수가 없다. 이를 확인하기 위해서는 함수의 원형을 확인해야 하고 뿐만 아니라 정의까지 확인해야한다. (왜냐하면 참조자로 매개 변수를 선언했을지라도 참조 후 값의 변경이 있는지 없는지는 정의를 확인해야하기 때문이다.) 이러한 불편함을 해소하기 위해서 함수의 원형 선언만 보더라도 const 키워드로 인해 값의 변경이 일어나는지 일어나지 않는지를 확인할 수 있게 해준다. 다음과 같이.
그리고 const 참조자에는 이러한 경우도 있는데,
16번 라인에서 const를 이용한 변수의 상수화로 심볼릭 상수를 선언하였고, 17번 라인에서 ref 참조자로 심볼릭 상수와 참조 관계를 형성한다. 그리고 18번 라인에서 참조자를 통한 값의 변경을 행한다. 기껏 16번 라인에서 상수로 만들었는데 참조자를 통한 값을 변경을 하고 있다. 그러나 이는 허용하지 않는다. 17번 라인 참조 관계를 형성하는 부분에서부터 허용을 하지 않는다 왜냐하면?
C언어 정리에서 언급했듯이 대입 연산자를 기준으로 l-value 와 r-value의 자료형은 완벽하게 같아야 대입이 이루어지는데 현재 17번 라인에서는 l-value의 타입은 int& 이고, r-value의 타입은 const int 이다. 이는 다른 타입이기 때문에 대입에서 오류가 나는 것임.
그래서 위와 같이 맞춰 주어야만 오류가 나지 않는다.
또 const 참조자는 상수를 참조할 수 있는 특징을 가지고 있다. 일반 참조자와 다르게 const 참조자를 이용해서상수와 참조 관계를 형성하려고 하면 상수가 해당 라인이 넘어가면 소멸되는 리터럴 상수가 아닌 임시 변수 형태로 생성되어 그 값을 가지고 있는 형태로 생성된다. 그렇기 때문에 그 임시 변수 공간과 참조 관계를 형성할 수 있는 것이다.
15번 라인에서 50이라는 임시 변수를 생성하고 그 임시 변수의 공간에 ref 이름을 부여한다. 7번 라인과 16번 라인 또한 같은 맥락이다.
-
함수의 출력 타입이 참조형인 경우
위와 같은 함수 정의에서 14번 라인의 경우에는 arg 변수에 대한 새로운 참조 관계가 형성되는 경우이고, 15번 라인에서는 200이라고 하는 값이 반환된 것이고 v에는 값이 저장되는 경우이다.
위와 같은 경우에는 반환 값을 참조가 아닌 일반 자료형으로 지정한 경우인데 이 경우 반환되는 값은 단순히 값만 반환되기 때문에 일반 변수로 값을 리턴 값을 받을 수 있지만, 25번 라인처럼 참조 관계를 형성할 수는 없다 왜냐하면 int& r = 200; 형태와 같은 형태이기 때문이다.
위와 같이 함수 내의 지역 특성을 지니는 지역 변수에 대해서 참조 관계를 형성시켜서는 안된다(왼쪽). 이는 포인터로 지역 변수에 대해서 가리키고 참조하는 것과 다를 것이 없다.(오른쪽).
함수가 종료되면 사라지는 지역적 특성을 지니는 지역 변수를 함수의 외부(함수 종료 상태의 지역)에서 참조한다는 것은 이미 해체되어 없는 공간 혹은 다른 쓰레드 작업에 의해 이미 다른 중요 값으로 채워져 있는 공간을 참조하고 조작할 수 있는 가능성이 있기 때문에 잘못된 것.
'컴퓨터 언어 정리 > C++ 언어' 카테고리의 다른 글
09 C언어의 표준 함수 호출 (0) | 2020.09.10 |
---|---|
08 Malloc과 free의 대체 – new와 delete (0) | 2020.09.10 |
06 이름 공간(namespace) (0) | 2020.09.08 |
05 인라인 함수(inline function) (0) | 2020.09.07 |
04 디폴트 매개변수(Default Parameter) (0) | 2020.09.07 |