[문과 코린이의 IT 기록장] C,C++ - 연산자 오버로딩 1 :
( operator 함수 사용법, operator+ 함수, 연산자를 오버로딩 하는데 있어서의 주의사항, 연산자 오버로딩이라고 이름이 붙은 이유는?, operator * 함수 )
- C언어에서는 불가능하지만, C++에서는 사용자 정의 연산자를 사용할 수 있다.
- ::(범위 지정), .(맴버 지정), .*(맴버 포인터로 맴버 지정)을 제외한 모든 연산자들은 사용할 수 있다.
ex ) 1. 산술 연산자 (+, -, *) 2. 축약형 연산자 (+=, -=) 3. 비교 연산자 (>=, ==) 4. 논리 연산자 (&&, ||) 5. 맴버 선택 연산자 (->, * : 역참조 연산자 ex. 포인터 *p와 같은 것) 6. 증감 연산자 (++, --) 7. 함수 호출 연산자 ( () ) 8. 배열 연산자 ( [] ) |
이러한 기본 연산자들을, 직접 사용자가 설계한 클래스에 맞게 정의하는 것을, 연산자 오버로딩이라고 부른다.
즉, 기존에 존재하던 연산자의 기본 기능에, 내가 원하는 다른 기능들을 추가하는 것이다.
1. operator 함수 사용법
(반환형) operator(연산자) (연산자가 받는 인자)
2. operator+ 함수
1) operator+함수, 오버로딩 된 연산자의 해석
#include<iostream>
using namespace std;
class Point { // Point 클래스
private:
int xpos, ypos;
public:
Point(int x=0,int y=0):xpos(x),ypos(y){ } // 생성자 (x=0, y=0 초기화)
void ShowPosition() const { // xpos, ypos 출력 함수 (const 맴버함수로 맴버변수 변경 불가능)
cout << '[' << xpos << ", " << ypos << ']' << endl;
}
Point operator+(const Point& ref) { // operator+라는이름의함수
Point pos(xpos + ref.xpos, ypos + ref.ypos); // 새로운 객체 생성
return pos; // pos라는 객체를 복사해서 임시객체 형태로 반환한다.
}
};
int main() {
Point pos1(3, 4);
Point pos2(10, 20);
Point pos3 = pos1.operator+(pos2);
// pos1 + pos2 = pos1.operator+(pos2)
// pos1객체의, 맴버함수 operator+를 호출하면서 인자로 pos2 객체를 전달하고 있다. 이것이 반환되어, pos3을 초기화하게 된다. (이 과정에서 복사생성자가 호출된다.)
pos1.ShowPosition();
pos2.ShowPosition();
pos3.ShowPosition();
return 0;
}
[ 오버로딩 된 연산자의 해석 ]
- 원래는 객체와 객체를 더하는 것이 불가능하다. 그러나, operator+ 내부에 사용자가 임의로 저장해놓았기 때문에 이 예제에서는 가능하다.
2) 연산자를 오버로딩 하는 두 가지 방법
[ 연산자를 오버로딩하는 방법 ] pos1 + pos2; a. 맴버함수로 오버로딩 된 경우 : pos1.operator+(pos2) b. 전역함수로 오버로딩 된 경우 : operator+(pos1, pos2) - 맴버함수로 오버로딩 되는 것이 우선호출된다. - 객체지향에서는 전역(global)이라는 개념이 존재하지 않는다. 따라서 보통 맴버함수로 구현하는 것이 좋다. |
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 ShowPosition() const { // xpos, ypos 출력 (const 맴버함수로 맴버변수 변경 불가능)
cout << '[' << xpos << ", " << ypos << ']' << endl;
}
friend Point operator+(const Point& pos1, const Point& pos2); // 전역함수
// 인자가 2개가 있다. 즉, 2개의 피연산자(피연산자 + 피연산자)를 알아야 하므로, 전역함수로 오버로딩되어야 한다는 점을 파악할 수 있다.
cf ) 맴버함수로 오버로딩되게 하고자 한다면, 피연산자 1개(객체 + 피연산자)로 이루어져야 한다.
};
Point operator+(const Point& pos1, const Point& pos2) { // 전역함수
// Point operator+함수는, Point 클래스의 맴버에 접근할 수 있다.(Point클래스 내부에 friend선언이 되어있기 때문에)
Point pos(pos1.xpos + pos2.xpos, pos1.ypos + pos2.ypos);
return pos;
}
int main() {
Point pos1(3, 4);
Point pos2(10, 20);
Point pos3 = pos1 + pos2;
// +연산자가 전역함수의 형태로 오버로딩 되었으므로, operator+(pos1,pos2)로 해석이 된다.
pos1.ShowPosition();
pos2.ShowPosition();
pos3.ShowPosition();
return 0;
}
3. 연산자를 오버로딩 하는데 있어서의 주의사항
1) 본래의 의도를 벗어난 형태의 연산자 오버로딩은 좋지 않다. ex) 값을 증가시키는, 변화시키는 목적 X
2) 연산자의 우선순위와 결합성은 바뀌지 않는다. ex) 사칙연산 준수
3) 매개변수의 디폴트 값 설정이 불가능하다.
4) 연산자의 순수 기능까지 빼앗을 수 없다. ex) operator+함수 내부에서 *연산 실행하는 경우 문제 발생
4. 연산자 오버로딩이라고 이름이 붙은 이유는?
- 함수가 오버로딩 되면 전달되는 인자에 따라 호출되는 함수가 달라진다.
- 이와 유사하게, 연산자가 오버로딩 되면, 피연산자의 종류에 따라 연산의 방식이 달라진다. 따라서 연산자 오버로딩이라고 불리는 것이다.
5. operator * 함수
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 ShowPosition() const { // xpos, ypos 출력 (const 맴버함수로 맴버변수 변경 불가능)
cout << '[' << xpos << ", " << ypos << ']' << endl;
}
Point operator*(int times) { // operator*함수.
Point pos(xpos * times, ypos * times); // xpos와 ypos에, 입력한 times를 곱해준다.
return pos; // pos 객체를 복사해서, 임시객체로 반환한다.
}
friend Point operator*(int times, Point& ref);
// 교환법칙의 성립을 위한 구현을 위해서 (전역함수의 형태로 곱셈 연산자를 오버로딩 해야한다.)
};
Point operator*(int times, Point& ref) {
return ref * times
}
int main() {
Point pos(1, 2);
Point cpy;
cpy = 3 * pos; // 3*pos는, Point operator*(int times, Point& ref) 이 실행됨
cf ) 3.operator*(pos)로는 해석이 불가능하다.
cpy.ShowPosition();
cpy = 2 * pos * 3;
cpy.ShowPosition();
return 0;
}
* 유의사항 - 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다. - 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다. - 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :) |