함수간의 매개변수 전달
함수에 매개변수를 전달하는 방법은 값에 의한 매개변수 전달과 주소에 의한 매개변수 전달이 있다. 또는 값에 의한 호출, 주소에 의한 호출이라고 한다.
값에 의한 호출은 매개 변수 전달 시 매개변수의 값을 복사하여 사용한다. 따라서 원변수의 내용은 바뀌지 않는다. 그러나 주소에 의한 호출은 변수의 주소를 참조하므로 변수의 내용이 바뀌게 된다. 앞에서 우리는 포인트를 배웠다. 이 포인트를 이용하는 방법이 주소에 의한 함수 호출이 되겠다. C++에서 추가된 전달방법이 레퍼런스이다.
레퍼런스도 포인트와 비슷하다. 아니 포인트라고 해도 타당하다.
아래의 예제는 값, 포인트, 레퍼런스에 의한 호출을 비교한 프로그램이다.
예제 | ☞ | 값, 포인트, 레퍼런스에 의한 호출 비교 |
#include
#include
#include
void call_by_value(int x){ // 값에 의한 호출 함수
x=x * 2;
}
void call_by_point(int * x){ // 포인트에 의한 호출 함수
*x=*x * 2;
}
void call_by_reference(int & x){ // 레퍼런스에 의한 호출 함수
x=x * 2; // 자동 역참조된다. 역참조 연산자 '*'이 필요 없다.
}
void main(){
int v=10,p=20,r=30;
clrscr();
call_by_value(v); // 함수 호출 후 v의 값이 바뀌지 않음
cout <<"v= " << v << endl ;
call_by_point(&p); // 함수 호출 후 p의 값이 바뀜
cout <<"p= " << p << endl ;
call_by_reference(r); // 함수 호출 후 r의 값이 바뀜
cout <<"r= " << r << endl ;
getch();
}
위 예제에서 보듯이 레퍼런스는 자동 역참조되는 포인트이다. 포인트를 사용하는 것보다 훨씬 간결하고 역참조 연산자를 사용하지 않으므로 이해하기도 쉽다.
레퍼런스 매개변수를 부주의하게 변경하지 못하도록 하기 위해 받아들인 레퍼런스 매개변수 앞에 const를 둘 수 있다.
먼저, 받는 함수가 호출 함수의 값을 변경하지 못하게 할 때 왜 레퍼런스에 의해 매개변수를 전달하는지 궁금할 것이다. 값에 의해 매개변수를 전달하는 것이 더 수월하고 받는 함수가 보내는 함수의 값을 건드리지 못할 것 같다.
값에 의해 매개변수를 전달하는 것은 프로그램의 몇 가지 비효율성을 만든다. 값에 의해 변수를 전달할 때 특히 커다란 구조체 변수일 경우, Turbo C++는 변수의 지역 복사본을 만들기 위해 상당한 실행시간을 요구한다. (그것은 by value라는 용어를 흔히 by copy라고도 하는 이유이다).
그러나, 주소나 레퍼런스에 의해 변수를 전달할 때 데이터 그 자체보다는 데이터에 대한 포인터가 전달된다.(레퍼런스에 의한 전달 때 포인터는 자동적으로 역참조된다). 그러므로, 효율성이 중요하면, 레퍼런스에 의한 전달이 좋은데, 받는 함수가 매개변수를 변경하지 못하도록 하려면 const를 받는 값 앞에 두는 것이다.
아래의 max( x , y)는 x, y 중 큰 수를 반환하다. 정확히 말하자면 큰 수의 레퍼런스를 반환한다. 즉 값이 아닌 변수를 반환하는 것이다.
int & max(int & x, int & y){
return (x > y)? x: y;
}
따라서 아래의 식이 성립된다.
max(a, b)=0; // a, b중 큰 수의 값이 0이 된다.
물론 z= max(a, b)의 형태도 된다.
아래의 예제는 레퍼런스 반환을 보여주고 있다.
예제 | ☞ | 레퍼런스 반환 |
#include
#include
#include
int & max(int & x, int & y){
return (x > y)? x: y;
}
void main(){
int a=10,b=20,z;
clrscr();
z=max(a, b); // z=20이 된다.
cout << "z= " << z << endl;
max(a, b)=0; // b=0이 된다. a의 값(10)은 변화 없다.
cout << "a= " << a << endl;
cout << "b= " << b << endl;
getch();
}
두 수의 합을 반환화는 sum(x, y)함수를 만들어 보자.
int sum (int x, int y){
return (x+ y);
}
세 수의 합을 반환하는 함수 sum2(x, y, z)를 만들어 보자.
int sum (int x, int y, int z){
return (x+ y+ z);
}
4, 5, 6,...가지 수의 합을 구하는 함수가 필요하면 별 수 없이 모두 만들어야 했다. 하지만 C++는 디폴트 인수를 사용하여 여러분의 타이핑 수고를 많이 들어준다.
int sum (int a, int b, int c = 0, int d = 0, int e = 0){
return (a+ b+ c+ d+ e);
}
매개변수에 값이 주어진 것들이 있다. c, d, e가 디폴트 매개변수(인수)이다.
디폴트 인수는 생략할 수 있다. 생략하면 디폴트 값(c = 0, d = 0, e = 0)이 들어간다.
x=sum(1, 2); // x = 3 : a = 1, b = 2, c = 0, d = 0, e = 0
x=sum(1, 2, 3); // x = 6 : a = 1, b = 2, c = 3, d = 0, e = 0
x=sum(1, 2, 3, 4); // x = 10 : a = 1, b = 2, c = 3, d = 4, e = 0
x=sum(1, 2, 3, 4, 5); // x = 15 : a = 1, b = 2, c = 3, d = 4, e = 5
마술 같은 일이 아닐 수 없다.(C++ 제작팀에게 감사하는 마음을 가지자....^^)
하나의 함수로 위와 같이 여러 가지 경우를 처리할 수가 있다.
매개변수에 디폴트 값을 주는 것을 '매개변수 초기화' 라고 한다.
초기화된 매개변수는 생략이 가능하고 반대로 초기화되지 않은 매개변수는 생략이 불가하다. x=sum(1)은 불가능하다.
또 초기화된 매개변수는 매개변수 리스트에서 맨 뒤로 보내야한다. 아래의 함수는 에러다.
int func(int x, int y = 0, int z){...}
int func(int x, int z, int y = 0){...} // 에러 없음
조금만 생각해보면 당연하다는 것을 알 수 있을 것이다.
cin.getline(city,25), cin.getline(city,25,'#')에서 마지막 매개변수가 디폴트 인수이다. 마지막 인수를 생략하면 cin.getline(city, 25, 'n')이 된다. |
혼자 해보기 | |
평균을 구하는 avg()함수를 만들어 보자. 매개변수는 4개이고 디폴트 매개변수는 2개가 되어야 한다. |
a = 5 * 3 ; // (1)
b = * p; // (2)
연산자 '*'는 (1)에서는 곱셈을 의미하고, (2)에서는 포인트임을 의미한다. 같은 연산자지만 형태에 따라 Turbo C++는 정확히 구별하고 있다. 즉 연산자 '*'가 다중 정의되어 있음을 의미한다. 연산자 다중정의를 '연산자 오버로드'라고 한다.
( 비트연산자 '<<' 와 cout의 '<<'는 오버로드 연산자이다.)
그렇다면 오버로드 함수는 무엇인가?
다중 정의된 함수를 오버로드 함수라 하며, 오버로드 함수를 작성하는 행위를 함수 오버로딩이라고 한다.
int func(int i){ return i; } // (1)
char func(char c){ return c; } // (2)
float func(float f){ return f; } // (3)
함수 func()는 단순히 매개 변수의 값을 반환하고 있다. func()는 다중정의 되어 있음을 볼 수 있다.
a=func(10);
위의 문장에서 (1),(2),(3) 중 어느 것이 호출될까?
생각할 것도 없이 (1)이 호출됨을 알 수 있다.
매개변수의 자료형에 따라 그에 맞는 함수가 호출된다.
"오버로드 함수는 함수의 이름은 같고 매개변수의 개수나 매개변수의 자료형이 달라야한다."
void good(int a){...}
int good(int a, int c){...}
매개변수의 개수가 다르므로 good()는 타당한 오버로드 함수이다.
char bad(int a){...} // (1)
int bad(int a, int b = 1){...} // (2)
위의 경우는 어떤가?
bad(1)을 호출하면 어느 것이 호출될까? (2)에서 매개 변수 b는 생략이 가능하다.
그러므로 어느 함수를 호출할 지 애매하다.
Turbo C++ 는 어느 함수를 호출해야 할지 애매하다는 에러 메시지를 보내고 컴파일을 중단한다. 따라서 위의 문장은 잘못된 것이므로 다음과 같이 수정해야한다.
char bad(int a){...}
int bad(int a, int b){...}
혼자 해보기 | |
같은 이름의 두 함수를 가진 프로그램을 작성해 보자. 첫 번째 함수는 그것에 전달되는 정수형 배열의 평균을 반환하고, 두 번째 함수는 부동-소수형 배열의 평균을 반환한다. |
연산자 '+'를 오버로딩해 보자.
int operator + (const struct buza & x, const struct buza & y){
return x.money + y.money;
}
a = x.money + y.money; // 이렇게 하기보다는 아래의 문장이 코드 량을 줄여준다.
a= x + y; // 또는 a = operator + ( x , y );
예제 | ☞ | '+' 오버로딩 |
#include
#include
#include
struct buza{
int money;
};
int operator + (const struct buza & x, const struct buza & y){
return x.money + y.money;
}
void main(){
buza a, b;
a.money = 10;
b.money = 20;
clrscr();
cout << a + b;
getch();
}