[C++] 연산자의 오버로딩 (Operator Overloading), 오버로딩 vs 오버라이딩
연사자의 오버로딩
인프런 Rookiss님의 'Part1: C++ 프로그래밍 입문' 강의를 기반으로 정리한 필기입니다.
😎[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 강의 들으러 가기!
목차
연산자
연산자 vs 함수
연산자는 피연산자의 갯수/타입이 고정되어 있다.
연산자 오버로딩
일단 [연산자 함수]를 정의해야 한다.
함수도 멤버함수 vs 전역함수가 존재하는것처럼, 연산자 함수도 두가지 방식으로 만들 수 있다.
- 멤버 연산자 함수 version
- 전역 연산자 함수 version
위의 상황에서는 연산을 할 수 없다. 하지만, 연산자 오버로딩을 하면 Position pos3 = pos + pos2 연산을 할 수 있다.
#include <iostream>
using namespace std;
class Position
{
public:
Position operator+(const Position& arg)
{
Position pos;
pos._x = _x + arg._x;
pos._y = _y + arg._y;
return pos;
}
public:
int _x;
int _y;
};
int main()
{
Position pos;
pos._x = 0;
pos._y = 0;
Position pos2;
pos2._x = 1;
pos2._y = 1;
Position pos3 = pos + pos2;
// pos3 = pos.operator+(pos2) 위와 같은 의미
return 0;
}
멤버 연산자 함수 version
a op b 형태에서 왼쪽으로 기준으로 실행됨 (a가 클래스여야 가능. a를 '기준 피연산자'라고 함)
한계) a가 클래스가 아니면 사용 못 함.
class Position {
public:
Position operator+(const Position& arg)
{
Position pos;
pos._x = _x + arg._x;
pos._y = _y + arg._y;
return pos;
}
public:
int _x;
int _y;
};
int main(){
//...
Position pos3 = pos + pos2;
// pos3 = pos.operator+(pos2); //위와 같다
Position pos4 = pos3 + 1;
// pos4 = pos3.operator+(1); // 1 + pos3로 연산되니 연산 오류가 난다. 빌드되지 않는다.
}
순서를 바꾸면 멤버 연산자 함수 버젼으로 사용할 수 없다.
전역 연산자 함수 version
a op b 형태라면 a, b 모두를 연산자 함수의 피연산자로 만들어준다.
전역 연산자 함수 version은 왼쪽이 클래스가 아니어도 문제가 되지 않는다.
...
// 전역 연산자 함수 version
Position operator+(int a, const Position& b){
Position ret;
ret._x = b._x + a;
ret._y = b._y + a;
return ret;
}
int main(){
//...
Position pos3 = pos + pos2;
Position pos4 = pos3 + 1;
// pos4 = po3.operator+(1) //위와 같은 의미
// Position pos4 = 1 + pos3 //순서를 바꾸면 멤버 연산자 함수 버젼으로 사용할 수 없다
Position pos44 = 1 + pos3; // 전역 연산자 함수 version으로 사용 가능하다.
// 위에 Position operator+(int a, const Position& b) 전역 연산자 함수 사용.
return 0;
}
멤버 vs. 전역 연산자 함수
그럼 무엇이 더 좋은가? 그런거 없음. 심지어 둘 중 하나만 지원하는 경우도 있기 때문
- 대표적으로 대입 연산자 (a = b)는 전역 연산자 version으로는 못 만든다
//...
// 전역 연산자 함수 version
Position operator+(int a, const Position& b){
Position ret;
ret._x = b._x + a;
ret._y = b._y + a;
return ret;
}
void operator=(int arg){
_x = arg;
_y = arg;
}
int main(){
//...
Position pos3 = pos + pos2;
Position pos4 = 1 + pos3; // 전역 연산자 함수 version으로 사용 가능하다.
// 위에 Position operator+(int a, const Position& b) 전역 연산자 함수 사용.
bool isSame = (pos3 == pos4);
Position pos5; // void operator=(int arg)에 대입
pos5 = 5; // 대입이 불가능하도록 문법적으로 막아놨다.
return 0;
}
pos5 = 5의 경우 전역 연산자인 void operator=(int arg)의 대입이 불가능하도록 문법적으로 막아놨다.
멤버 vs. 전역 연산자 함수
그럼 무엇이 더 좋은가?
- 그런거 없음. 심지어 둘 중 하나만 지원하는 경우도 있기 때문
- 대표적으로 대입 연산자 (a = b)는 전역 연산자 version으로는 못 만든다
Position& operator=(int arg) // 자기자신의 참조값으로 만들어 넘겨줌
{
_x = arg;
_y = arg;
// Position* this = 내자신의 주소
return *this;
}
int main(){
...
Position pos5; // void operator=(int arg)에 대입
pos3 = (pos5 = 5);
return 0;
}
전체 코드
#include <iostream>
using namespace std;
class Position
{
public:
Position operator+(const Position& arg)
{
Position pos;
pos._x = _x + arg._x;
pos._y = _y + arg._y;
return pos;
}
Position operator+(int arg)
{
Position pos;
pos._x = _x + arg;
pos._y = _y + arg;
return pos;
}
bool operator==(const Position& arg)
{
return _x == arg._x && _y == arg._y;
}
void operator=(int arg)
{
_x = arg;
_y = arg;
}
public:
int _x;
int _y;
};
// 전역 연산자 함수 version
Position operator+(int a, const Position& b)
{
Position ret;
ret._x = b._x + a;
ret._y = b._y + a;
return ret;
}
int main()
{
Position pos;
pos._x = 0;
pos._y = 0;
Position pos2;
pos2._x = 1;
pos2._y = 1;
Position pos3 = pos + pos2;
// pos3 = pos.operator+(pos2) //위와 같은 의미
Position pos4 = pos3 + 1;
// pos4 = po3.operator+(1) //위와 같은 의미
// Position pos4 = 1 + pos3 //순서를 바꾸면 멤버 연산자 함수 버젼으로 사용할 수 없다
Position pos4 = 1 + pos3; // 전역 연산자 함수 version으로 사용 가능하다.
// 위에 Position operator+(int a, const Position& b) 전역 연산자 함수 사용.
bool isSame = (pos3 == pos4);
Position pos5; // void operator=(int arg)에 대입
pos5 = 5;
return 0;
}
복사 대입 연산자
- 대입 연산자가 나온 김에 [복사 대입 연산자]에 대해 알아보자
- 용어가 좀 헷갈린다
- [복사 생성자] [대입 연산자] [복사 대입 연산자]
- 복사 대입 연산자 = 대입 연산자 중, 자기 자신의 참조 타입을 인자로 받는 것
[복사 생성자] [복사 대입 연산자] 등 특별 대우를 받는 이유는, 객체가 ' 복사 ' 되길 원하는 특징 때문이다.
TODO ) 동적할당 시간에 더 자세히 알아볼 것
Position& operator=(const Position& arg)
{
_x = arg._x;
_y = arg._y;
// Position* this = 내자신의 주소
return *this;
}
기타
- 모든 연산자를 다 오버로딩 할 수 있는 것은 아니다 (:: . .* 이런건 안됨)
- 모든 연산자가 다 2개 항이 있는 것이 아님. ++ -- 가 대표적 (단항 연산자)
- 증감 연산자 ++ --
- 전위형 (++a) opertator++()
- 휘위형 (a++) operator++(int)
void operator++()
{
_x++;
_y++;
return *this;
}
void operator++(int)
{
Position ret = *this;
_x++;
_y++;
return ret;
}
위의 void형 보다는 자기 자신을 참조(&Position)하는 아래의 버젼이 더 낫다.
아래와 같이 만들어 ++를 연속적으로 호출할 수 있다.
Position& operator++()
{
_x++;
_y++;
return *this;
}
Position& operator++(int)
{
Position ret = *this;
_x++;
_y++;
return ret;
}
연산자
int main()
{
Position pos;
pos._x = 0;
pos._y = 0;
Position pos2;
pos2._x = 1;
pos2._y = 1;
Position pos3 = pos + pos2;
Position pos4 = pos3 + 1;
Position pos4 = 1 + pos3; // 전역 연산자 함수 version으로 사용 가능하다.
// 위에 Position operator+(int a, const Position& b) 전역 연산자 함수 사용.
bool isSame = (pos3 == pos4);
Position pos5; // void operator=(int arg)에 대입
pos3 = (pos5 = 5);
// (const Pos&)줘~ (Pos)복사값 줄께~
pos5 = pos3++;
// 위의 코딩이 연산되는 방식은 아래와 같다
// Position XXXXXX = pos3++
// pos5 = XXXXXX;
++(++pos3);
// pos3 = pos.operator+(pos2);
return 0;
}
call Position::operator++ (03E12A8h)을 실행하면 스택(Stack) 어딘가에 임시적으로 저장한 후,
그 복사본의 참조값을 eax에 넣어준다.
전체 코드
#include <iostream>
using namespace std;
class Position{
public:
// 연산자 오버로딩
Position operator+(const Position& arg)
{
Position pos;
pos._x = _x + arg._x;
pos._y = _y + arg._y;
return pos;
}
Position operator+(int arg)
{
Position pos;
pos._x = _x + arg;
pos._y = _y + arg;
return pos;
}
bool operator==(const Position& arg)
{
return _x == arg._x && _y == arg._y;
}
/*
Position& operator=(int arg) // 자기자신의 참조값으로 만들어 넘겨줌
{
_x = arg;
_y = arg;
// Position* this = 내자신의 주소
return *this;
}
*/
// [복사 생성자] [복사 대입 연산자] 등 특별 대우를 받는 이유는,
// 말 그대로 객체가 '복사'되길 원하는 특징 때문이다
// TODO ) 동적할당 시간에 더 자세히 알아볼 것
Position& operator=(const Position& arg)
{
_x = arg._x;
_y = arg._y;
// Position* this = 내자신의 주소
return *this;
}
Position& operator++()
{
_x++;
_y++;
return *this;
}
Position operator++(int)
{
Position ret = *this;
_x++;
_y++;
return ret;
}
public:
int _x;
int _y;
};
// 전역 연산자 함수 version
Position operator+(int a, const Position& b)
{
Position ret;
ret._x = b._x + a;
ret._y = b._y + a;
return ret;
}
int main()
{
Position pos;
pos._x = 0;
pos._y = 0;
Position pos2;
pos2._x = 1;
pos2._y = 1;
Position pos3 = pos + pos2;
// pos3 = pos.operator+(pos2) //위와 같은 의미
Position pos4 = pos3 + 1;
// pos4 = po3.operator+(1) //위와 같은 의미
// Position pos4 = 1 + pos3 //순서를 바꾸면 멤버 연산자 함수 버젼으로 사용할 수 없다
Position pos44 = 1 + pos3; // 전역 연산자 함수 version으로 사용 가능하다.
// 위에 Position operator+(int a, const Position& b) 전역 연산자 함수 사용.
bool isSame = (pos3 == pos4);
Position pos5; // void operator=(int arg)에 대입
pos3 = (pos5 = 5);
// (const Pos&)줘~ (Pos)복사값 줄께~
pos5 = pos3++;
// 위의 코딩이 연산되는 방식은 아래와 같다
// Position XXXXXX = pos3++
// pos5 = XXXXXX;
++(++pos3);
// pos3 = pos.operator+(pos2);
return 0;
}
'⭐ Programming > C++' 카테고리의 다른 글
[C++] 객체지향 마무리 (0) | 2022.04.06 |
---|---|
[C++] 오버로딩과 오버라이딩의 차이 (0) | 2022.04.06 |
[C++] 초기화 리스트 (0) | 2022.04.05 |
[C++] 다형성, 가상함수 테이블, 순수 가상함수, 추상 클래스 (0) | 2022.04.05 |
[C++] 다형성 (Polymorphism) (0) | 2022.04.05 |
댓글
이 글 공유하기
다른 글
-
[C++] 객체지향 마무리
[C++] 객체지향 마무리
2022.04.06 -
[C++] 오버로딩과 오버라이딩의 차이
[C++] 오버로딩과 오버라이딩의 차이
2022.04.06 -
[C++] 초기화 리스트
[C++] 초기화 리스트
2022.04.05 -
[C++] 다형성, 가상함수 테이블, 순수 가상함수, 추상 클래스
[C++] 다형성, 가상함수 테이블, 순수 가상함수, 추상 클래스
2022.04.05