C++/Operator Overloading

C++ Operator Overloading

Soul-Learner 2016. 12. 23. 15:49

C++ 프로그래밍, 연산자 오버로딩 ( Operator Overloading )


프로그램을 구성하는 코드는 간결하고 가독성이 좋아야 한다. 그러한 코드는 개발자가 코드를 읽을 때 직관적으로 이해될 수 있으므로 유지보수면에서도 도움이 된다. 코드의 가독성을 높이기 위한 좋은 방법은우리에게 익숙한 기호(연산자)를 최대한 사용하는 것인데, 예를 들어 다음과 같은 코드가 동일한 기능을 한다면 어떤 코드가 직관적인지 비교해 보자

CVector v1(2,3);
CVector v2(4,5);
CVector v3 = v1.add(v2);   // 이렇게 하는 것이 직관적인가?
CVector v3 = v1 + v2;       // 이건 어떨까?

위의 코드에서 v1 + v2 부분은 바로 그 의미가 이해되는데, 개발자가 작성한 클래스인 CVector 객체를 덧셈기호로 연결해주면 바로 덧셈이 되는 것은 아니다. 특정 클래스(CVector)에서 연산자의 의미를 확장/변경해야만 위와 같은 연산자를 사용한 직관적인 코드를 사용할 수 있는 것이다. 

C++에서는 기본 연산자의 기능을 개발자가 확장/변경 할 수 있는 방법을 제공하는데, 이 방법을 연산자 오버로딩(Operator Overloading )이라고 한다.

연산자 오버로딩을 적용하여 기능을 확장/변경할 수 있는 연산자

+ - * / = < > += -= *= /= << >> <<= >>= == != <= >= ++ -- % & ^ ! | ~ &= ^= |= && || %= [] () , ->* -> new delete new[] delete[]


연산자 오버로딩

특정 클래스에서 사용할 연산자의 기능을 변경하고자 한다면, 연산자에 적용할 연산자 함수(Operator Functions)를 해당 클래스에 멤버함수로 정의해야 한다. 연산자 함수란 해당 연산자가 사용될 때 실행될 로직을 함수로 표현한 것이다. 다음과 같은 형식을 가져야 한다

리턴타입 operator 대상연산자 (연산자에 전달할 파라미터) { 연산자 기능 정의 }

CVector operator + ( CVector ) ;                               // 연산자 함수 선언부

CVector :: operator + ( CVector v ) {                         // 연산자 함수 정의부
    CVector resVec = new CVector( x+v.x , y+v.y );         // 연산자의 기능
    return resVec;
}


벡터 덧셈 연산을 수행할 때 + 연산자를 사용하도록 연산자를 오버로딩한 예

#include <iostream>
#include <locale>
#include <string>

using namespace std;

class CVector
{
    double x;
    double y;

    public:

    //CVector(){}
    CVector(double x, double y) {
        this->x = x;   // this는 현재 객체의 포인터
        this->y = y;
    }

    // 덧셈연산자를 오버로딩하여 벡터덧셈을 구현한다
    CVector operator + (CVector& v) {
    	return CVector(x+v.x, y+v.y);
    }

    void printInfo(){
    	wcout << "x=" << x << ", y=" << y << endl;
    }
};

int main()
{
    setlocale(LC_ALL,"");
    wcout << L"C++ 연산자 오버로딩" << endl;

    CVector v1(1,2);
    CVector v2(3,4);

    v1.printInfo();
    v2.printInfo();

    CVector v3 = v1+v2;  // 연산자를 이용한 벡터 덧셈
    v3.printInfo();

    return 0;
}


다양한 연산자 오버로딩 형식

오버로딩 가능한 연산자의 수는 상당히 많은데, 위에서 해본 덧셈 연산자(+)는 이항연산자로 두개의 객체를 서로 더할 때 obj1 + obj2 형식으로 표현되므로 +기호 우측에 오는 객체를 연산자 함수의 파라미터로 선언하여 전달해주면 되는 것이다. 이와 같이 2항 연산자는 연산자 함수의 파라미터를 쉽게 결정할 수 있다. 그러나 모든 연산자들이 이렇게 분명하게 파라미터가 결정되는 것은 아니다. 예를 들어, 단항 연산자인 후증가 연산자(obj++)를 오버로드하려면 연산자 함수의 파라미터는 어떻게 선언해야 하는가? 또 전증가 연산자(++)를 오버로드할 때 연산자 함수의 파라미터나 그 형식은 어떻게 될까?

연산자를 오버로딩할 때는 해당 연산자에 따라서 다음과 같은 규칙을 따라야 한다. 

아래의 표에서 a : A 클래스의 객체, b : B 클래스의 객체, c : C클래스의 객체를 각각 의미하고 @기호는 연산자를 나타낸다

연산자 위치 

 연산자 종류

 멤버 함수

전역 함수 

 @a  + - * & ! ~ ++ --  A::operator@()  operator@(A)
 a@  ++ --  A::operator@(int)  operator@(A,int)
 a@b  + - * / % ^ & | < > == != <= >= << >> && || ,  A::operator@ (B)  operator@(A,B)
 a@b  = += -= *= /= %= ^= &= |= <<= >>= []  A::operator@ (B)  
 a(b, c...)  ()  A::operator() (B, C...)  
 a->x  ->  A::operator->()