본문 바로가기

교육 노트/C++ 심화강의

[C++ 때려잡기] C++ 심화강의 9 연산자 오버로딩, chaining

2018/08/27 - [교육 노트/C++ 심화강의] - [C++ 때려잡기] C++ 심화강의 1 객체 지향 프로그래밍과 클래스

2018/08/27 - [교육 노트/C++ 심화강의] - [C++ 때려잡기] C++ 심화강의 2 접근 한정자

2018/08/27 - [교육 노트/C++ 심화강의] - [C++ 때려잡기] C++ 심화강의 3 생성자와 소멸자

2018/08/29 - [교육 노트/C++ 심화강의] - [C++ 때려잡기] C++ 심화강의 4 복사 생성자와 깊은 복사

2018/08/29 - [교육 노트/C++ 심화강의] - [C++ 때려잡기] C++ 심화강의 5 this 포인터

2018/08/29 - [교육 노트/C++ 심화강의] - [C++ 때려잡기] C++ 심화강의 6 Class 선언과 정의

2018/08/29 - [교육 노트/C++ 심화강의] - [C++ 때려잡기] C++ 심화강의 7 inline 함수

2018/08/31 - [교육 노트/C++ 심화강의] - [C++ 때려잡기] C++ 심화강의 8 Friend function, friend class


함수 오버로딩은 다른 행동을하는(파라미터를 달리하여) 같은 이름을 가진 함수를 만드는것이다.


연산자오버로딩은 여기서 함수가 아닌 연산자를 오버로딩 하는것이다.


1, 연산자 오버로딩

말그대로 연산자를 오버로딩하는것
사용자 정의 자료형이기때문에 기존 연산자
(+, -, *, /, %, … 등)을 사용하지 못한다.
그러나 연산자 오버로딩을 통하여 사용가능하게 할수있다


마리오 클래스에서 hp를 감소시키는 hit와 hp를 늘리는 heal 함수를 만들었다고 하자.


하다보니 귀찮다.

걍 마리오에  +50 하면 hp가 차게 하고 -50 하면 hp 가 감소하도록 하고싶다.


다음과 같이 +=, -= 연산자를 오버로딩 하여  +=, -=연산자에 대하여 마리오 클래스에서 작동하는법을 설정하도록 할수있다.


+=, -= 뿐만아니라 = - * - 는 물론 == < > 과같은 비교, 심지어 배열첨자 연산자 [] 까지 정의할수있다.

<< >> 과 같은 비트단위 연산자도 정의할수있다.


익숙한 예로

cout 과 cin 에서의  <<, >> 연산자는  원래 비트 쉬프트 연산자이다.

이걸 istream, ostream클래스에서 오버로딩 한것이고 cout과 cin은 해당 클래스로 만들어진 객체이다!



연산자 오버로딩은 해당 클래스에 맴버 함수로 오버로딩해도 되고

전역 함수로 오버로딩후 해당 함수를 프랜드로 만들어 주어도 된다(내부 변수 접근이 필요없으면 friend할 필요없다)

단 전역함수와 맴버함수는 파라미터로 받는것이 틀려지게된다. 맴버 함수의 경우 자기 자신이 이미 포함되어있다.


무슨뜻인가 하면


맴버 함수로


#include <iostream>
using namespace std;

class Character
{
public:
    Character(int _hp)
    {
        hp = _hp;
    }

private:
    int hp;

public:
    int getHp();

    void operator+= (int hp);

};


int Character::getHp()
{
    return hp;
}

void Character::operator+=(int hp)
{
    this->hp += hp;
}


int main()
{
    Character mario(100);
    mario += 50;
    cout << mario.getHp() << endl;
    return 0;
}


다음과 같고 +=를 전역 함수로 고치면


#include <iostream>
using namespace std;


class Character
{
public:
    Character(int _hp)
    {
        hp = _hp;
    }

private:
    int hp;

public:
    int getHp();

    friend void operator+= (Character& character, int hp);
};

void operator+= (Character& character, int hp)
{
    character.hp += hp;
}


int Character::getHp()
{
    return hp;
}




int main()
{
    Character mario(100);
    mario += 50;
    cout << mario.getHp() << endl;
    return 0;
}

다음과 같다. 전역 함수는 어떤 Character인지 알려주기위한 파라미터 하나가 더 추가되었다.


각각 오퍼레이터들의 매개변수 수는 미리 정해져있는데 그때그때 찾아보면 된다.

근데 대부분 당연한 크기의 파라미터를 가진다.



2. 대입 연산자 오버로딩

#include <iostream>
using namespace std;


class Character
{
public:
    Character(int _hp)
    {
        hp = _hp;
    }

private:
    int hp;

public:
    int getHp();

    friend void operator+= (Character& character, int hp);
};

void operator+= (Character& character, int hp)
{
    character.hp += hp;
}


int Character::getHp()
{
    return hp;
}




int main()
{
    Character mario(100),mario2(0);
    mario += 50;
    mario2 = mario;
    cout << mario2.getHp() << endl;
    return 0;
}

을 보면

mario2 = mario를 사용하였다.

그러나 우리는 Character을 파라미터로 받는 = 연산자를 사용하지 않았다.

그러나 오류없이 정상적으로 동작한다.

이유는 디폴트 대입 연산자이다.

만들어주지 않으면 자동으로 만들어준다.

다만 동작하는 방법은 디폴트 복사 생성자와 동일하다.

따라서 깊은 복사가 필요하다면 직접 만들어 주어야한다.


#include <iostream>
using namespace std;


class Character
{
public:
    Character(int _hp)
    {
        hp = new int;
        *hp = _hp;
    }

    ~Character()
    {
        delete hp;
    }
private:
    int* hp;

public:
    int getHp();
    void operator= (Character& character);
};



int Character::getHp()
{
    return *hp;
}

void Character::operator=(Character& character)
{
    *(this->hp) = *(character.hp);
}




int main()
{
    Character mario(100),mario2(0);
    mario2 = mario;
    cout << mario2.getHp() << endl;
    return 0;
}

hp를 동적할당 할 이유는 없지만 예시를 위해서 동적할당 해보았다.

암튼 이런식으로 대입 연산자를 오버로딩 해야할것이다.



3. chanining 과 this의 활용



    int a, b, c;
    a = b = c = 5;
    cout << a << b << c << endl;

위코드는 a,b,c를 한번에 5로 초기화 하였다.

그러나 우리가 만든 = 오퍼레이터는

다음과 같이 한번에 초기화 할수없다.




= 연산자는 수행순서가 오른쪽 에서 왼쪽이다.

즉 a=b=c=5에서 c가 5가되고 b = c에서 c가 5이므로 b가 5가 되고 a=b 에서 b가 5 이므로 a가 5가 되는것이다.


이렇게 연속적으로 실행되게 되는데 이러한 과정을 체이닝 (chanining) 이라고 한다 뭐 체인을 거는 모양새이기 때문인가보다.


암튼 현재 우리 캐릭터 클래스는 체이닝이 불가능하다.

그이유는 mario가 mario2에  들어간뒤 리턴값이 void 이기 때문이다.

그러면 어떻게 할까?


정답은 간단한데

우리가 만든 대입연산자는 지금 캐릭터 레퍼런스를 받는다.

즉 캐릭터 레퍼런스가 나오면 대입이 가능하다는 소리이다.


Character& Character::operator=(Character& character)
{
    *(this->hp) = *(character.hp);
    return *this;
}

this포인터를 이용하여 자기 자신의 레퍼런스를 리턴해주면 된다.



지금까지 class 기초에 대하여 배웠다.

지금까지 배운 지식을 활용하여


다음 실습을 진행하자


1. 분수 계산기

분수 클래스를 만들고 해당 클래스를 이용하여 분수 계산기를 만들자.

+,-,*,/ 모두 가능해야한다.
cout << 으로 출력이 가능해야 하며 분수 표기는 3/4  이렇게 해주면 됩니다!


2. 행렬 계산기

행렬클래스를 만들고 해당 클래스를 이용하여 행렬 계산기를 만들자.

내가 원하는 n*m행렬 두개를 만들어야 한다.
+,-,*,/ 모두 가능하게 하자!
행렬은 배열이 아닌 동적 할당을 이용하여 생성
nXm * mXk 이런 예외처리를 해줘야 합니다


728x90