클래스 연산자 중복
class CNumber
{
public:
int m_Value;
};
int main()
{
CNumber a;
CNumber b;
a.m_Value = 1;
b.m_Value = 2;
CNumber sum1 = 1 + 2;
CNumber sum1 = a + 2;
CNumber sum1 = 1 + b;
CNumber sum1 = a + b;
}
- 이 코드에서는 '+' 연산자를 통해 CNumber 타입의 객체와 int 타입의 값, 또는 CNumber 타입의 객체끼리 더하려고 시도하고 있다. 😖
- 이때 발생하는 에러의 이유는 C++에서 '+' 연산자는 기본적으로 정수, 실수, 포인터 등의 기본 타입에 대해서만 정의되어 있기 때문이다.❗️
- 따라서 사용자 정의 타입인 CNumber 클래스에 대해서는 '+' 연산자의 동작이 정의되어 있지 않아서 발생하는 에러이다.❗️
- 이 문제를 해결하려면, '+' 연산자를 클래스에 대해 오버로딩(중복 정의)해야 한다.❗️❗️❗️
class CNumber
{
public:
int m_Value;
CNumber(int arg)
{
m_Value = arg;
}
};
int main()
{
CNumber a(1);
CNumber b(2);
a.m_Value = 1;
b.m_Value = 2;
CNumber sum1 = 1 + 2;
CNumber sum1 = a + 2;
CNumber sum1 = 1 + b;
CNumber sum1 = a + b;
}
- 위의 코드에서 '1 + 2' 연산은 정수 두 개를 더하는 연산으로, C++에서 기본적으로 제공하는 기능이다. 따라서 '1 + 2'는 정상적으로 계산되어 결과값 '3'을 반환합니다.
- 그런데 이후에 'CNumber sum1 = 1 + 2;' 코드는 '3'이라는 정수를 CNumber 타입의 객체에 할당하려는 것이다. 이때 에러가 발생하지 않는 이유는 CNumber 클래스에 정수를 인자로 받는 생성자가 정의되어 있기 때문이다.😃
- 즉, 'CNumber sum1 = 1 + 2;' 코드는 사실상 'CNumber sum1 = CNumber(1 + 2);'와 같다. 이후에는 'CNumber sum1 = CNumber(3);'이 되고, 마지막으로 'CNumber sum1(3);'으로 해석된다.
- 따라서 이 코드는 CNumber 클래스의 객체 sum1을 생성하고, 그 객체의 m_Value 멤버 변수에 '3'을 할당하는 것이다.
- 이런 과정을 암시적 변환(implicit conversion) 또는 암시적 형변환(implicit type conversion)이라고 한다.
- C++에서는 특정 조건을 만족하는 경우, 하나의 타입을 다른 타입으로 자동적으로 변환해주는 기능을 제공한다. 여기에서는 정수를 CNumber 타입으로 자동 변환하는 과정이 일어났다.🧐
- 다른 세 가지 경우('a + 2', '1 + b', 'a + b')에서는 '+' 연산자를 CNumber 타입의 객체와 사용하려는 시도가 지만, 이전에 앞전의 코드처럼, '+' 연산자는 C++에서 기본적으로 정수, 실수, 포인터 등의 기본 타입에 대해서만 정의되어 있다. 따라서 CNumber 타입에 대해서는 '+' 연산자의 동작이 정의되어 있지 않아서 에러가 발생한다. 이 문제를 해결하려면, '+' 연산자를 CNumber 클래스에 대해 오버로딩(중복 정의)해야 한다.
연산자 중복이란?
- 같은 연산자에 대해서 피연산자의 타입을 다양하게 정의하는 것이다.
연산자 중복의 특징
- C++에 본래 있는 연산자만 중복 가능하다.
8%%5
8##5
- 피 연산자 타입이 다른 새로운 연산 정의
- 연산자를 새로 중복 정의하려면 ‘객체 + 수’, ‘수 + 객체’, ‘객체 + 객체’, 등을 사용하여 중복 정의한다.❗️❗️❗️
- 연산자는 함수 형태로 구현
- 연산자 중복이란 새로운 연산 처리를 수행하는 함수를 구현하는 것이고 이 함수를 연산자 함수라고 한다.🧐
- 반드시 클래스와 관계를 가짐
- 중복된 연산자는 반드시 피연산자에 객체를 포함한다.
- 연산자 함수는 클래스의 멤버 함수로 구현하는 방법과
- 전역 함수로 구현하고 클래스에 프렌드 함수로 선언하는 방법이 있다.
- 피연산자의 개수를 바꿀 수 없다.
- 이항 연산자인 + 에 대해, 피연산자를 1개 혹은 3개로 중복 선언할 수 없다.
- 연산의 우선 순위 변경 안됨
- 모든 연산자가 중복 가능하지 않음
- 중복 가능한 연산자
| + | - | * | / | % | ^ | & |
|---|
| | ~ | ! | = | < | > |
| -= | *= | /= | %= | ^- | &= | |
| << | >> | > > | < < | == | ! = | > = |
| < = | && | | | | ++ | — |
| - > | [ ] | ( ) | new | delete | new[ ] | delete[ ] |
| . | .* | ::(범위지정 연산자) | ? : (3항 연산자) |
|---|
연산자 함수 구현
- 클래스의 멤버 함수로 구현
- 외부 함수(전역 함수)로 구현
클래스의 멤버 함수로 구현
연산자 함수 작성에 사용할 클래스
class Power {
int kick;
int punch;
public:
Power() {
kick = 0;
punch = 0;
}
Power(int kick, int punch)
{
this->kick = kick;
this->punch = punch;
}
void show() {
cout << "kick = " << kick << ", punch = " << punch << endl;
}
};
+ 연산자
클래스 내부에 선언
Power operator+ (Power op2);
정의
Power Power::operator+(Power op2)
{
Power tmp;
tmp.kick = this->kick + op2.kick;
tmp.punch = this->punch + op2.punch;
return tmp;
}
main
Power a(3, 5);
Power b(4, 6);
Power c;
c = a + b;
a.show();
b.show();
c.show();
- 객체끼리의 연산 즉, 연산자 중복 + 를 계산할 때 기준이 되는 피연산자는 a가 되며 이 객체가 즉 this가 되는 것이다. 그리고 op2가 b가 되는 것이다.
- 리턴 타입은 클래스의 이름으로 하여 c객체가 반환받을 수 있도록 했다.
'==' 연산자 중복
클래스 내부에 선언
bool operator== (Power op2);
정의
bool Power::operator== (Power op2)
{
if (this->kick == op2.kick && this->punch == op2.punch)
{
return true;
}
else
{
return false;
}
}
main
if (a == b)
{
cout << "같음" << endl;
}
else
{
cout << "다름" << endl;
}
- '==' 연산자는 두 객체 a, b가 동일한 값을 가지는지를 확인하는 연산자이다. 이 경우 두 객체의 'kick'과 'punch' 값을 모두 비교한다.❗️
- 기준이 되는 피연산자는 a이며, this를 사용하여 a의 'kick'과 'punch' 값을 접근한다. op2는 b를 나타냄
- 반환 타입은 bool이며, 두 객체의 값이 동일하면 true, 그렇지 않으면 false를 반환한다.
'+= 연산자 중복
클래스 내부에 선언
Power& operator+= (Power op2);
정의
Power& Power::operator+= (Power op2)
{
this->kick += op2.kick;
this->punch += op2.punch;
return *this;
}
main
c = a += b;
a.show();
c.show();
- 'operator+=' 함수는 현재 객체의 'kick'과 'punch' 값에 op2 객체의 해당 값을 더한다. 그리고 결과를 현재 객체에 저장한 후, 현재 객체의 레퍼런스를 반환❗️
- 객체 a가 기준이 되는 피연산자로, this를 사용하여 a의 'kick'과 'punch' 값을 접근한다. op2는 b를 나타냄
- 반환값은 *this로, 이렇게 하면 현재 객체(a)의 레퍼런스를 반환한다. 이는 'a += b' 연산의 결과를 다른 객체(c)에 할당할 수 있게 한다.❗️❗️❗️
'++(전위) 연산자 중복
클래스 내부에 선언
Power& operator++ ();
정의
Power& Power::operator++()
{
this->kick++;
this->punch++;
return *this;
}
main
b = ++a;
a.show();
b.show();
- 전위 '++' 연산자는 'kick'과 'punch' 값을 각각 증가시킨 후, 현재 객체의 레퍼런스를 반환한다.
- 이 경우 a가 기준이 되는 피연산자로, this를 사용하여 a의 'kick'과 'punch' 값을 접근함
- 반환값은 *this로, 이렇게 하면 현재 객체(a)의 레퍼런스를 반환하며, 이는 '++a' 연산의 결과를 다른 객체(b)에 할당할 수 있게한다.❗️❗️❗️
'++(후위) 연산자 중복
클래스 내부에 선언
Power operator++ (int x);
정의
Power Power::operator++ (int x)
{
Power tmp = *this;
this->kick++;
this->punch++;
return tmp;
}
main
b = a++;
a.show();
b.show();
- 후위 '++' 연산자는 'kick'과 'punch' 값을 증가시키기 전의 객체를 반환한다. 이를 위해 임시 객체를 생성하여 반환함❗️❗️❗️
- 이 경우 a가 기준이 되는 피연산자로, this를 사용하여 a의 'kick'과 'punch' 값을 접근함
- 반환값은 tmp로, 이렇게 하면 현재 객체(a)의 복사본을 반환하며, 이는 'a++' 연산의 결과를 다른 객체(b)에 할당할 수 있게한다.❗️❗️❗️
'+ 연산자 중복 (클래스와 정수 간)'
클래스 내부에 선언
Power operator+ (int op2);
정의
Power Power::operator+ (int op2)
{
Power tmp;
tmp.kick = this->kick + op2;
tmp.punch = this->punch + op2;
return tmp;
}
main
b = a + 2;
b.show();
- '+ 연산자 중복'은 Power 객체와 정수를 더하는 연산이다. 이 경우 Power 객체의 'kick'과 'punch' 값에 op2 값을 각각 더한 새로운 객체를 반환한다.❗️❗️❗️
- 여기서 a가 기준이 되는 피연산자로, this를 사용하여 a의 'kick'과 'punch' 값을 접근한다. op2는 더하고자 하는 정수를 나타냄
- 반환값은 tmp로, 이렇게 하면 현재 객체(a)와 정수를 더한 결과인 새로운 객체를 반환하며, 이는 'a + 2' 연산의 결과를 다른 객체(b)에 할당할 수 있게 한다.❗️❗️❗️
이렇게 연산자 중복을 통해 사용자 정의 타입과 기본 타입 간의 연산도 가능해진다. 이를 통해 코드의 유연성을 높이고, 다양한 연산을 수행할 수 있다.😀😃