본문 바로가기
Programming/C,C++

[C++] #5 참조자(Reference)와 함수

by yoiii 2021. 1. 2.

#5 참조자와 함수


<Before Start> 

omyodevelop.tistory.com/64

참조자의 기본 개념에 대해 알고있다면, 보지 않아도 됩니다.


목차

#1 참조자와 함수

#2 참조자를 이용한 함수와, const 키워드

#3 반환형이 참조형인 함수


#1 참조자와 함수

C언어에서, 함수의 호출방식은 Call-by-value , Call-by-reference 2가지로 나뉜다는 것을 공부했을 것이다.

Call-by-value 로 선언된 함수의 내부에서, 함수외부에 선언된 변수의 접근이 불가능하기에, 선언한 함수 블럭 안에서, 외부의 변수에 접근하고자 한다면, Call-by-reference 형태로 함수를 선언 해야만 한다.

 

그런데, C++에서는 Call-by-reference 함수 선언 방식이 2가지가 존재한다.

1. 주소 값을 이용한 Call-by-reference (기존 C 방식)

2. 참조자를 이용한 Call-by-reference

 

1번 방식은 알고 있을테니, 참조자를 이용한 Call-by-reference 방식에 대해 알아보자.

 

void Swap(int &ref1, int &ref2){
	int temp = ref1;
	ref1 = ref2;
	ref2 = temp;
}

 

참조자를 이용한 Call-by-reference 방식의 예시이다.

언뜻보기에, 참조자는 선언 동시에 누군가를 참조해야 한다는 참조자 선언 2번 규칙에 어긋나는 것처럼 보일 수 있지만,

'매개변수''함수가 호출되어야 초기화가 진행되는 변수' 이기에, 규칙에서 벗어나지 않는다. 

 

이제 main 함수부에서, Swap 함수를 호출해보자.

int main()
{
	int val1 = 10;
	int val2 = 20;
	Swap(val1,val2);
	cout << "val1 is " << val1 << endl;
	cout << "val2 is " << val2 << endl;
	return 0;
} 

Swap 함수는 매개변수가 참조자 형태로 선언 되었기에, 변수 val1과 val2에 각각 ref1과 ref2라는 별칭이 붙게되고,

참조자와 변수는 동일한 메모리 공간을 가리키기에, 실제로 val1과 val2의 값이 변경되게 된다.


#2 참조자를 이용한 함수와 const 키워드

참조자를 이용한 함수에는, 코드 분석의 관점에서 단점이 존재한다.

 

int val1 = 2021;
myFunc(val1);
cout << val1 << endl;

 

C언어의 관점에서 본다면, 다음 호출문에서, 2021이 출력되는 것이 당연한다.

하지만 C++은 얼마가 출력될지 바로 판단이 불가능하다.

 

void myFunc(int val1){...}

 

다음과 같이, 일반 매개변수라면, 2021이 출력되겠지만,

 

void myFunc(int &ref1){...}

 

매개변수가 참조자 형태로 선언되어 있다면, 값이 바뀔 수 도 있기 때문이다.

 

이렇듯, 코드를 분석하는 과정에 있어서, 함수의 호출문을 보고 , 함수의 특성을 판단이 불가능 하다는 것은, 큰 단점이다.

하지만, const 키워드를 함께 사용한다면, 단점을 어느정도는 보완이 가능하다.

 

void myFunc(const int &ref1){...}

 

참조자 선언 앞에 const를 붙여주면, 참조자를 이용한 값 변경은 하지 않겠다는 의미를 가진다.

따라서, 함수에서 참조자를 통한 값의 변경을 하지 않는다면, 참조자와 const를 함께 사용해 주는것이 C++에서의 관례이다.


#3 반환형이 참조형인 함수

함수의 반환형참조형이 될 수 있다.

int& RefReturnFunc1(int &ref1){
	return ref1;
}

 

반환형이 참조형인 함수들은 반환값을 어떤 형태로 저장하느냐에 따라서, 결과가 달라진다.

다음 두가지 예시를 통해 확인해보자.

 

case 1 : 참조자로 참조형 반환값을 받는경우

int& RefFunc1(int &ref1){
	ref1 ++;
	return ref1;
}

int main(){
	int val1 = 1;
	int &val2 = RefFunc1(val1);
	val1++;
	val2++;
	cout<<"val1 is "<<val1<<endl;
	cout<<"val2 is "<<val2<<endl;
	return 0;
}

main 함수부에서 참조형 반환값을 참조자로 받는 경우이다. 

처음으로, int형으로 val1 변수를 1로 초기화 하였기에, val1 메모리 공간이 생긴다. int val1 = 1;

다음으로, 함수의 인자 전달 과정에서, val1 메모리 공간에 ref1 이라는 별칭이 붙는다. int &ref1 = val1;

마지막으로, 참조형 반환값을 참조자에 받았기에, val2 라는 별칭이 추가로 생긴다.  int &val2 = ref1;

 

단, 매개변수로 선언된 참조자 ref1은 지역변수와 동일한 성격을 갖기에, 반환시 참조자 ref1은 소멸된다는 것을 기억해두자.

 

case 2 :  변수로 참조형 반환값을 받는경우

#include <iostream>
using namespace std;

int& RefFunc1(int &ref1){
	ref1 ++;
	return ref1;
}

int main(){
	int val1 = 1;
	int val2 = RefFunc1(val1);
	val1++;
	val2+=100;
	cout<<"val1 is "<<val1<<endl;
	cout<<"val2 is "<<val2<<endl;
	return 0;
}

main 함수부에서 참조형 반환값을, 변수로 받는 경우이다.

case1과 크게 다른 부분은 없지만, 리턴값을 변수 val2가 받기에, val2 별도의 메모리 공간이 생성되었다.

이렇듯, 환값을 어떤 형태로 받느냐에 따라, 결과가 달라진다.

 

단, 반환형이 기본자료형으로 선언된 함수의 반환값은 반드시 변수로 받아야만 한다.

왜냐하면, 반환 값은 상수나 다름없기에, 참조자 형태로 받는다면

앞선 포스팅에서 소개 한 참조자 선언 규칙 중 첫 번째 규칙인 "참조자는 변수에 대해서만 참조가 가능하다" 를 어긋나게 되기 때문이다.


요약

#1 참조자와 함수

C++ 은 Call-by-reference 방식주소값을 이용한 방식과, 참조자를 이용한 방식으로 나뉜다.

#2 참조자를 이용한 함수와 const 키워드

함수에서 참조자를 통한 값의 변경을 하지 않는다면, 참조자와 const를 함께 사용해 주는것이 C++에서의 관례이다.

#3 반환형이 참조형인 함수

case 1 : int& RefReturn1(int &ref1){...} 반환형이 참조형 - 참조자,변수 둘다 받을 수 있음.

case 2 : int RefReturn2(int &ref2){ return ref2 반환형이 기본자료형 - 변수로만 받아야함


 

댓글