연산자 오버로딩 - 산술 연산자(1)

조한별·2022년 4월 7일
0

오버로딩
오버로딩은 사전적 의미로는 과적재로서, 프로그래밍 내에서는 '함수 중첩'이라고 생각하면 이해하기가 쉬울 것같다.
함수들은 오버로딩을 통해서 다형성을 갖게 될 수 있다.

#include <iostream>

using namespace std;

class Vector
{
public:
    float x;
    float y;
    float z;

    Vector operator-()
    {
        return Vector{ -x, -y, -z };
    }

    Vector operator-(const Vector& v)
    {
        return Vector{ x - v.x, y - v.y, z - v.z };
    }

    Vector operator+()
    {
        return *this;
    }

    Vector operator+(const Vector& v)
    {
        return Vector{ x + v.x, y + v.y, z + v.z };
    }

    float operator*(const Vector& v)
    {
        return (x * v.x) + (y* v.y) + (z* v.z);
    }

    Vector operator*(float v)
    {
        return Vector{ x * v, y * v, z * v };
    }

    Vector operator/(float v)
    {
        return Vector{ x / v, y / v, z / v };
    }

    //전위연산자
    Vector& operator++()
    {
        this->x += 1;
        this->y += 1;
        this->z += 1;

        return *this;
    }

    //후위연산자
    Vector operator++(int)
    {
        Vector temp = *this;
        ++(*this);
        return temp;
    }

    void print()
    {
        cout << "x : " << x << ", y : " << y << ", z : " << z << endl;
    }
};

int main()
{
    Vector v0{ 0, 1, 2 };
    Vector v1{ 1, 2, 3 };

    Vector v2 = v0 + v1;    // Vector v2 = v0.operator+(v1);
    Vector v3 = -v1;        // Vector v3 = v1.operator-();
    Vector v4 = v1 * 3.0f;
    Vector v5 = v4 / 2.0f;

    v0.print();
    v1.print();
    v2.print();					//이항연산자(+)
    v3.print();					//단항연산자(-)
    cout << v0 * v1 << endl;	//이항연산자(*) 내적구하기
    v4.print();					//이항연산자(*) 실수와 곱하기
    v5.print();					//이항연산자(/) 실수로 나누기

    (++v0).print();				//전위연산자(++)
    (v0++).print();				//후외연산자(++)
    v0.print();					//후위연산자 기능확인

    return 0;
}

결과

x : 0, y : 1, z : 2			
x : 1, y : 2, z : 3
x : 1, y : 3, z : 5	
x : -1, y : -2, z : -3
8
x : 3, y : 6, z : 9
x : 1.5, y : 3, z : 4.5
x : 1, y : 2, z : 3
x : 1, y : 2, z : 3
x : 2, y : 3, z : 4

Vector 클래스 내에서 산술 연산자를 오버로딩을 해보았다.

단항 연산자의 경우엔 매개변수를 따로 받아오지 않아도 되지만 이항 연산자의 경우엔 매개변수를 필히 받아와야 한다. 그 때에 const로 선언하여 연산자 내에서 매개변수의 값을 변경하지 못하도록 막는 것도 중요하다. 그러므로 필요하지 않다면 const를 써주는 게 좋다.

전위 연산자와 후위 연산자 오버로딩은 매개변수에 int를 넣어 구분을 짓고 있다. 마이너스 전/후위 연산자의 경우에는 플러스를 마이너스로 바꿔주면 된다.

Vector& operator--();		//마이너스 전위 연산자
Vector operator--(int);		//마이너스 후위 연산자

사실 여기서 전위 연산자에 &를 붙히지 않아도 즉, 레퍼런스 리턴이 아니여도 기능상 크게 다르지 않다.

//전위연산자
//기능상으로는 크게 다르지 않고 심지어 리턴의 값도 같다.
//하지만 리턴하는 Vector의 주소값이 다르다.
    Vector operator++()
    {
        this->x += 1;
        this->y += 1;
        this->z += 1;

        return *this;
    }

이 둘의 차이에 대해서 말하기 위해서는 lvalue와 rvalue에 대해서 언급할 필요가 있다.
처음엔 나도 Left와 Right에서 따온 L과 R일거라 생각했지만 cpp 표준에서는 더 이상 L과 R은 left, right를 의미하지 않는다고 한다.
lvalue - 지속되는 값
rvalue - 임시적인 값

void smt()
{
	int num = 30;
    //num은 lvalue, 30은 rvalue
    
    int& num2 = num;
    //num2, num 모두 lvalue
}

그래서 이 것을 오늘의 예제에 통해서 말하자면

Vector& operator++();	//lvalue
Vector operator++();	//rvalue

결국 함수에서 리턴하는 것이 레퍼런스냐 아니냐의 차이인데 아닌 경우엔 아무리 *this를 리턴했어도 그 값이 복사가 한번 이뤄진다. 즉, 메모리 낭비가 발생한다.
하지만 레퍼런스로 사용하면 메모리 공간도 절약이 된다.

profile
게임프로그래머 지망생

0개의 댓글