10. [C++] 참조자(Reference)와 함수2
2024. 4. 11. 16:01ㆍ[C++]/C++ 언어 기초
🍃 참조자를 이용한 Call-by-reference의 황당함과 const 참조자
- 포인터는 잘못 사용할 확률이 높고, 참조자의 활용이 상대적으로 쉽기 때문에 참조자 기반의 함수정의가 더 좋은 선택이라고 생각할 수 있다.
- 하지만 참조자 기반의 함수정의에 좋은 점만 있는 것은 아니다.
int num = 24;
HappyFunc(num);
cout<<num<<endl;
- C언어의 관점에서는 100% 24가 출력된다.
- 그러나 C++에서는 얼마가 출력될 지 알 수 없다.
함수가 다음과 같이 정의되어 있다면 24가 출력되겠지만,
void HappyFunc(int prm) { . . . . }
다음과 같이 정의되어 있다면, 참조자를 이용해 num에 저장된 값을 변경할 수도 있다.
void HappyFunc(int &ref) { . . . . }
- 이는 참조자의 단점이다.
- 함수의 호출 문장만 보고도 함수의 특성을 어느 정도 판단할 수 있어야 한다.
- 하지만 참조자를 사용하는 경우, 함수의 원형을 확인해야하고, 확인 후 참조자가 매개변수 선언에 와있다면 함수의 몸체까지 문장 단위로 확인을 해서 참조자를 통한 값의 변경이 일어나는지 확인해야 한다.
이러한 문제점의 해결방안이 무엇일까? 완벽한 해결방안이 되질 못하겠지만, 어느정도 극복할 수 있는 방안이 있다.
const 키워드의 사용이다.
void HappyFunc(const int &ref) { . . . . }
- 참조자 ref에 const 선언이 추가되었다. 이는 다음의 의미를 지닌다.
- "함수 HappyFunc 내에서 참조자 ref를 이용한 값의 변경은 하지 않겠다!"
- 여기 const 선언으로 인해, 참조자 ref에 값을 저장하는 경우 컴파일 에러가 발생한다.
- 따라서 함수 내에서 값의 변경이 이뤄지지 않음을 확신할 수 있다.
"함수 내에서, 참조자를 통한 값의 변경을 진행하지 않는 경우, 참조자를 const로 선언해서, 함수의 원형만 봐도 변경이 이뤄지지 않음을 알 수 있게 한다."
🍃 반환형이 참조형(Reference Type)인 경우
- 함수의 반환형에도 참조형이 선언될 수 있다.
int& RefRetFuncOne(int &ref){
ref++;
return ref;
}
- 위 함수에서는 매개변수로 참조자가 선언되었는데, 이 참조자를 그대로 반환하고 있다.
- 매개변수가 참조자인데, 이를 반환하니 반환형이 참조형인 거구나(이는 잘못된 판단이다.)
int RefRetFuncTwo(int &ref){
ref++;
return ref;
}
- 다음과 같이 참조자를 반환해도 반환형은 참조형이 아닐 수 있다.
RefReturnOne.cpp
#include <iostream>
using namespace std;
int& RefRetFunOne(int &ref) {
ref++;
return ref;
}
int main(void) {
int num1 = 1;
int &num2 = RefRetFunOne(num1);
num1++;
num2++;
cout <<"num1: "<<num1<<endl;
cout <<"num2: "<<num2<<endl;
return 0;
}
num1: 4
num2: 4
- 참조형으로 반환된 값을 참조자에 저장하면, 참조의 관계가 하나 더 추가된다.
- ref에 저장된 값 4가 아닌, ref라는 참조의 데이터가 반환된다.
- 따라서 num2는 num1에 대한 참조의 데이터를 전달받는 것이다.
아래와 같이 변경해보자
int &num2 = RefRetFunOne(num1);
int num2 = RefRetFunOne(num1);
RefReturnTwo.cpp
#include <iostream>
using namespace std;
int& RefRetFunOne(int &ref) {
ref++;
return ref;
}
int main(void) {
int num1 = 1;
int num2 = RefRetFunOne(num1);
num1+=1;
num2+=100;
cout <<"num1: "<<num1<<endl;
cout <<"num2: "<<num2<<endl;
return 0;
}
num1: 3
num2: 102
- 다음과 같이 변경함으로써, num1과 num2는 완전히 별개의 변수가 된다.
- 참조형(&num2)가 아니라 변수(num2)이어서 참조데이터가 아닌 '값'을 전달 받기 때문이다.
참조자를 반환하되, 반환형은 기본자료형인 경우
RefReturnThree.cpp
#include <iostream>
using namespace std;
int RefRetFunTwo(int &ref) { //반환형이 기본자료형 int이다!
ref++;
return ref;
}
int main(void) {
int num1 = 1;
int num2 = RefRetFunTwo(num1);
num1+=1;
num2+=100;
cout <<"num1: "<<num1<<endl;
cout <<"num2: "<<num2<<endl;
return 0;
}
num1: 3
num2: 102
return ref;
- 이 행을 살펴보면, 참조자를 반환하지만, 반환형이 기본자료형 int이기 때문에 참조자가 참조하는 변수의 값이 반환된다. 다시 한번 말하지만, 변수에 저장된 값이 반환된다.
- RefReturnTwo.cpp와 실행결과에 차이가 없으나 반환형이 기본자료형으로 선언된 RefReturnTwo 함수의 반환 값은 반드시 변수에 저장해야한다. 반환 값은 상수나 다름없기 때문이다.
- int num2 = RefRetFunTwo(num1); (o)
- int &num2 = RefRetFunTwo(num1); (x)
🍃 잘못된 참조의 반환
int & RetuRefFunc(int n) {
int num = 20;
num +=n;
return num;
}
- 위 함수에서는 지역변수 num에 저장된 값을 반환하지 않고, num을 참조의 형태로 반환하고 있다.
- 따라서 다음의 형태로 함수를 호출하고 나면, 지역변수 num에 ref라는 또 하나의 이름이 붙게 된다.
int &ref = RetuRefFunc(10);
- 하지만 이게 끝이 아니다. 함수가 반환이 되면, 정작 지역변수 num은 소멸 된다.
- 따라서 위 처럼 지역변수를 참조형으로 반환하는 일은 없어야 한다.
🍃 const 참조자의 또 다른 특징
const int num = 20;
int &ref = num;
ref+=10;
cout<<num<<endl;
- 여기서 에러의 원인은 다음 코드이다.
int &ref = num;
- 이를 허용한다는 것은 ref를 통한 값의 변경을 허용한다는 뜻이 되고, 이는 num을 const로 선언하는 이유를 잃게 만든다.
- 따라서 다음과 같이 참조자 선언을 해야 한다.
const int num = 20;
const int &ref = num;
- 이렇게 선언이 되면 ref를 통한 값의 변경이 불가능하기 때문에 상수화에 대한 논리적인 문제점은 발생하지 않는다.
- 그리고 const 참조자는 다음과 같이 상수도 참조가 가능하다.
const int &ref = 50;
🍃 어떻게 참조자가 상수를 참조하냐고요!
int num = 20+30;
- 여기서 20, 30은 '리터럴 상수(literal constant)'라 하고 다음의 특징을 지닌다.
- "임시적으로 존재하는 값이다. 다음 행으로 넘어가면 존재하지 않는 상수다."
- 그런데, 이러한 상수를 참조한다는 것이 이치에 맞는가?
const int &ref = 30;
- 이는 숫자 30이 메모리 공간에 계속 남아있을 때에나 성립이 가능하다.
- 그래서 C++에서는 위의 문장이 성립할 수 있도록, const 참조자를 이용해 상수를 참조할 때 '임시변수'라는 것을 만든다.
- 그리고 이 장소에 상수 30을 저장하고선 참조자가 이를 참조하게끔 한다.
- 사진과 같이 정의된 함수에 인자의 전달을 목적으로 변수를 선언한다는 것은 매우 번거로운 일이다.
- 그러나 임시변수의 생성을 통한 const 상수참조를 허용함으로써, 위의 함수는 다음과 같이 매우 간단히 호출이 가능해졌다.
cout<<Adder(3, 4)<<endl;
반응형
'[C++] > C++ 언어 기초' 카테고리의 다른 글
12. [C++] C++에서 C언어의 표준함수 호출하기 (0) | 2024.04.11 |
---|---|
11. [C++] malloc & free를 대신하는 new & delete (0) | 2024.04.11 |
9. [C++] 참조자(Reference)와 함수 (0) | 2024.04.11 |
8. [C++] 참조자(Reference)의 이해 (0) | 2024.04.11 |
7. [C++] 새로운 자료형 bool (0) | 2024.04.11 |