기존에 존재하는 +, ++, == 등의 연산자를 프로그래머가 중복 정의가 가능하다.
operator+, operator++ 등으로 오버로딩이 가능한데,
멤버 함수내에 선언할 수도 있고, friend 키워드를 사용해서 전역으로 사용 할 수도 있지만,
멤버 함수에 선언해서 사용하는 것에 대해서 먼저 살펴 보자.
연산자 오버로딩의 사용 예,
#include <iostream>
class Point
{
int x, y;
public:
Point( int _x = 0, int _y = 0 ) : x(_x), y(_y) {}
void ShowPoint(){std::cout << x << " " << y << std::endl;}
int Get_x() const{return x;}
int Get_y() const{return y;}
// Point Class x, y에 동시에 값 증가.
void operator+(int val);
// 인스턴스 A - 인스턴스 B
Point operator -(const Point& p) const;
// x,y값을 1 증가
Point& operator++();
// x,y값을 1 감소
Point& operator--();
};
Point& Point::operator++()
{
x++;
y++;
return *this;
}
Point& Point::operator--()
{
x--;
y--;
return *this;
}
Point operator +(const Point& p1, const Point& p2)
{
Point tmp(p1.Get_x()+p2.Get_x(), p1.Get_y()+p2.Get_y());
return tmp;
}
Point Point::operator -(const Point& p ) const
{
Point tmp(x-p.x, y-p.y);
return tmp;
}
void Point::operator +( int val)
{
x+=val;
y+=val;
}
void main()
{
Point p1(1, 2);
Point p2(3, 3);
p1.ShowPoint();
// p1.operator++(); 과 같다.
p1++; // operator ++ 의 사용
p1.ShowPoint();
// p1.operator++(); 과 같다.
++p1; // operator ++ 의 사용
p1.ShowPoint();
// p1.operator+(10); 과 같다.
p1+10;
p1.ShowPoint();
// p1 = p1.operator-(p2); 와 같다.
p1=p1-p2;
p1.ShowPoint();
// p1 = p1.operator+(p2); 와 같다.
p1=p1+p2;
p1.ShowPoint();
}
// 결과
// 1 2
// 2 3
// 3 4
// 13 14
// 10 11
// 13 14
operator++을 보면 인자를 받지 않고 return *this를 해주고 있다.
설명이 필요 없을지도 모르겠지만, 멤버 함수 내에 선언되어 있는 존재이기 때문에 자신의x,y를 증가 시키고
*this를 넘겨 줌으로써 값을 받아 온다.
만약 *this를 쓰지 않고 return tmp; 같은 식으로 하게 된다면,
++(++p);의 경우,
++p(1증가);
다시 1 증가
총 2증가를 하게 되지만,
복사본을 사용한다면,
++(++p);의 경우,
++p(p의 복사본,1증가);
p의 값은 1 증가,
p의 복사본이 1증가,
결론은 1밖에 증가가 되지 않는다.
다음은 전역에 선언한 operator에 대해서 살펴 보자.
#include <iostream>
class Point
{
int x, y;
public:
Point( int _x=0, int _y=0 ) : x(_x), y(_y){}
// 외부에서 내부 데이터를 건들기 때문에
// friend 선언을 해주어야 하는 단점이 존재.
// 정보 은닉에 대한 문제가 생길 수 있다.
friend std::ostream& operator << (std::ostream& os, const Point& p);
};
// 문자열을 저장하는 ostream!
std::ostream& operator << (std::ostream& os, const Point& p)
{
os << "[" << p.x << ", " << p.y << "]" << std::endl;
// 원하는 식으로 출력 형태를 정하고
// os를 return 해 주고 있다.
return os;
}
void main()
{
Point p(1,3);
// p를 출력 하고 있다.
std::cout << p;
}
// 결과
// [1, 3]
이 처럼 사용이 가능하지만,
가능하면 객체지향적인 코딩을 생각해서 멤버 변수에 연산자 오버로딩을 하는 것이 옳은 방법이라고 생각한다.
그리고 ++p와 p++에 대해서 차이점이 존재한다.
실제로는 선연산 , 후연산이지만 operator를 사용해서는 그 의미가 불문명 해지게 된다.
이럴 경우를 대비해
Point& operator++(); ++p와 같고,
Point& operator++(int); p++과 같다.
예제를 잠깐 보자.
// 차이가 있는 부분만 확인.
Point Point::operator++(int)
{
std::cout << "p++" << std::endl;
Point temp(x, y);
x++;
y++;
return temp;
}
Point& Point::operator++()
{
std::cout << "++p" << std::endl;
x++;
y++;
return *this;
}
void main()
{
Point p1(1, 2);
(p1++).ShowPoint(); // 1, 2 출력
p1.ShowPoint(); // 2, 3 출력
Point p2(1, 2);
(++p2).ShowPoint(); // 2, 3 출력
p2.ShowPoint(); // 2, 3 출력
}
// 결과
// p++
// 1 2
// 2 3
// ++p
// 2 3
// 2 3
하지만 이 방법에는 큰 문제점이 존재하는데,
위에서 언급 하였던
(++(++p))와
((p++)++)우리가 예상 했던 결과가 나오지 않는 다는 것이다.
void main()
{
Point p1(1, 2);
// 후연산
// 복사로 인한 연산이기 때문에 중복이 아니된다.
// Point Point::operator++(int)
((p1++)++).ShowPoint();
p1.ShowPoint();
Point p2(1, 2);
// 선연산 this를 사용하기 때문에 중복이 가능하다.
// Point& Point::operator++()
(++(++p2)).ShowPoint();
p2.ShowPoint();
}
// 결과
// p++
// p++
// 1 2
// 2 3
// ++p
// ++p
// 3 4
// 3 4
연산자 오버로딩이 안되는 경우도 존재 하는데,
. .* :: ?: sizeof 는 할 수 없다고 한다.
마지막으로 주의점에 대해서 이야기 하겠다.
1. p+3;
이 부분은 p의 값들에 3을 더할 수도 있겠고,
또는 p에 3을 더해서 새로운 객체를 생성해서 리턴하는 것일 수도 있다.
즉 저 부분만 봐서는 어떤 일을 하는지 알 수 없다는 것이다. (직접 operator+함수를 보기 전까진.)
2. p+3*3;
연산자는 계산시에 우선 순위가 존재 한다.
만약 저렇게 연산자 오버로딩을 하였다고 하더라도, 순서는 *3 이후 +3을 하게 된다는 것이다.
3. void operator+(int val=10);
이 처럼 연산자 오버로딩은 디폴트 매개 변수를 가질 수 없다. 만약 가능하다면,
p+; 이런 구문이 나올지도 모르고 이는 분명 프로그래머가 원하는 방향은 아닐 것이다.
4. int operator+(int a, int b){ return a+b+3 }
이것 또한 안된다. 연산자 오버로딩을 하지 않은 (int)3+(int)4라는 기본 연산에 대해서 다시 오버로딩을 하는 것은 불가능하다.
마지막으로 전역으로 선언된 연산자 오버로딩과, 멤버 함수로 선언된 연산자 오버로딩은 조금의 불러오는 차이가 존재 하는데,
++p -> 멤버 함수의 경우( p.operator++(); ) 와 같고,
++p -> 전역 함수의 경우( operator++(p); ) 와 같다는 걸 기억해 두자.
'프로그래밍 > C++' 카테고리의 다른 글
임시 객체 생성 (0) | 2010.04.04 |
---|---|
연산자 오버로딩(operator overlonding)(2) (0) | 2010.04.04 |
가상 복사 생성자 (0) | 2010.04.03 |
virtual(가상) (0) | 2010.04.03 |
함수 오버라이딩(overriding) (0) | 2010.04.03 |