Programming/C,C++

함수중복(fuction overloading)

충남대이적 2022. 2. 19. 21:49

함수중복이란?

C언어에서는 없는 기능으로 C++에서만 지원하는 기능입니다. 말 그대로 함수의 이름을 중복시켜도 상관없다는 것인데요. 그렇게 할 수 있는 이유는 함수를 호출할 때 매개변수의 개수 그리고 종류로 함수의 종류를 구분할 수 있기 때문입니다.

 

#include<iostream>
using namespace std;

int add(int a, int b) {
	cout << "int add(int a, int b)가 실행되었습니다."<<endl;
	return (a + b);
} //1

int add(int a, int b, int c) {
	cout << "int add(int a, int b, int c)가 실행되었습니다." << endl;
	return (a + b + c);
} //2

double add(double a, double b) {
	cout << "double add(double a, double b)가 실행되었습니다." << endl;
	return a + b;
} //3

int main() {
	cout << add(3, 5) << endl;
	cout << add(3, 5, 8) << endl;
	cout << add(3., 5.) << endl;
}

위의 코드에서 함수 3개의 이름은 모두 같지만 매개변수, 매개변수의 타입을 통해 구분하여 접근할 수 있습니다.

1번과 3번함수는 매개변수의 타입을 통해, 1번과 2번함수는 매개변수의 개수를 통해 구분되어집니다.

 

이를 통해 굳이 add1,add2,add3과 같이 여러 개의 덧셈함수를 만들 필요없이 하나의 이름으로 만들 수 있어 보기 좋은 코드를 만들 수 있습니다.

 

 


기본매개변수(Default parameter)

 기본매개변수란 함수 호출 시에 따로 값을 주지 않아도되는 매개변수를 말합니다. 기본매개변수는 여러가지 이점이 있습니다. 예를 들어, 커피전문점에서 커피를 시키면 커피의 종류는 정할 수 있지만, 커피원두, 컵은 커피와 함께 지정되어져 나옵니다. 물론 좋은 곳은 원두도 고를 수 있고, 컵도 고를 수 있죠. 여기서 커피의 종류는 값을 줘야하는 매개변수, 커피원두, 컵은 기본매개변수입니다. 커피의 종류만을 고르면 되므로 생각을 덜할 수 있다는 장점이 있죠.

 

기본매개변수를 사용하는 예제를 보시겠습니다.

#include<iostream>
#include<string>
using namespace std;

void Coffee(string coffee, string coffee_bean = "케냐산", string cup = "플라스틱컵") {
	cout << "주문하신 " << coffee_bean<<" " << coffee<<" " << cup << "에 나왔습니다.";
}

int main() {
	string coffee, coffee_bean;
	string cup;
	cout << "커피를 주문하시겠습니까?" << endl;
	cout << " -------------------" << endl;
	cout << "1. 아메리카노 2. 콜드브루 3. 카페라떼 :";
	cin >> coffee;
	Coffee(coffee);
}

Coffee예제를 보시면 원두와 컵은 정해져서 출력됩니다.

 

기본매개변수를 사용할 때는 한 가지 주의해야할 점이 있습니다.

기본매개변수는 모두 제일 오른쪽으로 몰아서 사용하여야 합니다.

void Coffee(string coffee_bean = "케냐산",string coffee, string cup = "플라스틱컵")

이런 식으로 중간에 기본매개변수가 아닌 녀석이 있어서는 안되고 모두 오른쪽으로 몰아서 사용해야합니다.

 


중복된 함수의 간소화

왜 함수중복 이야기를 하다가 기본매개변수를 설명하는 것일까요? 

그 이유는 함수중복된 것을 기본매개변수를 사용하면 간소화 할 수 있기 때문입니다.

 

예시를 봅시다.

class Circle {
public:
	Circle();
	Circle(int radius);
private:
	int radius;
};

Circle::Circle() {
	radius = 1;
}

생성자를 매개변수의 개수에 따라 여러 개 만드는 것은 대표적인 함수중복의 예입니다.

위 코드에서 Circle 클래스의 객체를 생성할 때 변수가 없으면 Circle()이 호출되고, 변수가 한 개 있으면 Circle(int radius)가 호출 됩니다. 기본매개변수를 사용한다면 이렇게 간소화할 수 있습니다.

 

class Circle {
public:
	Circle(int radius = 1);
private:
	int radius;
};

두 개의 생성자를 하나로 합쳤습니다.

Circle a 라고 호출된다면 매개변수가 없기 때문에 기본매개변수 radius = 1 로 설정되어 객체가 생성될 것이고, Circle a(5) 로 주어진다면 radius = 5 가 될것입니다.

 


함수중복의 모호성

함수중복은 이름으로 명확히 구분하는 것이 아닌 매개변수의 개수, 종류로 구분하기 때문에 실행할 때 어떤 함수를 호출해야하는 지 모호한 상황이 생길 가능성이 있습니다. 크게 3가지 모호한 경우가 있습니다.

1. 형변환으로 인한 모호성

2. 참조 매개 변수로 인한 모호성

3. 디폴트 매개 변수로 인한 모호성

 

1. 형변환으로 인한 모호성

#include<iostream>
using namespace std;

double sqaure(double a) {
	cout << "double 함수 호출" << endl;
	return a * a;
}

float square(float a) {
	cout << "float 함수 호출" << endl;
	return a * a;
}

int main() {
	cout << square(3.0) << endl;
	cout << square(3);
}

3.0은 double형 변수인가요? float형 변수인가요? 정답은 둘 다 입니다.

하지만 컴파일러는 임의로 3.0과 3을 float형 변수로 보고 실행합니다. 지금은 간단한 코드라 문제가 없는 것 같지만 다른 함수에 들어가는 것은 논리오류로 찾기가 힘든 상황이 올 수 있습니다.

 

2. 참조 매개변수로 인한 모호성

#include<iostream>
using namespace std;

int add(int a, int b) {
	return a + b;
} // 1

int add(int a, int& b) {
	b = b + a;
	return b;
} // 2

int main() {
	int s = 10, t = 20;
	cout << add(s, t);
}

 

실행을 할려고 하면 오류가 떠서 안됩니다.

그 이유는 add(s,t)가 어떤 함수에 접근해야할 지 모호하기 때문인데요.

1번 함수는 '값 전달 방식'으로 매개변수를 전달하는 것이니 가능합니다. 2번 함수도 '참조 호출 방식'으로 매개변수를 전달하는 것이니 가능합니다. 

 

참조 매개변수로 인한 모호성

따라서, 다음과 같은 오류가 뜨며 컴파일이 불가합니다.

 

3. 디폴트 매개변수로 인한 모호성

#include<iostream>
#include<string>
using namespace std;

void msg(int id) {
	cout << id << endl;
} // 1

void msg(int id, string s = "") {
	cout << id << ":" << s << endl;
} // 2

int main() {
	msg(5, "Good Morning");
	msg(6);
}

msg(6)은 1번,2번 모두 접근 가능합니다. 2번 함수는 string s =" " 로 굳이 값을 주지 않아도 되는 기본매개변수이기 때문이죠.

 

디폴트 매개변수로 인한 모호성

2,3번 경우로 발생하는 모호성 같은 경우는 컴파일러가 잡아주지만 1번은 잡아주지 않았습니다. 1번은 특히 더 조심해야할 것 같네요.

 

 

읽어주셔서 감사합니다.

'Programming > C,C++' 카테고리의 다른 글

오버라이딩을 통한 다형성의 실현(virtual 개념, 오버라이딩 개념)  (0) 2022.03.01
참조리턴  (0) 2022.02.23
복사생성자  (0) 2022.02.23
연산자 중복  (0) 2022.02.22
static 멤버(정적멤버)  (0) 2022.02.21