[열혈 C++] Chap10: 연산자의 오버로딩1
1. 연산자 오버로딩의 이해와 유형
✏️Operator+
#include <iostream>
using namespace std;
class Point
{
private:
int xpos, ypos;
public:
Point(int x=0, int y=0) : xpos(x), ypos(y)
{ }
void ShowPosition() const
{
cout<<'['<<xpos<<", "<<ypos<<']'<<endl;
}
Point operator+(const Point &ref) //operator+라는 이름의 함수
{
Point pos(xpos+ref.xpos, ypos+ref.ypos);
return pos;
}
};
int main(void)
{
Point pos1(3, 4);
Point pos2(10, 20);
Point pos3=pos1.operator+(pos2);
pos1.ShowPosition();
pos2.ShowPosition();
pos3.ShowPosition();
return 0;
}
- 객체끼리 더할 수 있다.
컴파일러는
pos3 = pos1 + pos2
를pos3=pos1.operator+(pos2)
로 해석하여 실행한다.
- C++의 객체지향이 나타나는 부분 중 하나라고 생각된다. 기본 자료형 변수처럼 객체도 완벽히 기본 자료형 데이터처럼 취급할 수 있기 때문에 덧셈, 뺄셈, 나눗셈, 곱셈 등의 연산들을 가능하게 하려 한다.
✏️연산자 오버로딩
- 멤버함수에 의한 연산자 오버로딩 pos1.operator+(pos2)
- 전역함수에 의한 연산자 오버로딩 operator+(pos1, pos2)
#include <iostream>
using namespace std;
class Point
{
private:
int xpos, ypos;
public:
Point(int x=0, int y=0) : xpos(x), ypos(y)
{ }
void ShowPosition() const
{
cout<<'['<<xpos<<", "<<ypos<<']'<<endl;
}
friend Point operator+(const Point &pos1, const Point &pos2);
// friend선언을 해주면 operator+ 함수는 Point클래스의 private에 접근할 수 있게 된다.
};
Point operator+(const Point &pos1, const Point &pos2)
{
Point pos(pos1.xpos+pos2.xpos, pos1.ypos+pos2.ypos);
return pos;
}
int main(void)
{
Point pos1(3, 4);
Point pos2(10, 20);
Point pos3=pos1+pos2;
pos1.ShowPosition();
pos2.ShowPosition();
pos3.ShowPosition();
return 0;
}
- friend 선언을 함으로써 이 클래스는 + 연산에 대해 오버로딩이 되어있다는 정보를 쉽게 확인할 수 있다.
- 객체지향은 전역의 개념이 없지만 c++은 C의 구현이 가능하기 때문에 전역의 개념이 남아있다. 그러나 멤버함수를 기반으로 연산자 오버로딩을 하는 것이 좋다.
✏️오버로딩이 가능한 연산자
- 멤버함수 기반으로만 오버로딩이 가능한 연산자
=
대입연산자( )
함수 호출 연산자[]
배열 접근 연산자->
멤버 접근을 위한 포인터 연산자
✏️주의사항
- 본래 의도를 벗어난 형태로 오버로딩하는 것은 좋지 않다
pos1+pos2
를 보고 우리가 기대하는 것은 pos1과 pos2를 더해 그 결과로 객체를 만들어 반환하는 것이다.- 그러나 생뚱맞게 pos1을 pos2만큼 증가시킨다거나, 연산 결과가 pos2에 저장된다면 곤란하다
-
매개변수 디폴트 값 설정이 불가능하다
- 연산자 순수 기능까지 뺏을 순 없다
- operator+ 인데 하는 연산은 곱셈이라던지, 여기서 이러면 안된다.
😀 함수가 오버로딩 되고, 컴파일러는 인자로 호출을 구분한다. 이와 유사하게, 연산자가 오버로딩 되면, 피연산자의 종류에 따라 연산의 방법이 바뀐다.
2. 단항 연산자 오버로딩
++pos
=pos.operator++()
(멤버함수),operator++(pos)
(전역함수)
#include <iostream>
using namespace std;
class Point
{
private:
int xpos, ypos;
public:
Point(int x=0, int y=0) : xpos(x), ypos(y)
{ }
void ShowPosition() const
{
cout<<'['<<xpos<<", "<<ypos<<']'<<endl;
}
Point& operator++() // this는 객체자신의 포인터 값을 의미하므로, *this는 객체 자신을 의미한다
{ // 반환형이 참조형으로 선언되었으므로 객체 자신을 참조할 수 있는 참조값을 반환한다
xpos+=1;
ypos+=1;
return *this;
}
friend Point& operator--(Point &ref);
};
Point& operator--(Point &ref)
{
ref.xpos-=1;
ref.ypos-=1;
return ref;
}
int main(void)
{
Point pos(1, 2);
++pos;
pos.ShowPosition();
--pos;
pos.ShowPosition();
++(++pos); //먼저 ++(pos.operator++())로 해석된다. 이어서 (참조값).operator++()로 해석된다
pos.ShowPosition();
--(--pos); //--(operator--(pos))로 해석된 후 이어서 operator--(참조값)으로 해석된다
pos.ShowPosition();
return 0;
}
- 참조값을 반환함으로써 ++(++pos)와 같은 연속적인 연산이 가능해진다
✏️전위증가 후위증가 구분
++pos
=pos.operator++()
pos++
=pos.operator++(int) //후위
--pos
=pos.operator--()
pos--
=pos.operator--(int) //후위
- int형 인자를 전달한다는 뜻이 아니라 구분을 위해 써줌
#include <iostream>
using namespace std;
class Point
{
private:
int xpos, ypos;
public:
Point(int x=0, int y=0) : xpos(x), ypos(y)
{ }
void ShowPosition() const
{
cout<<'['<<xpos<<", "<<ypos<<']'<<endl;
}
Point& operator++() //전위 증가
{
xpos+=1;
ypos+=1;
return *this;
}
const Point operator++(int) //후위 증가
{
const Point retobj(xpos, ypos); // const Point retobj(*this);
xpos+=1;
ypos+=1;
return retobj;
}
friend Point& operator--(Point &ref);
friend const Point operator--(Point &ref, int);
};
//전역함수
Point& operator--(Point &ref) //전위 감소
{
ref.xpos-=1;
ref.ypos-=1;
return ref;
}
const Point operator--(Point &ref, int) //후위 감소
{
const Point retobj(ref);
ref.xpos-=1;
ref.ypos-=1;
return retobj;
}
int main(void)
{
Point pos(3, 5);
Point cpy;
cpy=pos--;
cpy.ShowPosition();
pos.ShowPosition();
cpy=pos++;
cpy.ShowPosition();
pos.ShowPosition();
return 0;
}
- 값을 증가 시키기 전에 객체를 복사해놓고 값을 증가시켜준다. 그러면 값은 증가되지만 복사본을 반환하면서 후위 증가의 효과를 낼 수 있다. 이때 복사본은 변경되면 안되므로 const선언을 해준다.
✏️반환형에서의 const선언과 const객체
const Point operator--(Point &ref, int)
{
const Point retobj(ref); //함수 내에서 retobj의 변경을 막겠다
ref.xpos-=1;
ref.ypos-=1;
return retobj;
}
- 반환형이 const로 선언된 의미
operator– 함수의 반환으로 인해서 생성되는 임시객체를 const 객체로 생성하겠다!
const Point pos(3,4)
이것이 const 객체이다. 상수로 취급하고 객체에 저장된 값을 변경하지 못하게 한다. 따라서 const로 선언되지 않은 함수를 호출 할 수 없다. 그리고 이러한 객체를 참조할 때도, 참조자에 const선언을 해주어야 한다.
즉, const로 선언된 후위 증가 함수는 반환되는 참조값(임시객체)도 변경될 수 없기때문에, (pos–)–이런 연산은 컴파일 에러가 난다.
3. 교환법칙
-
A + B = B + A
-
마찬가지로
cpy = 3 * pos
와cpy = pos * 3
둘다 연산이 가능하게 해주려면 전역함수를 기반으로 연산자 오버로딩을 해준다
#include <iostream>
using namespace std;
class Point
{
private:
int xpos, ypos;
public:
Point(int x=0, int y=0) : xpos(x), ypos(y)
{ }
void ShowPosition() const
{
cout<<'['<<xpos<<", "<<ypos<<']'<<endl;
}
Point operator*(int times)
{
Point pos(xpos*times, ypos*times);
return pos;
}
friend Point operator*(int times, Point &ref);
};
Point operator*(int times, Point &ref){
return ref*times;
}
int main(void)
{
Point pos(1, 2);
Point cpy;
cpy=pos*3; //cpy = pos.operator*(3);
cpy.ShowPosition();
cpy=3*pos*2; // cpy = (operator*(3, pos)).operator*(2);
cpy.ShowPosition();
return 0;
}