오버로딩
오버로딩은 사전적 의미로는 과적재로서, 프로그래밍 내에서는 '함수 중첩'이라고 생각하면 이해하기가 쉬울 것같다.
함수들은 오버로딩을 통해서 다형성을 갖게 될 수 있다.
#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를 리턴했어도 그 값이 복사가 한번 이뤄진다. 즉, 메모리 낭비가 발생한다.
하지만 레퍼런스로 사용하면 메모리 공간도 절약이 된다.