[C++] 함수의 오버로딩, 생성자, 디폴트 생성자

·2023년 8월 18일
0

C++

목록 보기
4/17
post-custom-banner

함수의 오버로딩

오버로딩의 사전적 의미

  • 과적하다
  • sb (with sth) 너무 많이 주다
  • ✔️ (컴퓨터, 전기 시스템 등에) 과부화가 걸리게 하다

함수의 오버로딩 -> 함수에 과부화를 주는 것


C++에서는 같은 이름의 함수를 호출했을 때 어떻게 구분할까?

함수 호출 시 사용하는 인자를 보고 결정함
-> 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


C++ 컴파일러에서 함수를 오버로딩 하는 과정

(1) 자신과 타입이 정확히 일치하는 함수를 찾는다.
(2) 정확히 일치하는 타입이 없는 경우 아래와 같은 형변환을 통해서 일치하는 함수를 찾아본다.

  • char, unsigned char, short는 int로 변환된다.
  • unsigned short는 int의 크기에 따라 int 혹은 unsigned int로 변환된다.
  • float은 double로 변환된다.
  • enum은 int로 변환된다.
    (3) 위와 같이 변환해도 일치하는 것이 없다면 아래의 좀 더 포괄적인 형변환을 통해 일치하는 함수를 찾는다.
  • 임의의 숫자 타입은 다른 숫자 타입으로 변환된다. (ex) float -> int
  • enum도 임의의 숫자 타입으로 변환된다. (ex) enum -> double
  • 0은 포인터 타입이나 숫자 타입으로 변환된다.
  • 포인터는 void 포인터로 변환된다.
    (4) 유저 정의된 타입 변환으로 일치하는 것을 찾는다

이 과정을 통하더라도 일치하는 함수를 찾을 수 없거나 같은 단계에서 두 개 이상 일치하는 경우
'모호하다'고 판단해서 오류를 발생시킨다.

#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도 변환 가능 -> 두 개 이상 일치하므로 모호해짐


다시 만들어보는 Date 클래스

보통 간단한 함수를 제외하면 대부분의 함수들은 클래스 바깥에서 정의하게 됨
-> 클래스 내부에 쓸 경우 클래스 크기가 너무 길어져서 보기 좋지 않음
(단, 예외적으로 템플릿 클래스는 모두 클래스 내부에 작성하게 됨)

#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 일 입니다

post-custom-banner

0개의 댓글