[c++]클래스 연산자 중복

이승희·2023년 11월 10일

c++

목록 보기
4/6
post-thumbnail

클래스 연산자 중복

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개로 중복 선언할 수 없다.
  • 연산의 우선 순위 변경 안됨
    • 연산자의 기본 우선 순위는 그대로 적용된다.
  • 모든 연산자가 중복 가능하지 않음
  • 중복 가능한 연산자
+-*/%^&
~!=<>
-=*=/=%=^-&=
<<>>> >< <==! => =
< =&&++
- >[ ]( )newdeletenew[ ]delete[ ]
  • 중복 불가능한 연산자
..*::(범위지정 연산자)? : (3항 연산자)

연산자 함수 구현

  1. 클래스의 멤버 함수로 구현
  2. 외부 함수(전역 함수)로 구현

클래스의 멤버 함수로 구현

연산자 함수 작성에 사용할 클래스

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)에 할당할 수 있게 한다.❗️❗️❗️

이렇게 연산자 중복을 통해 사용자 정의 타입과 기본 타입 간의 연산도 가능해진다. 이를 통해 코드의 유연성을 높이고, 다양한 연산을 수행할 수 있다.😀😃

0개의 댓글