함수의 오버로딩 -> 함수에 과부화를 주는 것
함수 호출 시 사용하는 인자를 보고 결정함
-> C++에서는 함수의 이름이 같더라도 인자가 다르면 다른 함수라고 판단함
/* 함수의 오버로딩 */
#include <iostream>
void print(int x) { std::cout << "int : " << x << std::endl; }
void print(char x) { std::cout << "char : " << x << std::endl; }
void print(double x) { std::cout << "double : " << x << std::endl; }
int main()
{
int a = 1;
char b = 'c';
double c = 3.2f;
print(a);
print(b);
print(c);
return 0;
}
실행 결과
int : 1
char : c
double : 3.2
(1) 자신과 타입이 정확히 일치하는 함수를 찾는다.
(2) 정확히 일치하는 타입이 없는 경우 아래와 같은 형변환을 통해서 일치하는 함수를 찾아본다.
이 과정을 통하더라도 일치하는 함수를 찾을 수 없거나 같은 단계에서 두 개 이상 일치하는 경우
'모호하다'고 판단해서 오류를 발생시킨다.
#include <iostream>
void print(int x) { std::cout << "int : " << x << std::endl; }
void print(double x) { std::cout << "double : " << x << std::endl; }
int main()
{
int a = 1;
char b = 'c';
double c = 3.2f;
print(a);
print(b);
print(c);
return 0;
}
인자로 char를 받는 print 함수는 없지만 오류가 발생하지 않는다.
실행 결과
int : 1
int : 99
double : 3.2
#include <iostream>
void print(int x) { std::cout << "int : " << x << std::endl; }
void print(char x) { std::cout << "char : " << x << std::endl; }
int main()
{
int a = 1;
char b = 'c';
double c = 3.2f;
print(a);
print(b);
print(c);
return 0;
}
오류 발생
1단계, 2단계에서 명화히 일치하는 것이 없음
3단계에서는 '임의의 숫자 타입이 임의의 숫자 타입'으로 변환돼서 생각되기 때문에
double은 char도 int도 변환 가능 -> 두 개 이상 일치하므로 모호해짐
보통 간단한 함수를 제외하면 대부분의 함수들은 클래스 바깥에서 정의하게 됨
-> 클래스 내부에 쓸 경우 클래스 크기가 너무 길어져서 보기 좋지 않음
(단, 예외적으로 템플릿 클래스는 모두 클래스 내부에 작성하게 됨)
#include<iostream>
class Date
{
int year_;
int month_; // 1 부터 12 까지.
int day_; // 1 부터 31 까지.
public:
void SetDate(int year, int month, int date);
void AddDay(int inc);
void AddMonth(int inc);
void AddYear(int inc);
// 해당 월의 총 일 수를 구한다.
int GetCurrentMonthTotalDays(int year, int month);
void ShowDate();
};
void Date::SetDate(int year, int month, int day)
{
year_ = year;
month_ = month;
day_ = day;
}
int Date::GetCurrentMonthTotalDays(int year, int month)
{
static int month_day[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month != 2)
{
return month_day[month - 1];
}
else if (year % 4 == 0 && year % 100 != 0)
{
return 29; // 윤년
}
else
{
return 28;
}
}
void Date::AddDay(int inc)
{
while (true)
{
// 현재 달의 총 일 수
int current_month_total_days = GetCurrentMonthTotalDays(year_, month_);
// 같은 달 안에 들어온다면;
if (day_ + inc <= current_month_total_days)
{
day_ += inc;
return;
}
else
{
// 다음달로 넘어가야 한다.
inc -= (current_month_total_days - day_ + 1);
day_ = 1;
AddMonth(1);
}
}
}
void Date::AddMonth(int inc)
{
AddYear((inc + month_ - 1) / 12);
month_ = month_ + inc % 12;
month_ = (month_ == 12 ? 12 : month_ % 12);
}
void Date::AddYear(int inc) { year_ += inc; }
void Date::ShowDate()
{
std::cout << "오늘은 " << year_ << " 년 " << month_ << " 월 " << day_
<< " 일 입니다 " << std::endl;
}
int main()
{
Date day;
day.SetDate(2011, 3, 1);
day.ShowDate();
day.AddDay(30);
day.ShowDate();
day.AddDay(2000);
day.ShowDate();
day.SetDate(2012, 1, 31); // 윤년
day.AddDay(29);
day.ShowDate();
day.SetDate(2012, 8, 4);
day.AddDay(2500);
day.ShowDate();
return 0;
}
실행 결과
오늘은 2011 년 3 월 1 일 입니다
오늘은 2011 년 3 월 31 일 입니다
오늘은 2016 년 9 월 20 일 입니다
오늘은 2012 년 2 월 29 일 입니다
오늘은 2019 년 6 월 9 일 입니다
만약 위 main함수에서 Date에 변경을 가하기 전에 SetDate 함수를 호출하여 초기화 해 주지 않으면
초기화 되지 않은 값들에 덧셈과 출력 명령이 내려져 원하지 않는 쓰레기 값이 출력된다.
-> 생성 후 초기화를 빠뜨리지 않도록 도와주는 장치가 '생성자'임
객체 생성 시 자동으로 호출되는 함수
클래스 이름(인자) {}
의 형태로 이루어지며 초기화 하는 역할을 하기 때문에 리턴값 없음
#include <iostream>
class Date
{
int year_;
int month_; // 1 부터 12 까지.
int day_; // 1 부터 31 까지.
public:
void SetDate(int year, int month, int date);
void AddDay(int inc);
void AddMonth(int inc);
void AddYear(int inc);
// 해당 월의 총 일 수를 구한다.
int GetCurrentMonthTotalDays(int year, int month);
void ShowDate();
Date(int year, int month, int day)
{
year_ = year;
month_ = month;
day_ = day;
}
};
// 생략
void Date::AddYear(int inc) { year_ += inc; }
void Date::ShowDate()
{
std::cout << "오늘은 " << year_ << " 년 " << month_ << " 월 " << day_
<< " 일 입니다 " << std::endl;
}
int main()
{
Date day(2011, 3, 1); // 생성자 Date(int year, int month, int day) 호출
day.ShowDate();
day.AddYear(10);
day.ShowDate();
return 0;
}
실행 결과
오늘은 2011 년 3 월 1 일 입니다
오늘은 2021 년 3 월 1 일 입니다
함수를 호출하듯이 사용하는 것이 암시적 방법
명시적으로 생성자를 호출한다는 것을 보여주는 것이 명시적 방법
Date day(2011, 3, 1); // 암시적 방법 (implicit)
Date day = Date(2012, 3, 1); // 명시적 방법 (explicit)
인자를 하나도 가지지 않는 생성자
사용자가 어떠한 생성자도 명시적으로 정의하지 않았을 경우 컴파일러가 자동으로 추가해주는 생성자
Date()
{
year = 2012;
month = 7;
day = 12;
}
int main()
{
Date day = Date();
Date day2;
}
주의
Date day3();
객체를 디폴트 생성자를 이용해서 초기화하는 것이 아니라
리턴값이 Date이고, 인자가 없는 함수 day3()을 정의하게 된 것으로 인식함
-> 인자가 없는 생성자를 호출 할 때 주의 필요
class Test
{
public:
Test() = default; // 디폴트 생성자를 정의해라
};
생성자 역시 함수이기 때문에 마찬가지로 함수의 오버로딩이 적용될 수 있음
#include <iostream>
class Date
{
int year_;
int month_; // 1 부터 12 까지.
int day_; // 1 부터 31 까지.
public:
void ShowDate();
Date()
{
std::cout << "기본 생성자 호출!" << std::endl;
year_ = 2012;
month_ = 7;
day_ = 12;
}
Date(int year, int month, int day)
{
std::cout << "인자 3 개인 생성자 호출!" << std::endl;
year_ = year;
month_ = month;
day_ = day;
}
};
void Date::ShowDate()
{
std::cout << "오늘은 " << year_ << " 년 " << month_ << " 월 " << day_
<< " 일 입니다 " << std::endl;
}
int main()
{
Date day = Date();
Date day2(2012, 10, 31);
day.ShowDate();
day2.ShowDate();
return 0;
}
실행 결과
기본 생성자 호출!
인자 3 개인 생성자 호출!
오늘은 2012 년 7 월 12 일 입니다
오늘은 2012 년 10 월 31 일 입니다