본문 바로가기

개발자/C++(Linux, Window)

[C++] 연산자 오버로딩

반응형

이번시간에는 연산자 오버로딩에 대해 알아보도록 하겠습니다.

 

☞ 연산자 오버로딩이란

기존의 제공하고 있는 연산자를 재정의하여 사용자 정의 클래스로 사용하는 것을 말합니다.

대부분의 기본 제공 연산자 함수는 전역 함수 또는 클래스로 재정의 가능합니다.

오버로드 된 연산자는 함수로 구현됩니다.

 

 

☞ 연산자 오버로드에 대한 일반 규칙

  • **과 같은 새로운 연산자를 정의할 수는 없습니다.
  • 기본 제공 데이터 형식에 적용할 때 연산자의 의미를 다시 정의 할 수 없습니다.
  • 오버로드 된 연산자는 비정적(non-static) 클래스 멤버 함수거나 전역 함수이어야 합니다.

   (private 또는 protected 접근자의 전역 함수는 해당 클래스의 friend로 선언해야 합니다.)

  • 단항 연산자 또는 이항 연산자(&, *, +, -)로 오버로드 가능하며 각 사용을 별도로 오버로드 할 수 있습니다.
  • 멤버 함수로 오버로드된 연산자의 첫번째 파라미터는 항상 연산자가 호출되는 객체의 클래스 형식입니다.

   (첫번째 파라미터에 대한 변환은 제공되지 않습니다.)

 

 

 오버로딩 불가능한 연산자

아래 표의 연산자들은 오버로드 할 수 없습니다.

 

 연산자 이름 
멤버선택 
.*  멤버 포인터 선택 
::  범위 
?:  조건 
문자열 전처리기 변환 
##  전처리기 연결 

 

 

 간단한 예

#include <iostream>
using namespace std;
 
class Point {
private :
    int x, y;
 
public :
    Point(int x_, int y_) {
        x = x_;
        y = y_;
    }
 
    void print() {
        cout << "x : " << x << ", y : " << y << "\n";
    }
};
 
 
int main(void) {
    Point p1 = { 1, 1 };
    Point p2(2, 2);
    
    Point p3 = p1 + p2;
 
    p3.print();
 
    return 0;
}

 

 

다음과 같이 Point라는 클래스가 있고 Point 클래스 내부에는 int x와 int y가 있습니다.

main함수에서 Point 객체인 p1과 p2를 생성해주고, p3에는 p1과 p2를 더한값을 저장하고 싶습니다.

근데 + 연산자에 대해 Point 객체의 덧셈은 지원 해주지 않습니다.

Point 클래스는 방금 제가 임의로 만든 클래스이며, + 연산자가 무엇을 의미하는지 모르기 때문이죠.

마치 String 변수의 + 연산이 a+a = aa인지 a+a=b인지 모르는것 처럼요.

 

 

이를 해결하기 위해 C++에서는 연산자 오버로딩을 제공합니다.

설명은 위에서 해드렸으니 코드에 대해 설명하겠습니다.

먼저 Point 클래스의 + 연산자에 대한 오버로딩 예제 코드입니다.

 

#include <iostream>
using namespace std;
 
class Point {
private :
    int x, y;
 
public :
    Point(int x_, int y_) {
        x = x_;
        y = y_;
    }
 
    void print() {
        cout << "x : " << x << ", y : " << y << "\n";
    }
 
    Point operator + (Point& p) {
        x = x + p.x;
        y = y + p.y;
        return Point(x, y);
    }
};
 
 
int main(void) {
    Point p1 = { 1, 1 };
    Point p2(2, 2);
    
    Point p3 = p1 + p2;
 
    p3.print();
 
    return 0;
}

 

18번째 줄을 보시면 새로운 함수가 하나 생성되었습니다.

+ 연산자에 대한 오버로딩 함수인데요. 하나씩 살펴보겠습니다. ^^

반환형으로는 Point 객체네요. 

함수의 이름은 operator입니다. 함수의 이름을 operator로 사용함으로써 컴파일러에게 연산자 오버로드 함수인것을 명시합니다.

뒤에 연산자 +를 입력했습니다. 매개변수로는 Point 객체의 참조변수입니다.

 

30번째 줄을 보시면 Point p3 = p1 + p2; 라는 코드가 있습니다.

여기서 p1이 오버로드 된 + 연산자 함수를 호출했으며, + 뒤에있는 p2는 Point& p로 넘겨 받아지는 것 입니다.

반환값은 Point 객체로 x와 y가 각각 더해진 새로운 임시 Point 객체를 반환합니다.

이 반환된 임시 객체 값이 p3 값으로 넘어가서 결과적으로 3, 3이 출력되는 것입니다.

 

ㅇ 출력 결과

 

x : 3, y : 3

 

 

☞ 연산자 오버로딩 활용 1

데이터 타입 변환에 대한 연산자 또한 오버로딩 할 수 있습니다.

다음 예제를 확인해보세요.

#include <iostream>
using namespace std;
 
class DIV {
private :
    int num, div;
public :
    DIV(int n, int d) {
        num = n;
        div = d;
    }
 
    operator double() const {
        return double(num) / double(div);
    }
};
 
 
int main(void) {
    DIV d(1, 3);
    double db = d;
    cout << d;
 
    return 0;
}

 

21번째 줄을 보시면 double 변수로 db를 선언하고 값을 DIV 객체인 d를 넘겨주었습니다.

어떻게 이게 가능할까요?

13번째 줄을 보시면 double 이라는 키워드를 연산자 오버로딩하여 double 값을 반환하는 함수로 오버로딩 했습니다.

그러므로 double 변수에 대입할 수 있게 된 것이죠.

 

☞ 연산자 오버로딩 활용 2

cin, cout의 << 연산자, >> 연산자 오버로딩 역시 가능합니다.

다음 예제 코드는 cin 의 << 연산자에 대한 오버로딩 입니다.

#include <iostream>
using namespace std;
 
class Point {
private :
    int x, y;
 
public :
    Point(int x_, int y_) {
        x = x_;
        y = y_;
    }
 
    friend ostream& operator<<(ostream&, const Point&);
};
 
ostream& operator<<(ostream& os, const Point& p) {
    os << p.x << ", " << p.y;
    return os;
}
 
int main(void) {
    Point p(2, 3);
    
    cout << p;
 
    return 0;
}

 

Point라는 클래스가 있습니다. p 라는 객체를 생성하고 cout << p; 라는 코드는 분명 오류가 발생합니다.

왜나면 cout의 << 연산자는 기본적으로 사용자 정의 클래스에 대한 출력을 지원해 주지 않기 때문이죠.

이를 연산자 오버로딩을 통해 해결 가능합니다.

17번째 줄을 보시면 << 연산자에 대한 오버로드 함수입니다.

또한 Point 객체의 private 멤버인 x와 y에 접근 할 수 있도록 Point 클래스 내부에 friend 함수를 지정해 주었습니다.

 

ㅇ 출력 결과

 

2, 3

 


* cout, cin, endl의 연산자 오버로딩

 

ex)

#include <iostream>


namespace mystd
{
    using namespace std;


    class ostream
    {
    public :
        ostream& operator<< (char * str)
        {
            printf("%s", str);
            return *this;
        }
        ostream& operator<< (char str)
        {
            printf("%c", str);
            return *this;        }
        ostream& operator<< (int num)        
        {
            printf("%d", num);
            return *this;        }
        ostream& operator<< (double e)        
        {
            printf("%g", e);
            return *this;        }
        ostream& operator<< (ostream& (*fp)(ostream &ostm))    // Fuction Poiner         
        {
            return fp(*this);
        }
    };


    ostream& endl(ostream &ostm)
    {
        ostm<<'\n';
        fflush(stdout);
        return ostm;
    }
    
    ostream cout;
}


int main()
{
    using mystd::cout;
    using mystd::endl;


    cout<<"Simple"<<endl<<3333<<<<endl;


    return 0;

- cout과 endl은 std라는 이름공간에 선언된 객체이다.

 

 

* <<, >> 연산자의 오버로딩

 

ex)

#include <iostream>
using namespace std;


class Point
{
private : 
    int xpos, ypos;


public :
    Point(int x=0, int y=0) : xpos(x), ypos(y) {}
    void ShowPoint() const { cout<<'['<<xpos<<", "<<ypos<<']'<<endl; }
    friend ostream& operator<<(ostream&, const Point&);
    friend istream& operator>>(istream&, Point & pos);
};


ostream& operator<<(ostream& os, const Point& pos)
{
    os<<'['<<pos.xpos<<", "<<pos.ypos<<']'<<endl;
    return os;
}


istream& operator>>(istream& is, Point& pos)
{
    is>>pos.xpos>>pos.ypos;
    return is;
}


int main()
{
    Point pos1;
    cin>>pos1;
    cout<<pos1;


    Point pos2;
    cin>>pos2;
    cout<<pos2;
    
    return 0;
}

- cout은 ostream 클래스의 객체이며 cin은 istream 클래스의 객체이다.

- 32행의 문장이 가능하기 위해서는 'operator<<(cout, pos)'와 같이 해석이 되어야 한다.(전역함수일 경우)

- 멤버함수에 의한 방법은 ostream 클래스를 직접 수정해야 하므로 불가능하다.

- 마찬가지로 31행의 문장이 가능하기 위해서는 'operator>>(cin, pos)'와 같이 해석이 되어야 한다.

 

 

 

 

 

 

이상 '연산자 오버로딩'에 대해 알아보았습니다.

질문 또는 오타나 잘못된 정보가 있는 경우 댓글로 달아주세요!

반응형