연산자 오버로딩의 이해
C++에서는 연산자를 오버로딩하여 기존에 존재하던 연산자의 기능 이외에 다른 기능을 추가할 수 있다.
#include <iostream>
using namespace std;
class Point{
// 좌표 값
private:
int x, y;
public:
Point(int x, int y){
this->x = x;
this->y = y;
}
void showInfo(){
cout << "(" << this->x << ", " << this->y << ")";
}
// 더하기 연산자 오버로딩
Point operator+(const Point & ref){
Point pos(x + ref.x, y + ref.y);
return pos;
}
};
int main(){
Point p1(1, 1);
Point p2(3, 4);
/**
* p1 + p2는
* 컴파일러가 p1.operator+(p2) 로 인식한다.
*/
Point p3 = p1 + p2;
p3.showInfo();// (4,5) 출력
return 0;
}
#include <iostream>
using namespace std;
class Point{
// 좌표 값
private:
int x, y;
public:
Point(int x, int y){
this->x = x;
this->y = y;
}
void showInfo(){
cout << "(" << this->x << ", " << this->y << ")";
}
// friend 선언으로 private 값에 접근 가능
friend Point operator+(const Point & p1, const Point & p2);
}
Point operator+(const Point & p1, const Point & p2){
Point pos(x + ref.xpos, y + ref.ypos);
return pos;
}
연산자 오버로딩의 주의사항
- 본래의 의도를 벗어난 형태의 오버로딩은 좋지 않다.
- 연산자의 우선순위와 결합성은 바뀌지 않는다.
- 매개변수의 디폴트 값 설정이 불가능하다.
- 연산자의 순수 기능까지 빼앗을 수는 없다. (오버라이딩 불가)
증감연산자 오버로딩
class Point{
// 좌표 값
private:
int x, y;
public:
Point(int x, int y){
this->x = x;
this->y = y;
}
/**
* prefix 증가 연산자
* lvalue를 return. ++(++num) 같은 연산이 가능
*/
Point& operator++(){
x += 1;
y += 1;
return *this;
}
/**
* postfix 증가 연산자
* parameter에 int는 전위연산과 구분하기 위함이다. 다른 의미는 없다.
* rvalue를 return. (num++)++ 같은 연산은 컴파일 에러
*/
const Point operator++(int){
const Point ret(x, y);
x += 1;
y += 1;
return ret;
}
}
교환 법칙 문제
class Point{
// 좌표 값
private:
int x, y;
public:
Point(int x, int y){
this->x = x;
this->y = y;
}
Point operator*(int times){
Point pos(x * times, y * times);
return pos;
}
/**
* 위의 함수만 정의하면
* Point p2 = 2 * p1;
* 해당 식을 실행할 수 없다.
* 교환 법칙을 위해서는 별도로 외부 선언이 필요하다.
*/
friend Point operator*(int times, Point & ref);
};
friend Point operator*(int times, Point & ref){
Point pos(ref.x * times, ref.y * times);
return pos;
}
cout, cin, endl
입출력에 사용되는 <<
또한 연산자이며 구조는 대략 다음과 같다.
namespace mystd{
using namespace std;
class ostream:{
public:
Ostream& operator<< (char * str){
printf("%s", str);
return *this;
}
Ostream& operator<< (char str){
printf("%c", str);
return *this;
}
Ostream& operator<< (int num){
printf("%d", num);
return *this;
}
Ostream& operator<< (double e){
printf("%f", e);
return *this;
}
Ostream& operator<< (ostream& (*fp)(Ostream &ostm)){ // 함수 포인터
return fp(*this); // 해당 함수 실행
}
Ostream& endl(ostream &ostm){
ostm<<'\n';
flush(stdout);
return ostm;
}
}
ostream cout;
}
int main(){
using mystd::cout;
using mystd::endl;
cout<<33<<endl;
return 0;
}
이를 이용해서 객체의 출력도 cout를 통해서 할 수 있다.
class Point{
private:
..
public:
..
Friend ostream& operator<<(ostream&, const Point&);
}
Ostream& operator<<(ostream& os, const Point pos){
os << '[' << pos.xpos << ", " << pos.ypos << "]";
return os;
}
대입 연산자 오버로딩
대입연산자도 오버로딩이 가능하며, 이는 Copy constructor의 특성과 비슷하다.
- 정의하지 않으면 디폴트 대입 연산자가 삽입된다.
- 디폴트 대입 연산자는 shallow copy이다.
- deep copy 필요시 직접 정의해야한다.
int main(){
Point pos1(5, 7);
Point pos2 = pos1; // Copy constructor에 의한 객체 생성
Point pos3(9, 10);
pos3 = pos1; // 두 객체 모두 초기화가 진행된 상태이며 대입 연산자 실행
}
상속 구조에서의 대입 연산자 호출
- 생성자 : 자식 클래스의 생성자는 아무런 명시를 하지 않아도, 부모 클래스의 생성자를 호출한다.
- 대입 : 자식 클래스에서 명시를 하지 않으면, 부모 클래스의 대입 연산자가 호출되지 않고 멤버 대 멤버 복사를 진행할 수 없다,
배열의 인덱스 연산자 오버로딩
#include <iostream>
using namespace std;
class BoundaryCheckIntArray{
private:
int * arr;
int arrlen;
BoundaryCheckIntArray(const BoundaryCheckIntArray& arr);
BoundaryCheckIntArray& operator=(const BoundaryCheckIntArray& arr);
// BoundaryCheckIntArray cpy1 = orgin, cpy2 = orgin 같은 문장을 막기 위해 private로 선언
public:
BoundaryCheckIntArray(int len)
: arrlen(len)
{
arr = new int[len];
}
int& operator[](int idx){
if(idx < 0 || idx >= arrlen){
cout << "out of index" << endl;
exit(1);
}
return arr[idx];
}
// 단순 값 참조를 위한 const 함수 오버로딩
// 함수의 const 선언 유무도 오버로딩의 조건에 해당한다.
int& operator[](int idx) const{
if(idx < 0 || idx >= arrlen){
cout << "out of index" << endl;
exit(1);
}
return arr[idx];
}
~BoundaryCheckIntArray(){
delete[] arr;
}
};
int main(){
BoundaryCheckIntArray mArr(10);
mArr[0] = 1;
mArr[1] = 2;
cout << mArr[1] << endl;
}