13. 연산자 오버로딩(Operator Overloading)
1. 키워드
- 연산자 오버로딩(Operator Overloading)
- 멤버 연산자 함수(Operator Function)와
operator
키워드 - 전역 연산자 함수와
friend
키워드
2. 연산자 오버로딩
- C++에서는 함수 오버로딩과 마찬가지 개념으로 연산자에 대해서도 오버로딩을 제공한다.
- 함수 오버로딩이란 같은 일을 처리하는 함수를 매개변수의 형식을 조금씩 달리하여, 하나의 이름으로 작성할 수 있게 해주는 것이다.
- C++은 이러한 오버로딩의 개념을 연산자까지 확대하여, 하나의 연산자를 여러 의미로 사용할 수 있게 해준다.
- C++에서는 연산자 오버로딩을 사용자 정의 타입까지 확장할 수 있으며, 클래스도 하나의 타입임을 확실히 보여준다.
1) 연산자 함수
- C++에서는 연산자를 오버로딩하기 위해서 연산자 함수라는 것을 사용한다.
- 연산자 함수의 문법은 다음과 같다.
- 연산자 함수는
operator
키워드를 사용하여 연산자를 오버로딩한다. - 오버로딩할 연산자는 적법한 C++ 연산자여야 하며,
operator
키워드와 공백 없이 연결되어 표시해야 한다. - 이러한 연산자 함수의 장점은 복잡한 함수 이름 대신에 간편하게 연산자를 사용할 수 있다는 점이다.
- 다음의 코드는 두 지점의 중간 지점의 좌표를 구하는 것이다.
#include <iostream>
using namespace std;
class Position {
private:
double x_;
double y_;
public:
Position(double x, double y); // 생성자
void Display();
Position operator-(const Position &other); // - 연산자 함수
};
int main(void) {
Position pos1 = Position(3.3, 12.5);
Position pos2 = Position(-14.4, 7.8);
Position pos3 = pos1 - pos2;
pos3.Display();
return 0;
}
Position::Position(double x, double y) {
x_ = x;
y_ = y;
}
Position Position::operator-(const Position &other) {
return Position((x_ + other.x_) / 2, (y_ + other.y_) / 2);
}
void Position::Display() {
cout << "두 지점의 중간 지점의 좌표는 x좌표가 " << this->x_
<< "이고, y좌표가 " << this->y_ << "입니다." << endl;
}
// 두 지점의 중간 지점의 좌표는 x좌표가 -5.55이고, y좌표가 10.15입니다.
- 위의 코드에서는
-
(뺄셈 연산자)를 두Position
객체 사이의 중간 좌표를 구하는 연산으로 오버로딩하여 사용하고 있다. - 만약 이러한 연산자 오버로딩을 하지 않으면 C++ 컴파일러는 두
Position
객체의 뺄셈은 지원하지 않는다며 오류를 발생시킬 것이다.
2) 연산자 함수의 정의 방법
- C++에서 연산자 함수를 정의하는 방법은 다음과 같이 두 가지 방법이 있다.
1] 클래스의 멤버 함수로 정의하는 방법
2] 전역 함수로 정의하는 방법
- 이 두 방법의 차이는 인수의 개수뿐만 아니라
private
멤버에 대한 접근 여부도 있다. - 연산자 함수를 전역 함수로 정의해야 할 경우,
private
멤버에 대한 접근을 위해 C++에서 제공하는 프렌드 함수를 사용할 수 있다.
- 다음의 코드는 앞선
-
의 오버로딩 코드를 전역 함수로 정의한 것이다.
#include <iostream>
using namespace std;
class Position {
private:
double x_;
double y_;
public:
Position(double x, double y); // 생성자
void Display();
friend Position operator-(const Position &pos1, const Position &pos2); // - 연산자 함수
};
int main(void) {
Position pos1 = Position(3.3, 12.5);
Position pos2 = Position(-14.4, 7.8);
Position pos3 = pos1 - pos2;
pos3.Display();
return 0;
}
Position::Position(double x, double y) {
x_ = x;
y_ = y;
}
Position operator-(const Position &pos1, const Position &pos2) {
return Position((pos1.x_ + pos2.x_) / 2, (pos1.y_ + pos2.y_) / 2);
}
void Position::Display() {
cout << "두 지점의 중간 지점의 좌표는 x좌표가 " << this->x_
<< "이고, y좌표가 " << this->y_ << "입니다." << endl;
}
// 두 지점의 중간 지점의 좌표는 x좌표가 -5.55이고, y좌표가 10.15입니다.
- 위의 코드에서는
-
를 전역 연산자 함수를 사용하여 오버로딩하고 있다. - 하지만 전역 함수는
private
멤버인x_
와y_
에 접근하지 못하므로,friend
키워드를 사용하여 프렌드 함수로 선언하고 있다.
3. 오버로딩의 제약 사항
- C++에서 연산자를 오버로딩할 때에는 다음과 같은 사항을 지켜야 한다.
1] 전혀 새로운 연산자를 정의할 수는 없다.
- 예를 들어 몫을 나타내기 위한
%%
라는 연산자를 새롭게 정의할 수 없다.
2] 기본 타입을 다루는 연산자의 의미는 재정의할 수 없으며, 따라서 오버로딩된 연산자의 피연산자 중 하나는 반드시 사용자 정의 타입이어야 한다.
- 예를 들어 두 개의
double
타입에 대한+
(덧셈 연산자)가 뺄셈을 수행하도록 오버로딩할 수 없다.
3] 오버로딩된 연산자는 기본 타입을 다루는 경우에 적용되는 피연산자의 수, 우선순위 및 그룹화를 준수해야 한다.
- 예를 들어
/
(나눗셈 연산자)는 이항 연산자이므로 단항 연산자로 오버로딩할 수 없다.
4] 오버로딩된 연산자는 디폴트 인수를 사용할 수 없다.
1) 오버로딩할 수 없는 연산자
- C++에서는 다음과 같은 연산자는 오버로딩할 수 없다.
1] ::
- 범위 지정 연산자
2] .
- 멤버 연산자
3] .*
- 멤버 포인터 연산자
4] ? :
- 삼항 조건 연산자
5] sizeof
- 크기 연산자
6] typeid
- 타입 인식
7] const_cast
- 상수 타입 변환
8] dynamic_cast
- 동적 타입 변환
9] reinterpret_cast
- 재해석 타입 변환
10] static_cast
- 정적 타입 변환
2) 멤버 함수로만 오버로딩할 수 있는 연산자
- C++에서 다음과 같은 연산자는 전역 함수가 아닌 멤버 함수로만 오버로딩할 수 있다.
1] =
- 대입 연산자
2] ()
- 함수 호출
3] []
- 배열 인덱스
4] ->
- 멤버 접근 연산자