함수 오버로딩(function overloading)
개념
함수 오버로딩(function overloading)은 C++에서 같은 이름을 가진 여러 함수를 정의할 수 있게 해주는 기능이다. C에서는 같은 이름의 함수를 여러 개 선언할 수 없지만, C++에서는 매개변수의 타입이나 개수가 다르면 같은 이름의 함수를 여러 개 선언할 수 있다.
// 정수형 두 값의 합을 구하는 함수
int add(int a, int b) {
return a + b;
}
// 실수형 두 값의 합을 구하는 함수
double add(double a, double b) {
return a + b;
}
// 정수형 세 값의 합을 구하는 함수
int add(int a, int b, int c) {
return a + b + c;
}
함수 오버로딩의 규칙
- 매개변수는 달라야 함: 함수 오버로딩은 매개변수의 타입이나 개수가 달라야 함
- 반환 타입만으로는 구분 불가: 반환 타입만 다르고 매개변수가 같은 함수는 오버로딩할 수 없음
int getNumber();
double getNumber(); // 컴파일 오류, 반환 타입만 다름
- 참조형과 비참조형은 구분 가능: 매개변수가 참조형인지 아닌지에 따라 함수를 오버로딩할 수 있음
void process(int i);
void process(int& i); // 유효한 오버로딩
- const와 non-const 매개변수로 구분 가능:
const
키워드의 유무에 따라 함수를 오버로딩할 수 있음
void display(char* str);
void display(const char* str); // 유효한 오버로딩
- 기본 매개변수와 주의점: 기본 매개변수가 있는 함수는 오버로딩 시 모호성을 발생시킬 수 있다.
void show(int a, int b = 0);
void show(int a); // 호출 시 모호성 발생 가능
함수 시그니처
함수 시그니처(function signature)는 컴파일러가 함수를 구별하는 데 사용하는 함수의 특성을 말한다.
시그니처에 포함되는 요소
- 함수 이름
- 매개변수 타입 및 개수
- const, volatile 한정자
- 참조 한정자(& 또는 &&)
- 예외 명세(C++17 이전)
반환 타입은 함수 시그니처에 포함되지 않음
오버로딩 해결(overload resolution)
컴파일러가 함수 호출 시 어떤 오버로딩된 함수를 선택할지 결정하는 과정을 오버로딩 해결이라고 한다.
오버로딩 해결 과정
- 후보 함수 식별: 호출된 함수와 이름이 같은 함수를 후보로 수집
- 적합한 함수 선별: 매개변수 개수와 타입이 호출과 일치하는 함수를 선별
- 최적의 함수 선택: 여러 함수가 적합하다면, 가장 정확히 일치하는 함수를 선택
- 모호성 검사: 동일한 적합성을 가진 함수가 여러 개라면 모호성 오류를 발생시킴
void print(int i) { cout << "정수: " << i << endl; }
void print(double d) { cout << "실수: " << d << endl; }
void print(const char* s) { cout << "문자열: " << s << endl; }
// 함수 호출
print(10); // print(int) 호출
print(10.5); // print(double) 호출
print("안녕"); // print(const char*) 호출
암시적 형변환과 오버로딩의 관계
암시적 형변환(implicit type conversion)은 컴파일러가 자동으로 수행하는 타입 변환을 말한다. C++에서는 함수 호출 시 인자의 타입이 매개변수와 정확히 일치하지 않으면, 컴파일러는 암시적 형변환을 통해 적합한 함수를 찾으려고 시도한다.
형변환 순위
C++에서는 암시적 형변환에 다음과 같은 순위를 부여한다.
- 정확한 일치: 타입이 정확히 동일한 경우(가장 우선)
- 증진(promotion): 작은 정수 타입에서 큰 정수 타입으로 변환(char → int, short → int)
- 표준 변환: 수치 간 변환(int → float, long → double)
- 사용자 정의 변환: 사용자가 정의한 변환 연산자나 생성자를 통한 변환
- 가변 인자 함수로 변환: 가변 인자 함수(...) 매칭(가장 후순위)
오버로딩과 형변환 관계
- 정확한 일치 우선: 정확히 일치하는 함수가 있다면 그것을 선택
void func(int i) { cout << "int 버전" << endl; }
void func(double d) { cout << "double 버전" << endl; }
func(5); // int 버전 호출 - 정확한 일치
func(5.5); // double 버전 호출 - 정확한 일치
- 변환이 필요한 경우: 정확히 일치하는 함수가 없다면 가장 적은 변환으로 호출할 수 있는 함수를 선택
void show(int i) { cout << "int: " << i << endl; }
void show(double d) { cout << "double: " << d << endl; }
short s = 10;
show(s); // show(int) 호출 - short에서 int로 증진이 우선
- 모호성이 발생하는 경우: 여러 함수가 동일한 수준의 변환으로 호출 가능하다면 모호성 오류가 발생
void display(long l) { cout << "long" << endl; }
void display(double d) { cout << "double" << endl; }
int i = 5;
// display(i); // 컴파일 오류! int→long과 int→double 변환은 같은 수준
- 사용자 정의 변환: 클래스 타입을 포함한 오버로딩에서는 사용자 정의 변환이 고려됨
class Number {
public:
operator int() { return 0; } // int로 변환
operator double() { return 0.0; } // double로 변환
};
void calc(int i) { cout << "int 버전" << endl; }
void calc(double d) { cout << "double 버전" << endl; }
Number n;
// calc(n); // 컴파일 오류! 모호한 변환
요약
함수 오버로딩은 C++에서 같은 이름으로 다양한 기능을 수행하는 함수를 제공할 수 있게 해주는 강력한 기능이다.
이를 효과적으로 사용하려면
- 매개변수 타입과 개수를 명확히 구분하여 오버로딩
- 함수 시그니처와 오버로딩 해결 과정을 이해
- 암시적 형변환과 그 우선순위를 고려하여 모호성을 방지
- 기본 매개변수와 오버로딩을 함께 사용할 때 발생할 수 있는 모호성에 주의
함수 오버로딩을 통해 코드의 가독성과 재사용성을 높일 수 있으며, 이는 객체 지향 프로그래밍의 다형성(Polymorphism)을 구현하는 기본적인 메커니즘 중 하나이다.