Call-by-value & Call-by-reference
Call-by-value : 값을 인자로 전달하는 함수의 호출방식
Call-by-reference : 주소 값을 인자로 전달하는 함수의 호출방식
#include <iostream>
using namespace std;
void SwapByValue(int num1, int num2)
{
int temp = num1;
num1 = num2;
num2 = temp;
} // Call-by-value
int main(void)
{
int val1 = 10;
int val2 = 20;
SwapByValue(val1, val2); // val1과 val2에 저장된 값이 바뀌기를 기대함
cout << "val1: " << val1 << endl; // 10 출력
cout << "val2: " << val2 << endl; // 20 출력
return 0;
}
==> Call-by-value의 형태로 정의된 함수의 내부에서는 함수외부에 선언된 변수에 접근이 불가능하다
따라서 두 변수에 저장된 값을 서로 바꿔서 저장할 목적으로 다음과 같이 함수를 정의하면 원하는 결과를 얻을 수 없다
#include <iostream>
using namespace std;
void SwapByRef(int* ptr1, int* ptr2)
{
int temp = *ptr1;
*ptr1 = *ptr2;
*ptr2 = temp;
} // Call-by-reference
int main(void)
{
int val1 = 10;
int val2 = 20;
SwapByRef(&val1, &val2); // val1과 val2에 저장된 값이 바뀌기를 기대함
cout << "val1: " << val1 << endl; // 20 출력
cout << "val2: " << val2 << endl; // 10 출력
return 0;
}
==> Call-by-reference의 형태로 그 주소 값이 참조하는 영역에 저장된 값을 직접 변경할 수 있다
Call-by-address? Call-by-reference!
Call-by-reference란
주소 값을 전달받아서, 함수 외부에 선언된 변수에 접근하는 형태의 함수호출
==> 즉 주소값이 외부 변수의 참조도구로 사용되는 함수 호출을 뜻한다
C++에서 함수 외부에 선언된 변수의 접근방법 2가지
- 주소 값을 이용한 Call-by-reference (Call-by-address) ==> 위에서 해봄
- 참조자를 이용한 Call-by-reference
참조자를 이용한 Call-by-reference
#include <iostream>
using namespace std;
void SwapByRef2(int& ref1, int& ref2)
{
int temp = ref1;
ref1 = ref2;
ref2 = temp;
}
int main(void)
{
int val1 = 10;
int val2 = 20;
SwapByRef2(val1, val2);
cout << "val1 : " << val1 << endl;
cout << "val2 : " << val2 << endl;
return 0;
==> 참조자는 선언과 동시에 변수로 초기화 되어야 한다
하지만 매개변수는 함수가 호출되어야 초기화가 진행되는 변수들이다
==> 매개변수로 선언된 참조자 ref1과 ref2는 main 함수에서 선언된 변수 val1과 val2의 또 다른 이름이 된다
그리고 SwapByRef2 함수 내에서는 이 두 참조자를 통해서 값의 교환 과정을 거치기 때문에, val1과 val2의 값의 교환으로 이어진다
참조자를 이용한 Call-by-reference의 황당함과 const 참조자
포인터는 잘못 사용할 확률이 높고, 참조자의 활요은 상대적으로 포인터의 활용보다 쉽기 때문에, 참조자 기반의 함수정의가 더 좋은 선택이라고 생각할 수 있다. 그러나 참조자 기반의 함수정의에 좋은 점만 있는 것은 아니다.
int num = 24;
HappyFunc(num);
cout << num << endl;
void HappyFunc(int prm) { . . . . } // 100% 24가 그대로 출력된다
void HappyFunc(int &pref) { . . . . } //참조자를 이용해서 num에 저장된 값을 변경할 수 있다
==> 참조자에 대한 해결은 const를 사용함으로 해결할 수 있다
void HappyFunc(const int& pref) { . . . . } //const를 사용했다
==> 함수 HappyFunc 내에서 참조자 ref를 이용한 값의 변경은 하지 않겠다
*함수 내에서, 참조자를 통한 값의 변경을 진행하지 않을 경우, 참조자를 const로 선언해서, 함수의 원형만 봐도 값의 변경이 이뤄지지 않음을 알 수 있게 한다
반환형이 참조형 (Reference Type)인 경우
반환형이 참조형인지 확인할려면 매개변수만 볼 것이 아니라 함수 앞에 참조자가 있는지도 확인을 해야 한다
1.
#include <iostream>
using namespace std;
int& RefRetFuncOne(int& ref) //참조자가 모두 있다
{
ref++;
return ref;
}
int main(void)
{
int num1 = 1;
int& num2 = RefRetFuncOne(num1); //참조자가 모두 있다
num1 += 1;
num2 += 100;
cout << "num1: " << num1 << endl;
cout << "num2: " << num2 << endl;
return 0;
}
int num1=1;
int &ref=num1; // 인자의 전달과정에서 일어난 일
int &num2=ref; // 함수의 반환과 반환 값의 저장에서 일어난 일
2.
#include <iostream>
using namespace std;
int& RefRetFuncOne(int& ref)
{
ref++;
return ref;
}
int main(void)
{
int num1 = 1;
int num2 = RefRetFuncOne(num1); // num2 앞의 참조자를 지웠다
num1 += 1;
num2 += 100;
cout << "num1: " << num1 << endl;
cout << "num2: " << num2 << endl;
return 0;
}
int num=1;
int &ref=num1; // 인자의 전달과정에서 일어난 일
int num2=ref; // 함수의 반환과 반환 값의 저장에서 일어난 일
3.
#include <iostream>
using namespace std;
int RefRetFuncTwo(int& ref) // 함수 앞의 참조자를 지웠다
{
ref++;
return ref;
}
int main(void)
{
int num1 = 1;
int num2 = RefRetFuncTwo(num1);
//int &num2 = RefRetFuncTwo(num1);는 오류가 뜬다
//반환형이 기본자료형으로 선언된 RefRetFuncTwo 함수의 반환값은 반드시 변수에 저장해야 하기 때문이다
num1 += 1;
num2 += 100;
cout << "num1: " << num1 << endl;
cout << "num2: " << num2 << endl;
return 0;
}
잘못된 참조의 반환
int& RetuRefFunc(int n)
{
int num = 20;
num += n;
return num;
}
지역변수 num에 저장된 값을 반환하지 않고, num을 참조의 형태로 반환하고 있다
int& ref = RetuRefFunc(10);
함수를 호출하면 지역변수 num에 ref라는 또 하나의 이름이 붙게 되지만 함수의 반환이 끝나면 정작 지역변수 num은 소멸하게 된다 ==> 따라서 지역변수를 참조형으로 반환하는 일은 없어야 한다
*컴파일러에서도 경고메세지만 줄 뿐 에러 메세지가 뜨지 않기 때문에 더욱 주의해야 한다
const 참조자의 또 다른 특징
const int num = 20;
int& ref = num; //여기서 오류가 발생한다
ref += 10;
cout << num << endl;
==> const 선언을 통해서 변수 num을 상수화했으므로, 참조자 ref를 통해서 값을 변경하는 것은 불가능하다
const int num = 20;
const int& ref = num; //변수 num과 같이 상수화된 변수에 대한 참조자 선언은 이렇게 한다
==> 이렇게 선언이 되면 ref를 통한 값의 변경이 불가능하기 때문에 상수화에 대한 논리적인 문제점은 발생하지 않는다.
const int& ref = 50;
==> 참조자는 변수만 참조가 가능했는데... 어떻게 된 일인지 알아보자!
어떻게 참조자가 상수를 참조하냐고요!
int num=20+30;
==> 여기서 20, 그리고 30과 같은 프로그램상에서 표현되는 숫자를 가리켜 '리터럴(literal)' 또는 '리터럴 상수(literal constant)'라 하고 임시적으로 존재하는 값이며, 다음 행으로 넘어가면 존재하지 않는 상수를 의미한다
const int &ref=30;
- 이는 숫자 30이 메모리 공간에 계속 남아있을 때에나 성립이 가능한 문장이다
- 그러나 C++에서는 위의 문장이 성립할 수 있도록, const 참조자를 이용해서 상수를 참조할 때 '임시변수'라는 것을 만든다
- 그리고 이 장소에 상수 30을 저장하고선 참조자가 이를 참조하게끔 한다
==> 임시로 생성한 변수를 상수화하여 이를 참조자가 참조하게끔 하는 구조이니, 결과적으로 상수화된 변수를 참조하는 형태가 된다
왜 임시변수라는 개념까지 사용해서 상수의 참조를 해야되냐고 묻는다면 아래 코드를 보면 답이 나온다
int Adder(const int& num1, const int& num2)
[
return num1 + num2;
]
cout << Adder(3, 4) << endl;
위와 같이 정의된 함수에 인자의 전달을 목적으로
*변수를 선언한다는 것은 번거롭지만 임시변수의 생성을 통한 const 참조자의 상수참조를 허용함으로써 간단히 호출이 가능해졌다
const int& num1 = 3;
const int& num2 = 4;
==> 위 코드를 정리해서 표현하면 이런 뜻이 된다
'C++ 이야기 (열혈) > C언어 기반의 C++ 2' 카테고리의 다른 글
6. C++에서 C언어의 표준함수 호출하기 (0) | 2023.01.17 |
---|---|
5. malloc & free를 대신하는 new & delete (0) | 2023.01.17 |
3. 참조자 &(Reference)의 이해 (0) | 2023.01.17 |
2. 새로운 자료형 bool (0) | 2023.01.17 |
1. 시작에 앞서 (0) | 2023.01.17 |