본문 바로가기

C++/C++ 기초

오버로딩(Overloading)

오버로딩이란?

 

  오버로딩의 사전적 의미를 먼저 알아보자. '과적'이라는 의미를 가지고 있다. 프로그래밍에서 이러한 의미를 가진 단어를 사용하는 이유는 무엇일까? 먼저 우리는 C언어를 공부하면서 컴퓨터의 명령어는 기본적으로 한가지의 의미를 가지고 있다고 배웠다. 하나의 명령어가 두 개 이상의 의미를 가지고 있으면 컴퓨터는 해당 명령어에서 어떤 의미로 해석해야할 지 구분하지 못한다. 이렇게 기계어로 이루어진 명령어를 인간이 이해하기 쉽게 만든 C언어에서도 원칙을 지키고 있다. 하나의 키워드나 예약어 또는 사용자에 의해 정의된 함수명들은 하나의 의미를 가지도록 되어 있다.

 

  C++에서는 C언어보다 다양한 조건으로 명령어을 해석하도록 개선되어 있다. 그래서 한 개의 명령어가 두 개 이상의 의미를 가지는 것이 가능해졌다. 이렇게 한 개의 명령어가 두 개 이상의 의미를 내포하고 있는 것을 '오버로딩'이라고 한다. C++에서는 오버로딩으로 연산자 오버로딩과 함수 오버로딩을 제공한다.

 


 

연산자 오버로딩

 

  C언어에서도 잘 생각해보면 하나의 연산자가 두 개의 의미를 가지는 것이 있었다. 바로 '*'이다. '*'이 단항 연산자로 사용되었을 경우에는 번지 지정 연산자로 사용되었고, 이항 연산자로 사용되었을 경우에는 곱셈 연산자로 사용되었다. 이는 C언어에서 연산자가 함께 사용된 피연산자의 개수를 확인하여 구별한다는 뜻이다.

 

C 컴파일러의 연산자 처리

  수식에서 연산자를 발견하면 연산자를 중심으로 연산을 처리한다. 그래서 함께 사용된 피연산자의 자료형과 상관없이 숫자로 변환하여 연산을 진행한다. 이 과정에서 피연산자가 숫자가 아닌 경우에는 문법 오류로 처리해 버린다. 즉, C언어에서 '+' 연산자의 경우에는 숫자를 더하는 용도로만 사용이 가능한 것이다.

 

char *p_string, str1[16] = "Hello", str[16] = "World";
p_string = str1 + str2;

 

위 소스를 보면 대충 무슨 의도로 작성했는지 알 수는 있다. "abc" 문자열과 "def" 문자열을 연결시키려는 시도로 보인다. 위에서 설명한 내용을 토대로 생각해보면 의도대로 진행되지 않는다는 것을 알 수 있다. 그런데 str1과 str2는 결국에서 주소익기 때문에 주소를 정수형 상수로 인식하여 연산은 진행되지 않을까 하고 생각할 수 있다. 결과는 오류가 난다. 그 이유는 C언어에서 주소 연산에서 한쪽의 피연산자가 주소인 경우 나머지 피연산자는 무조건 정수형 상수형이 되어야 하기 때문이다.

 

#include <stdio.h>
#include <string.h>

int main()
{
    char *p_string, str1[16] = "Hello", str2[16] = "World";

    p_string = strcat(str1, str2);
    printf("%s\n", p_string);

    return 0;
}

 

처음의 의도대로 결과를 확인하기 위해서 소스를 수정한 것이 위의 소스이다.

 

C++ 컴파일러의 연산자 처리

  위에서 수정한 소스코드를 C++의 클래스 문법을 사용하여 수정해보면 아래와 같다.

 

#include <string.h>

class My_string
{
private:
    char m_string[16];
public:
    void SetString(char *p_string)
    {
        strcpy(m_string, p_string);
    }

    char *AppendString(char *p_string)
    {
        return strcat(m_string, p_string);
    }
};

 

'SetString()' 멤버 함수의 역할은 "Hello" 문자열을 처음 변수에 저장해주는 역할이고, 'AppendString()' 멤버 함수의 역할은 "Hello"와 "World"를 연결시켜주는 역할이다. 위의 클래스를 사용하여 C언어로 작성한 소스의 결과와 동일한 결과를 확인하는 소스가 아래의 소스이다.

 

#include <stdio.h>
#include <string.h>

class My_String
{
private:
    char m_string[16];
public:
    void SetString(char *p_string)
    {
        strcpy(m_string, p_string);
    }

    char *AppendString(char *p_string)
    {
        return strcat(m_string, p_string);
    }
};

int main()
{
    My_String str;
    
    str.SetString("Hello");
    char *p_string = str.AppendString("World");

    printf("%s\n", p_string);

    return 0;
}

 

C++는 멤버 함수의 이름에 연산자 키워드를 사용할 수 있다. '+' 연산자를 'operator+'의 이름으로 사용할 수 있다. 이 점을 이용하여 'AppendString()' 멤버 함수의 이름을 'operator+'로 바꾸면 의미상 문자열을 더한다는 느낌이 더 강해진다. 하지만, 사용상에는 별다른 편의를 느끼기 힘들다. 이 때 C++에서 제공하는 멤버 함수의 이름으로 연산자 키워드를 사용했울 때의 표현 방법을 알게 되면 훨씬 편리하단 것을 느낄 것이다. 어떻게 표현할 수 있는지 아래에서 설명하겠다.

char *p_string = str.operator+("World");

 

위의 코드는 'AppendString()' 멤버 함수의 이름을 연산자로 바꿨을 때에 사용하는 명령문이다. 위의 코드를 새로운 표현 방법으로 바꾸면 아래와 같다.

 

char *p_string = str + "World";

 

정말 확실한 의미 강조이지 않은가? str 문자열에 "World" 문자열을 연결한다는 것을 바로 알 수 있다. 위와 같은 표현을 사용할 수 있는 이유는 C++ 컴파일러는 연산자 키워드를 만났을 경우에 C 컴파일러와는 다르게 피연산자를 숫자로 단정 짓지 않고 객체의 유무를 확인한다. 그리고 객체가 존재할 경우에 해당 객체에 해당 연산자 키워드로 함수가 호출되었는지 확인하고 호출되었을 경우에 기본 연산 기능을 수행하지 않고 새롭게 정의된 함수를 실행하기 때문이다.

 

  연산자 오버로딩에서 주의할 점이 있는데, 피연산자를 적는 순서이다. 아래와 같이 사용하게 되면 오류가 난다.

 

char *p_string = "World" + str;

 

즉, 연산자의 이름을 사용한 멤버 함수가 선언된 객체를 먼저 적어야 정상 동작한다.

 

  위의 예와 같이 객체에 연산자 키워드를 사용하여 멤버 함수를 정의하고 그렇게 새로운 정의가 추가된 연산자가 두 개 이상의 의미를 가지는 것을 연산자 오버로딩이라 한다.

 


 

함수의 오버로딩

 

C언어의 함수 정의

  C언어는 함수를 구분할 때에 함수의 이름을 가지고 구별한다. 그래서 동일한 플로그램에 같은 이름의 함수가 존재하면 오류를 발생시킨다.

 

C++의 함수 정의

  C++ 언어는 함수의 이름만으로 함수를 구분하지 않고 매개 변수의 자료형과 개수가 동일한지 추가로 비교하여 함수를 구분한다.

 

int Sum(int num1, int num2)
{
    return num1 + num2;
}

int Sum(int num1, int num2, int num3)
{
    return num1 + num2 + num3;
}

double Sum(double num1, double num2)
{
    return num1 + num2;
}

 

즉, 위의 세 개의 함수가 모두 다른 함수라는 의미이다. 이렇게 함수의 이름은 같지만 매개 변수의 개수나 자료형으로 구분하여 다른 의미를 정의하는 것을 함수의 오버로딩이라고 한다.

 

  함수의 오버로딩을 할 때에 주의할 점이 있는데, 바로 반환형으로 구분하지는 않는다는 것이다. 그리고 매개 변수로 넘겨주는 인자를 애매하게 사용하면 안된다. 앞서 정수형 매개 변수 두 개를 받는 함수와 실수형 매개 변수 두 개를 받는 함수를 선언하였는데, 이 때 함수를 호출할 떄 넘겨주는 인자로 정수와 실수 두 개를 사용하게 되면 컴파일러가 정수형 매개 변수 두 개를 사용하는 함수인지 실수형 매개 변수 두 개를 사용하는 함수인지 판단할 수 없기 떄문이다.

'C++ > C++ 기초' 카테고리의 다른 글

네임스페이스 (Namespace)  (0) 2019.09.09
객체 파괴자  (0) 2019.09.05
객체 생성자  (0) 2019.09.02
클래스(Class)  (0) 2019.08.31
C++ 개요  (0) 2019.08.31