8. 연산자 오버로딩 (1) - 소개
◆ 연산자 오버로딩
이번 연산자 오버로딩 강의가 재미있는 이유
1. 지금 까지 배운 내용을 종합적으로 이해를 해야 코드를 이해할 수 있다.
퍼즐 푸는 것처럼 능력을 테스트할 수 있는 기회이다.
2. 굉장히 C++ 의 특화된 기능이다
Java는 연산자 오버로딩이 없다.
C++을 배우고 연산자 오버로딩을 할 줄 알면 좋다.
어려울 수도 , 재미있을 수도 있으나 재미있으면 좋겠다.
이 강의가 재밌으려면 지난 강의들이 다 이해가 되어야 할 수 있다.
Operator Overlading
◆ 연산자 오버로딩
◆ 멤버 함수인 연산자 오버로딩
◆ 전역 함수인 연산자 오버로딩
◆ 스트림 삽입 및 추출 연산자 오버로딩
◆ 대입 연산자 오버로딩
◆ 첨자 연상자 오버로딩
강의 순서는 개요부터 시작한다.
연산자 오버로딩을 하는 2가지 방법
멤버 함수 / 전역 함수 연산자 오버로딩을 배운다.
그 이후에 스트림 삽입 및 추출 연산자 / 대입 연산자 / 첨자 연산자
오버로딩에 대해서 배울 예정이다.
중요한 Part이다.★★★★★
◆ 연산자 오버로딩
● +, = , * 등 연산자를 유저 정의 타입(ex. 클래스)에 사용할 수 있도록 하는 기능
☞ player1 + player2
● 유저 정의 타입을 기본 타입(int, float, etc)과 유사하게 동작하게 할 수 있다
● 코드를 보다 이해하기 쉽고, 작성하기 쉽게 한다.
☞ 항상 좋은 것은 아니다. 직관성과 가독상을 높인다고 판단될 경우 활용.
● 대입 연산자(=)를 제외하면 자동 생성되지 않으며, 사용자가 구현해주어야 한다.
◆ 연산자 오버로딩 기능 자체도 의미 있지만, 지금까지 배운 모든 내용이 종합적으로 적용되므로 이해하지 못한 내용이 없는지 점검 필요.
연산자 오버로딩이란 무엇일까?
연산자는 + - * / -> C++ 첫 시작에 이야기하였다
연산자를 오버로딩하는것이다. 오버라이딩과 헷갈리지 말아야 한다.
*AI Claude 오버로딩과 오버라이딩 간단 비교
오버로딩(Overloading) = " 같은 이름, 다른 역할"
// 함수 오버로딩
void print(int x);
void print(string s);
void print(double d);
// 연산자 오버로딩
Vector operator+(const Vector& other); // + 연산자에 새로운 의미 부여
오버라이딩(Overriding) = " 부모 것을 새롭게 재 정의"
class Entity{
virtual void Move() { cout << "Entity moves"; }
};
class Player : public Entity {
void Move() override { cout << "Player runs fast"; } // 부모 함수 재정의
};
핵심 차이점
오버로딩
● 같은 이름의 함수 / 연산자를 여러 개 만드는 것
● 매개변수가 달라야 함
오버라이딩
● 부모 클래스의 가상 함수를 자식 클래스에서 다시 구현하는 것
● 상속 관계에서만 발생
쉬운 기억법
● 오버로딩 = "로딩"이 많다 = 같은 이름 여러 개
● 오버라이딩 = "라이딩"해서 덮어쓴다 = 부모 것을 새로 써서 덮음
이름이 똑같은 함수를 여러 개 만들 수 있다 - 오버로딩
이번 시간에 연산자를 오버로딩 하는 방법을 배운다.
연산자를 왜 오버로딩 해야 할까?
개발자가 클래스를 만들었다는 이야기는 (ex. Player Class)
Player를 갖는 변수-> 객체 (ex. Player p;)를 만들 수 있었다.
Player p1, Player p2를 선언하였고
Player p3 = p1 + p2; 를 하면 불가능하다.
int a =1; int b =2;
int c = a+b; 이렇게 선언 가능하다
선언할 수 있는 이유는
a에 저장되어 있는 1 과 b 에 저장되어있는 2를 더해
변수 c에 대입할 수 있다.
a와 b의 타입은 int이다.
int , float , boolean , Character 등은 기본 데이터 타입이라고 이야기하였다.
기본 데이터 타입은 개발자가 아무런 작성하지 않아도
기본적으로 C++에 정의되어 있는 데이터 타입이다.
int는 4byte를 차지하고 있는 등 미리 정의되어 있다.
그런데 Player 데이터타입은 어떤가? 개발자가 만들었다.
어떤 공간을 할당해야 하는지 컴파일러가 미리 알고 있는 게 아니고
개발자가 직접 코드를 작성해서 구현해야 한다.
그런 클래스는 사용자 정의 또는 유저 정의 타입이라고 한다.
유저 정의 타입은 + , - , = , * 등등 연산자를 기본적으로 사용이 불가능하다.
player1 + player2 가 기본적으로는 불가능하다.
왜 불가능하냐면 컴파일러가 어떻게 해야 하는지 모르기 때문이다.
+ 연산을 진행할 때도 어떻게 해야 하는지 개발자가 구현해야 한다.
정의할 수 있는 방법이 연산자 오버로딩이다.
기본 타입(int, float 등등) 은 기본적으로 연산자를 사용할 수 있다.
클래스 객체에 대해서는 불가능하다.
그러나 연산자 오버로딩을 하면 유사하게 사용할 수 있다.
연산자 오버로딩 하는 이유는 코드를 이해하기 쉽고 작성하기 쉽게 한다.
연산자 오버로딩을 하지 않아도 똑같은 기능을 하는 코드를 만드는 데 전혀 문제가 없다.
연산자 오버로딩은 보기 쉽고 읽기 쉬운 사용하기 쉬운 코드를 만들기 위해 사용된다.
연산자 오버로딩을 한다면 무조건 좋은가? 세상엔 무조건이란 없다.
연산자 오버로딩이 필요할 때 사용하면 좋다.
◆ 연산자 오버로딩
● 숫자 하나를 갖는 Number 클래스를 예시로 작성하고,
클래스 객체끼리 사칙 연산 기능이 필요한 경우 (a+b) * (c/d)를 계산해야 한다면,
● 전역 함수를 사용하여 구현
Number result = multiply(add(a, b) , divide(c, d));
● 멤버 함수를 사용해 구현
Number result = (a.add(b)). multiply(c.divide(d));
(이렇게 구현하면 너무나 실수 가기 쉽다.)
● 연산자 오버로딩을 사용하여 구현
Number result = (a+b)*(c/d);
다른 간단한 예제를 보자
#include <iostream>
class Number
{
private:
int val;
public:
Number(int val)
:val{ val } { }
};
int main()
{
Number n1{ 5 };
Number n2{ 10 };
Number n3 = n1 + n2; // ERROR!
}
n3 에 15를 넣고 싶으면 연산자 오버로딩을 해야한다.
class Number
{
private:
int val;
public:
Number(int val)
:val{ val } { }
Number Add(Number b)
{
return Number{ val + b.val };
}
void Print()
{
cout << val << endl;
}
};
Number 클래스에 멤버 함수로 Add 를 추가하여
또 다른 Number 를 인자로 받도록 한다.
반환형이 Number 이고, 인자가 Number 인 멤버 함수를 구현하였다.
함수 내용은 새로운 Number 객체를 만들어서 반환을 하는데
Number 객체는 인자로 가진 Number 객체와 멤버 변수를 더하여 반환한다.
int main()
{
Number n1{ 5 };
Number n2{ 10 };
Number n3 = n1.Add(n2);
n3.Print(); // 15
}
또 다른 방법은 전역 함수를 만드는 것이다.
Number Add(Number n1, Number n2)
{
return Number{ n1.val + n2.val };
}
또 다른 방법은 멤버 함수가 아닌 전역 함수를 만들어서
반환형에 Number, Number 인자를 2개 받고
함수 내용은 n1 의 val 와 n2 의 val 를 더하여
새로운 Number 객체를 생성해서 반환할 수 있다.
기존 Number 클래스를 사용하면 val 부분에서 에러가 나올 수 있다.
class Number
{
private:
int val;
public:
Number(int val)
:val{ val } { }
Number Add(Number b)
{
return Number{ val + b.val };
}
void Print()
{
cout << val << endl;
}
int GetValue()
{
return val;
}
};
Number Add(Number n1, Number n2)
{
return Number{ n1.GetValue() + n2.GetValue() };
}
본문으로 넘어가면 + 이외에 복잡한 식을 필요로 한다고 가정해보자.
전역 함수 또는 멤버 함수를 이용해서 만들면
(a+b) *(c/d) 를 사용하고 싶다면
multiply 함수, add 함수, divide 함수를 만들어서 사용해야한다.
해석하는데 굉장히 피곤하다.
연산자 오버로딩을 하게되면 간단하게 (a+b) *(c/d) 를 사용할 수 있다.
◆ 연산자 오버로딩 예제
class Point {
private:
int xPos;
int yPos;
public:
Point(int x = 0, int y = 0)
:xPos{ x }, yPos{ y }
{
}
void ShowPosition() const
{
cout << "[" << xPos << ", " << yPos << "]" << endl;
}
};
int main()
{
Number n1{ 5 };
Number n2{ 10 };
Number n3 = n1.Add(n2);
n3.Print(); // 15
Number n4 = Add(n1, n2);
n4.Print(); // 15
}
이번 강의에서는 Point 클래스를 이용하여 연산자 오버로딩을 알아보자.
좌표값 2개를 받는 int xPos, int yPos 멤버 변수가 있다.
ShowPosition() 멤버 함수는 x 와 y 의 값을 보여주는 함수이다.
◆ 연산자 오버로딩 기본 규칙
● 연산의 우선순위를 바꿀 수 없다. ( * 는 + 보다 먼저 계산)
● 단항, 이항, 삼항 연산의 교체 불가능
● 기본 타입(int, float, etc)에 대한 연산자는 오버로딩 불가능
● 새로운 연산자의 정의 불가
◆ 연산자는 멤버 함수 또는 전역 함수로 오버로딩 가능
● [ ] , ( ) , -> , = 등 몇몇 연산자는 멤버 함수로만 오버로딩 가능.
연산자 오버로딩에서 제약 사항이 있다.
1. 연산의 우선 순위를 바꿀 수는 없다.
* 는 + 보다 먼저 계산된다.
p1 * p2 + p3 을 하게 되면
괄호가 없는 경우에는 수학 공식과 똑같이
(p1 * p2) + p3 가 계산이 된다.
2. 단한, 이항, 삼항 연산자에 대해서 추후에 설명할 예정인데
그것을 바꾸는 것은 불가능 하다.
3. 연산자 오버로딩은 개발자가 만든 클래스에 대해서만 계산이 가능하다
int 변수 등 기본 타입 연산자를 개발자 마음대로 바꿀 수는 없다.
기본 타입의 오버로딩은 불가능 하다.
4. 없는 기본 연산자 타입을 새로 만들어내는것도 불가능 하다.
이미 존재하는 기본 연산자 타입을 클래스에 대해서 동작할 수 있게 만드는 것이다.
이 강의에서 중요한게 연산자를 오버로딩하는 방법이다.
멤버 함수 또는 전역 함수로 오버로딩 하는 방법이다.
몇몇 연산자들 [ ] , ( ) , -> , = 등은 예외적으로 멤버 함수로만 오버로딩이 가능하다.
◆ 연산자 오버로딩 사용 목적
● Point는 기본 자료형이 아닌 사용자 정의 자료형(클래스)이다.
● 연산자 오버로딩을 통해 연산자에 의한 객체의 동작 정의 가능하다.
int main()
{
Point p1{ 10,20 };
Point p2{ 30,40 };
Point p3 = p1 + p2; // 가능하도록 연산자 오버로딩을 할 예정이다.
// p3.xPos = 40, p3.yPos = 60 이 될 예정이다.
cout << (p1 < p2) << endl;
cout << (p1 == p2) << endl;
cout << p3 << endl;
}
현재는 불가능하지만, 이런 식으로 사용하고 싶은 경우
=> 연산자 오버로딩을 하면 가능하다.
Operator Overlading
◆ 연산자 오버로딩 : 클래스에 대한 연산자의 적용 방식을 사용자가 직접 오버로딩하여 구현할 수 있다.
◆ 멤버 한수인 연산자 오버로딩
◆ 전역 함수인 연산자 오버로딩
◆ 스트림 삽입 및 추출 연산자 오버로딩
◆ 대입 연산자 오버로딩
◆ 첨자 연상자 오버로딩
https://youtu.be/S6MkdRzJjjg?si=wu89FqCc9WgIjdNM
조금 시간이 오래걸린 강의였는데, 확실히 한 흐름으로 읽으면 재밌는 강의이다.
제대로 연산자 오버로딩을 하는걸 사용하고 싶다.