Corgi Dog Bark

ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Overloading 오버로딩 - 연산자
    뜯고 또 뜯어보는 컴퓨터/씨쁠쁠 C++ 2022. 11. 2. 12:54
    반응형

    0. Overloading 이란?


    C++에서는 코드에 이름이 같은 함수가 여러 개 있다면, 컴파일러(Compiler)는 함수를 호출하는 시점에, 타입이 일치하는 함수를 선택하게 되는데, 이를 오버 로딩(Overloading)이라 합니다.(오버 라이딩(Overriding)과 혼동하는 경우가 많으니, 주의해야 합니다.) 더구나 C++는 대부분의 연산자가 클래스와 관련된 특수 연산을 수행하도록 오버 로드하는 기능을 제공하는데, 예를 들어서 String 클래스는 + 연산자를 오버 로드하여 두 문자열을 연결할 수 있도록 합니다.

     

    [그림1. gcc basic_string.h 발췌]

     

    다양한 오버 로딩 연산자가 있지만, 이 글에서는 연산자 함수의 오버 로딩에 대해서 알아보겠습니다. (연산자 함수를 오버 로딩하면서, 유저가 만든 다양한 구조체 및 클래스에 대해서, 연산을 수행할 수 있게 된다는 장점이 있습니다.)

     

     

    1. 연산자 오버 로딩


    먼저 연산자 오버 로딩 함수를 구현하려면, 연산자 함수를 생성함으로써 오버 로드됩니다.(당연한 말일 수도 있습니다.) 생성된 연산자 함수는 오버 로드된 연산자가 클래스의 개체에 대해 어떠한 연산을 수행할 것인지 정의하게 됩니다. 먼저 오버 로딩을 사용하지 않았을때, 그리고 사용했을때의 차이를 보여주는 간단한 예제를 보이자면,

    • 연산자 오버 로딩을 구현했을 시, 다음과 같이 사용자가 무슨 의미인지 유추할 수 있게끔 표현이 가능하지만,
    String s1;
    String s2;
    String stringSum = s1 + s2;
    cout << s1 << " " << s2 << endl;
    • 만약, 오버 로딩을 사용하지 않고 구현한다면, 다음과 같이 구현이 됩니다.
    String s1;
    String s2;
    String stringSum = s1.add(s2);
    s1.print(cout);
    cout << " ";
    s2.print(cout);
    cout << endl;

    위 와 같이 연산자 오버로딩을 적용하게 되면, 자신이 정의한 클래스의 인터페이스를 훨씬 쉽고 빠르게 만들 수 있습니다. 하지만 사용자가 알기 힘든 타입이나 어떻게 동작하는지 모르는 객체에 대해서, 오버 로딩을 남용하면 된다는 뜻은 아닙니다. 

     

     

    2. 연산자 오버 로딩의 작동방식


    처음 연산자를 오버 로딩을 하게 될 때, 왜 굳이 이렇게까지 하면서, 구현할까 하는 생각이 많았었습니다. 지금 와서 생각을 해보면, 우리가 사용하는 수많은 클래스들은 이미 많은 경우의 오버 로딩이 되어있기 때문에, 그 편리함을 몰랐다고 생각할 수 있을 것 같습니다. 그렇다면 연산자 오버 로딩은 어떻게 작성될까 하는 의문점이 들 수 있습니다.

     

    연산자 오버 로딩은 다음과 같은 형태를 띠게 됩니다.

    // 객체 내부 함수로의 구현
    UserClass operator+(const UserClass& anotherUser) const{}
    
    // 전역 Operator 의 구현
    UserClass operator+(const UserClass& lUser, const UserClass& rUser) const {}

     

    위는 + 연산자를 오버 로딩한 간단한 예제인데, 처음 오버 로딩을 봤었을 때, 저 자리(operator+)가 함수 자리의 이름인 건 알겠는데 이해가 너무나 안 갔던 기억이 생생합니다.. 아무튼 <operator+>는 함수 이름입니다. 이는 나중에 컴파일러가 연산자를 발견하게 되면, 객체 내부에서 객체를 인수로 받는 연산자 함수가 있는지 찾거나, 아니면 객체 두 개를 인수로 받는 연산자 함수가 전역에 있는지 찾게 됩니다.

     

     

     

    3. operator+ 연산자 구현하기


    그렇다면, 본격적으로 오버 로딩을 구현해보도록 하겠습니다. 다음과 같이 간단한 Box라는 객체가 있다고 가정해봅시다.

    struct Box
    {
    	int length;
    	int breadth;
    	int height;
    };

    그렇다면, Box를 쌓아 올렸을 때, + 연산자를 오버 로딩해보는 방법을 생각해 볼 수 있을 것 같습니다. 다음과 같이 구조체 내부에 + 연산자를 오버 로딩하여, + 연산자를 작성해주었습니다.

    Box operator+(const Box& rBox) {
    	Box box;
    	box.length = this->length + rBox.length;
    	box.breadth = this->breadth + rBox.breadth;
    	box.height = this->height + rBox.height;
    	return box;
    }

    그렇다면, 2번째 방법으로 객체 내부에 있는 연산자 오버 로딩이 아닌 전역 함수로 바깥에서 작성해보도록 하겠습니다.

    Box operator+(const Box& lhs, const Box& rhs)
    {
    	Box box;
    	box.length = lhs.length + rhs.length;
    	box.breadth = lhs.breadth + rhs.breadth;
    	box.height = lhs.height + rhs.height;
    	return box;
    }

    이때, 전역 함수로 작성했을 때의 장점은, Box 객체로의 묵시적 변환이 우측에 있을 때도 가능하다는 점입니다. 이는 객체 내부에 연산자 함수가 있을 시, 반드시 Box 객체에 대하여 호출 후, 연산자 함수를 호출해야 하기 때문입니다.

     

    4. 정리


    이로써 오버 로딩(Overloading)을 통하여, 객체를 새로 생성해주거나, 새로 선언해준 자료형들 간의 연산을 할 때 컴파일러에게 어떻게 그 연산을 처리해야 하는지 알려주어 편리함을 제공하는 방법을 알아보았습니다.. 다음 오버 로딩(2) 편에서는 좀 더 다양한 오버 로딩을 소개하는 글로 찾아뵙겠습니다. 

     

    간단한 문제 : 다음 문제의 output 은?

    #include <iostream>
    
    using namespace std;
    
    class sample {
    public:
      int x, y;
      sample(){};
      sample(int a, int b):x(a),y(b){};
      sample operator+(const sample param) {
        sample temp;
        temp.x = x + param.x;
        temp.y = y + param.y;
        return temp;
      }
    };
    
    int main() {
      sample a(4, 1);
      sample b(3, 2);
      sample c;
      c = a + b;
      cout << c.x << "," << c.y;
      return 0;
    }

     

     

    긴 글 읽어주셔서 감사합니다.

    잘못된 내용이 있거나, 도움 되셨다면, 댓글 부탁드리겠습니다~

    반응형

    댓글

Designed by Tistory.