'연산자 오버로딩'에 해당되는 글 4건

  1. 2013.05.02 연산자 오버로딩
  2. 2012.10.04 포인터 연산자 오버로딩
  3. 2012.08.27 단항 연산자의 오버로딩
  4. 2012.08.25 연산자 오버로딩 (Operator Overloading)


파이썬에서는 다른 자료형에 적용되는 모든 연산을 사용자가 정의하는 클래스에서 동작할 수 있도록 구현할 수 있다.


 ex) 문자열 나누기


__div__ 메소드는 나누기 연산자 '/'가 사용되었을 때 자동으로 호출된다.


파이썬은 객체 m이 __div__ 메소드를 가지고 있는가를 동적으로 검사한다. 만일 메소드를 가지고 있다면 'b'를 인수로 __div__ 메소드를 호출한다.

즉, 다음과 같이 메소드를 호출한다.

# m / 'b' 와 같은 코드

m.__dive__('b')


만일 나눗셈의 피연산자 순서를 바꾸면 어떻게 될까?

예외가 발생한다.!! 객체가 오른쪽으로 가면, __rdiv__ 메소드를 호출하기 때문이다.

그래서 __rdiv__를 정의해야 한다.

예제에서는 __div__와 __rdiv__ 메소드 내부가 동일하다. 

따라서, 다음과 같이 간단히 메소드를 정의할 수 있다.

__rdiv__ = __div__


수치 연산자 메소드 


※ 피연산자가 바뀐 경우의 수치 연산자 메소드 이름앞에 'r' 만 추가해주면 된다.

ex) __rshift__     =>     __rrshift__


※ 확장 산술 연산자 메소드 (+=, -=, *= 등등...)

메소드 이름앞에 'i' 만 추가해주면 된다.

ex) __add__     =>     __iadd__


 Method 

 Operator

 __add__(self, other) 

 +

 __sub__(self, other) 

 - 

 __mul__(self, other) 

 * 

 __div__(self, other) 

 / 

 __truediv__(self, other) 

 from __future__ import division이 실행되었다면, '/' 연산자에 의해 이 메소드 호출

 __floordiv__(self, other) 

 // 

 __mod__(self, other) 

 __divmod__(self, other) 

 divmod() 

 __pow__(self, other[, module]) 

 pow(), ** 

 __lshift__(self, other) 만

 << 

 __rshift__(self, other) 

 >> 

 __and__(self, other) 

 & 

 __xor__(self, other) 

 ^ 

 __or__(self, other) 

 | 



수치 단항 연산자 메소드

 Method

 Operator 

 __neg__(self) 

 - 

 __pos__(self) 

 + 

 __abs__(self) 

 abs() 

 __invert__(self) 

 ~ 비트 반전 (0은 1로, 1은 0으로) 


기타 형 변환 메소드

다음의 연산자들은 객체를 인수로 하여 함수 형태로 호출된다. 그에 따른 적절한 값을 리턴해 줘야 한다. 

예를 들면, int(a) 에 의해서 a.int() 가 호출된다.

.__oct__, .__hex__ 인 경우는 문자열을 리턴한다.

Method

 Opereator 

 __complex__(self) 

 complex()   

 __int__(self) 

 int() 

 __long__(self) 

 long() 

 __float__(self)  

 float() 

 __oct__(self) 

 oct() 

 __hex__(self) 

 hex() 



'Programming > Python' 카테고리의 다른 글

class 메소드 관련  (0) 2013.05.08
협동적 메소드와 super()  (0) 2013.05.07
생성자와 소멸자  (0) 2013.05.02
class member, instance member  (0) 2013.05.02
정적 메소드, 클래스 메소드, 장식자  (0) 2013.05.01
Posted by scii
:

포인터를 기반으로 하는 연산자 모두를 포인터 연산자라 한다. 그런데 그 중에서도 대표적인 포인터 연산자 둘은 다음과 같다.


->  포인터가 가리키는 객체의 멤버에 접근.

*  포인터가 가리키는 객체에 접근.


※ 둘 다 피 연산자가 하나인 단항 연산자의 형태로 오버로딩 된다는 특징만 기억하면 된다.




19행: 이 함수는 객체자신의 주소 값을 반환하도록 -> 연산자를 오버로딩 하고 있다. 

-> 연산자를 다른 형태로 오버로딩 하는 것도 가능하지만, 이 연산자의 오버로딩을 허용하는 이유는,

주소 값의 반환이 목적이기 때문에 다른 형태로는 오버로딩 하지 않는 것이 좋다.


38행: 19행의 멤버함수가 반환하는 것은 주소 값이니, ShowData 함수의 호출은 문법적으로 성립하지 않는다.  

ex) num.operator->() ShowData();

때문에 반환되는 주소 값을 대상으로 적절한 연산이 가능하도록 -> 연산자를 하나 더 추가하여 진행해야 한다.


operator-> 함수가 반환하는 것이 주소 값이니, 이를 통한 멤버의 접근을 위해서 -> 연산자를 하나 더 추가시켜서 해석한 것이다.

Posted by scii
:

피연산자가 두 개인 이항 연산자와 피연산자가 한 개인 단항 연산자의 가장 큰 차이점은 피연산자의 개수이다.

그리고 이에 따른 연산자 오버로딩의 차이점은 매개변수의 개수에서 발견된다.


증가, 감소 연산자의 오버로딩



# this는 객체자신의 포인터 값을 의미하므로, *this는 객체 자신을 의미한다.


# ++(++pos); 는 소괄호 부분이 먼저 다음의 형태로 해석된다.

=> ++(pos.operator++()); 그 다음에 반환이 참조 값이므로 다음의 형태가 된다.

=> (pos의 참조 값).operator++();

# 그런데 pos의 참조 값을 대상으로 하는 연산은 pos 객체를 대상으로 하는 연산이기 때문에 결과적으로 위의 문장이 실행되면서 pos 객체의 멤버변수 값은 다시 1씩 증가한다.


# 정리하면, 위 예제의 operator++ 함수에서 객체 자신을 참조할 수 있는 참조 값을 반환하는 이유는 일반적인 ++ 연산자와 같은 형태의 연산이 가능하게 하기 위함이다.




Posted by scii
:

# C++ 에서는 함수뿐만 아니라 연산자도 오버로딩이 가능하다.

# 연산자 오버로딩은 C++ 을 이해하는데 매우 중요한 요소이다.


# 함수가 오버로딩 되면, 오버로딩 된 수만큼 다양한 기능을 제공하게 된다. 

즉, 이름은 하나이지만 기능은 여러 가지가 되는 셈이다.

마찬가지로 연산자의 오버로딩을 통해서, 기존에 존재하던 연산자의 기본 기능 이외에 다른 기능을 추가할 수 있다.




※ + 뿐만 아니라.. -, /, *, %로도 해보자~!!!


"p1 + p2 가 p1.operator+(p2) 의 다른 표현이다. "

즉, 약속된 변환의 규칙이다. 'operator' 키워드와 '연산자' 를 묶어서 함수의 이름을 정의하면, 함수의 이름을 이용한 함수의 호출뿐만 아니라, 연산자를 이용한 함수의 호출도 허용해준다. 


※ 연산자 오버로딩이란 C++ 이 우리에게 제시하는 하나의 약속에 지나지 않는다.


연산자를 오버로딩 한 함수도 const 로 선언이 가능하다. 


# operator+ 함수는 const로 선언을 하는 것이다 좋다. 덧셈연산이라는 것이 원래 연산의 대상이 되는 피연산자의 값을 변경하는 게 아니고, 새로운 연산의 결과를 만들어내는 것이기 때문이다.



연산자를 오버로딩 하는 두 가지 방법


1. 멤버함수에 의한 연산자 오버로딩.

2. 전역함수에 의한 연산자 오버로딩.


+ 연산자는 전역함수를 이용해서도 오버로딩이 가능하다. 그리고 이렇게 전역함수를 이용해서 오버로딩을 하면 p1 + p2 는 다음과 같이 해석이 된다.

operator+(p1, p2);

즉, 어떻게 오버로딩을 했느냐에 따라 해석하는 방법이 두 가지로 나뉘게 된다. 


※ 참고로, 동일한 자료형을 대상으로 + 연산자를 전역함수 기반으로, 그리고 멤버함수 기반으로 동시에 오버로딩 할 경우, 멤버함수 기반으로 오버로딩 된 함수가 전역함수 기반으로 오버로딩 된 함수보다 우선시되어 호출된다. 

단, 일부 컴파일러는 이러한 상황에서 컴파일 에러를 발생시키기도 하니, 이러한 상황은 가급적 만들지 않는 게 좋다. 



전역함수 기반의 연산자 오버로딩에 대한 일반적인 모델 예 &

friend 선언이 적절히 사용된 예


Point 클래스는 + 연산에 대해서 연산자 오버로딩이 되어 있구나를 생각할 수 있다.

이렇듯 Point 클래스에 삽입된 friend 선언으로 인해서, 이 클래스는 + 연산에 대해 오버로딩이 되어 있다는 정보를 쉽게 확인할 수 있다.


※ 객체지향에는 "전역(Global)"에 대한 개념이 존재하지 않는다. 다만 C++은 C 스타일의 코드구현이 가능한 언어이기 때문에 전역에 대한 개념이 여전히 존재한다. 

따라서 특별한 경우가 아니면, 멤버함수를 기반으로 연산자를 오버로딩 하는 게 낫다.


#include <iostream>

using namespace std;


class Point

{

private:

int xpos, ypos;


public:

Point(int x=0, int y=0)

:xpos(x), ypos(y)

{ }


void Show() const

{

cout<<xpos<<", "<<ypos<<endl;

}


Point& operator+=(const Point &ref)

{

xpos += ref.xpos; 

ypos += ref.ypos;

return *this;

}


Point& operator-=(const Point &ref)

{

xpos -= ref.xpos; 

ypos -= ref.ypos;

return *this;

}


friend Point operator-(const Point &, const Point &);

friend bool operator==(const Point &, const Point &);

friend bool operator!=(const Point &, const Point &);

};


Point operator-(const Point &ref1, const Point &ref2)

{

Point pos(ref1.xpos-ref2.xpos, ref1.ypos-ref2.ypos);

return pos;

}


bool operator==(const Point &ref1, const Point &ref2)

{

if(ref1.xpos == ref2.xpos && ref1.ypos == ref2.ypos)

return true;

else 

return false;

}


bool operator!=(const Point &ref1, const Point &ref2)

{

return !(ref1==ref2); //오버로딩 된 == 연산자를 호출하고 있다.

}


int main(void)

{

Point p1(20, 30);

Point p2(5, 7);

Point p3(5, 7);


(p1-p2).Show(); //객체를 반환하므로 함수호출 가능.

(p2+=p3).Show(); //반환형이 참조형이므로 함수호출 가능.


if(p2==p3) //operator==(p1, p2);랑 100% 동일한 문장.

cout<<"같음"<<endl;

else

cout<<"다름"<<endl;


(p2-p3).Show(); //반환형이 참조형이므로 함수호출 가능.


if(operator!=(p1, p2))         //p1 != p2; 랑 100% 동일한 문장.

cout<<"다름"<<endl;

else

cout<<"같음"<<endl;


return 0;

}





오버로딩이 불가능한 연산자의 종류


# C++ 의 모든 연산자들이 오버로딩의 대상이 되는 것은 아니다.

- 오버로딩이 불가능한 연산자들 -


.                                          멤버 접근 연산자

.*                                        멤버포인터 연산자

::                                        범위 지정 연산자

?:                                        조건 연산자

sizeof                                  바이트 단위 크기 계산

typeid                                  RTTI 관련 연산자

static_cast                           형변환 연산자

dynamic_cast                       형변환 연산자

const_cast                           형변환 연산자

reinterpret_cast                     형변환 연산자


이들 연산자에 대해서 오버로딩을 제한하는 이유는 C++ 의 문법규칙을 보존하기 위함이다.


※ 참고로, 위의 연산자들을 오버로딩 해야만 하는 상황이 딱히 존재하지 않다.



멤버함수 기반으로만 오버로딩이 가능한 연산자


- 멤버함수 기반으로만 오버로딩이 가능한 연산자들 - 


=                                        대입 연산자

()                                       함수 호출 연산자

[]                                      배열 접근 연산자(인덱스 연산자)

->                                      멤버 접근을 위한 포인터 연산자


이들은 객체를 대상으로 진행해야 의미가 통하는 연산자들이기 때문에, 멤버함수 기반으로만 연산자의 오버로딩을 허용한다.




연산자를 오버로딩 하는데 있어서의 주의사항


1. 본래의 의도를 벗어난 형태의 연산자 오버로딩은 좋지 않다.


# 연산자를 오버로딩 할 때에는 연산자의 본래 의도를 가급적 충실히 반영해서, 연산자의 오버로딩으로 인해서 발생하는 혼란스러운 부분을 최소화해야 한다.


2. 연산자의 우선순위와 결합성은 바뀌지 않는다.


# 연산자가 갖는 연산의 기능은 오버로딩 되어도, 연산자가 지니는 우선순위와 결합성은 그대로 따르게 되어 있다.


3. 매개변수의 디폴트 값 설정이 불가능하다.


# 연산자 오버로딩의 특성상 매개변수의 디폴트 값이 설정되면, 함수의 호출관계가 매우 불분명해진다. 따라서 매개변수의 디폴트 값 설정은 허용되지 않는다.


4. 연산자의 순수 기능까지 빼앗을 수는 없다.

int operator+(const int num1, const int num2)    //정의 불가능한 함수

{

 return num1 * num2;

}

# int 형 데이터의 + 연산은 이미 그 의미가 정해져 있다. 이것을 변경하는, 위와 같은 함수의 정의는 허용되지 않는다. 

이처럼 연산자의 기본 기능을 변경하는 형태의 연산자 오버로딩은 허용되지 않는다.




"연산자 오버로딩" 이라고 불리는 이유.


int num = 5+5;

Point p3 = p1 + p2;    //p1과 p2는 Point형 객체


함수가 오버로딩 되면 전달되는 인자에 따라서 호출되는 함수가 달라진다. 이와 유사하게, 위의 두 문장에서 보이듯이, 연산자가 오버로딩 되면, 피연산자의 종류에 따라서 연산의 방식이 달라진다. 그래서 연산자 오버로딩이라 불리는 것이다.

Posted by scii
: