[C++] Chapter 10 - 연산자 오버로딩 1

Lee Jeong Min·2021년 1월 12일
0

Cpp

목록 보기
10/16
post-thumbnail

10-1 연산자 오버로딩의 이해와 유형

operator+라는 이름의 함수

함수가 오버로딩이 되어 다양한 기능을 제공하는 것처럼 연산자 또한 오버로딩이 가능하다.
FirstOperationOverloading.cpp

#include <iostream>
using namespace std;

class Point
{
private:
	int xpos, ypos;
public:
	Point(int x = 0, int y = 0) : xpos(x), ypos(y)
	{

	}
	void ShowPosition() const
	{
		cout << '[' << xpos << ", " << ypos << ']' << endl;
	}
	Point operator+(const Point& ref)
	{
		Point pos(xpos + ref.xpos, ypos + ref.ypos);
		return pos;
	}
};

int main(void)
{
	Point pos1(3, 4);
	Point pos2(10, 20);
	Point pos3 = pos1.operator+(pos2); // Point pos3 = pos1+pos2;

	pos1.ShowPosition();
	pos2.ShowPosition();
	pos3.ShowPosition();
	return 0;
}

위의 main함수 내에서 pos3을 pos1.operator+(pos2)라고 정의하였지만 pos1+pos2로 대입을 하여도 컴파일이 가능함. 즉 '+' 자체가 .operator+로 해석이되어 컴파일러가 처리해준다는 것을 알 수 있음.

연산자를 오버로딩 하는 방법 두 가지

  • 멤버함수에 의한 연산자 오버로딩

  • 전역함수에 의한 연산자 오버로딩

    	pos1.operator+(pos2); // 멤버함수 오버로딩
    
    	operator_(pos1, pos2); // 전역함수 오버로딩

두 가지 동시에 오버로딩이 된 경우 멤버함수 먼저 우선시되어 호출

전역함수 기반 오버로딩 예시
GFunctionOverloading

#include <iostream>
using namespace std;

class Point
{
private:
	int xpos, ypos;
public:
	Point(int x = 0, int y = 0) : xpos(x), ypos(y)
	{

	}
	void ShowPosition() const
	{
		cout << '[' << xpos << ", " << ypos << ']' << endl;
	}
	friend Point operator+(const Point& pos1, const Point& pos2);
};

Point operator+(const Point& pos1, const Point& pos2)
{
	Point pos(pos1.xpos + pos2.xpos, pos1.ypos + pos2.ypos);
	return pos;
}

int main(void)
{
	Point pos1(3, 4);
	Point pos2(10, 20);
	Point pos3 = pos1 + pos2;

	pos1.ShowPosition();
	pos2.ShowPosition();
	pos3.ShowPosition();
	return 0;
}

위 예제의 friend선언을 보고 operator+ 함수가 Point 클래스의 private 영역에 접근이 가능함 뿐만이 아니라 이 클래스가 +연산에 대해서 연산자 오버로딩이 되어 있음을 확인할 수 있어야 함!

연산자 오버로딩을 하는데 있어서의 주의사항

  • 본래의 의도를 벗어난 형태의 연산자 오버로딩 X
  • 연산자의 우선순위와 결합성은 바뀌지 않는다.
  • 매개변수의 디폴트 값 설정이 불가능 하다.
  • 연산자의 순수기능 까지 빼앗을 수 없다.

10-2 단항 연산자의 오버로딩

전위증가와 후위증가의 구분

C++에서 전위 및 후위 연산에 대한 해석 방식
++pos -> pos.operator++();
pos++ -> pos.operator++(int);

--pos -> pos.operator--();
pos-- -> pos.opreator--(int);

관련예제
PostOpndOveraloding.cpp

#include <iostream>
using namespace std;

class Point
{
private:
	int xpos, ypos;
public:
	Point(int x= 0, int y = 0): xpos(x), ypos(y)
	{}
	void ShowPosition() const
	{
		cout << "[" << xpos << ", " << ypos << "]" << endl;
	}
	Point& operator++() // 전위 증가
	{
		xpos += 1;
		ypos += 1;
		return *this;
	}
	const Point operator++(int) // 후위 증가
	{
		const Point retobj(xpos, ypos); // const Point retobj(*this);
		xpos += 1;
		ypos += 1;
		return retobj;
	}
	friend Point& operator--(Point& ref);
	friend const Point operator--(Point& ref, int);
};

Point& operator--(Point& ref) // 전위 감소
{
	ref.xpos -= 1;
	ref.ypos -= 1;
	return ref;
}

const Point operator--(Point& ref, int) // 후위감소
{
	const Point retobj(ref); // const 객체라고함
	ref.xpos -= 1;
	ref.ypos -= 1;
	return retobj;
}

int main(void)
{
	Point pos(3, 5);
	Point cpy;
	cpy = pos--;
	cpy.ShowPosition();
	pos.ShowPosition();

	cpy = pos++;
	cpy.ShowPosition();
	pos.ShowPosition();
	return 0;
}

후위 증가의 효과를 정의하기위해 operator++(int) 와 operator--(int)내의 함수에서 const를 사용하여 함수 내에서 retobj의 변경을 막겠다는 의도와 const형 임시객체를 반환하고 그 뒤에 값이 변경되는 후위증가 및 감소의 특성을 반영하였다.

위 예제와 관련한 전위 증감과 후위 증감에 대한 것
일반적으로 C++의 연산 특성은 다음과 같다.
int main(void)
{

int num = 100;

(num++)++;  // 컴파일에러
(num--)--;  // 컴파일 에러

++(++num); // 컴파일 성공
--(--num); // 컴파일 성공

}

PostOpndOveraloding.cpp 예제에서 후위 증감함수들을 const를 사용하여 표현한 이유는 위의 특성을 반영하여 후위증감 시 const객체를 반환하지만 함수들은 증가의 경우 함수가 const함수가 아니고 감소의 경우 매개변수 참조자가 const로 선언되지 않았기 때문에 에러를 발생시킨다.


10-3 교환법칙 문제의 해결

일반적으로 연산자 오버로딩에서 pos라는 객체가 존재한다고 할시
pos x 3은 되지만 (pos.operator*(3))
3 x pos와 같이 연산을 하려면 전역함수의 형태로 곱셈 연산자를 오버로딩 하는 수 밖에 없다.

관련예제
CommuMultipleOperation.cpp

#include <iostream>
using namespace std;

class Point
{
private:
	int xpos, ypos;
public:
	Point(int x = 0, int y = 0) : xpos(x), ypos(y)
	{

	}
	void ShowPosition() const
	{
		cout << "[" << xpos << ", " << ypos << "]" << endl;
	}
	Point operator*(int times)
	{
		Point pos(xpos * times, ypos * times);
		return pos;
	}
	friend Point operator*(int times, Point& ref);
};

Point operator*(int times, Point& ref)
{
	return ref * times;
}

int main(void)
{
	Point pos(1, 2);
	Point cpy;

	cpy = 3 * pos;
	cpy.ShowPosition();

	cpy = 2 * pos * 3;
	cpy.ShowPosition();
	return 0;
}

전역함수에 Pointer operator*(int times, Point& ref)로 정의를 해주어 곱셈 연산자의 교환법칙이 성립하도록 하였다.


10-4 cout, cin 그리고 endl의 정체

cout와 endl 이해하기

ConsoleOutput.cpp

#include <iostream>
namespace mystd
{
	using namespace std;

	class ostream
	{
	public:
		void operator<< (const char* str)
		{
			printf("%s", str);
		}
		void operator<< (char* str)
		{
			printf("%c", str);
		}
		void operator<< (int num)
		{
			printf("%d", num);
		}
		void operator<< (double e)
		{
			printf("%g", e);
		}
		void operator<< (ostream& (*fp)(ostream& ostm))
		{
			fp(*this);
		}
	};

	ostream& endl(ostream& ostm)
	{
		ostm << '\n';
		fflush(stdout);
		return ostm;
	}

	ostream cout;
}

int main(void)
{
	using mystd::cout;
	using mystd::endl;

	cout << "Simple String";
	cout << endl;
	cout << 3.14;
	cout << endl;
	cout << 123;
	endl(cout);
}

예제를 실행하면 이상하게 '\n'이 원래는 공백으로 표시되어 결과가 나와야하는데 아스키 코드 값인 10이 출력이 된다... 왜 이런 현상이 발생하는 지는 모르겠다.. ---> main함수에서 using std::cout, std::endl을 사용하여 컴파일을 하면 정상적으로 작동이 된다.

<<, >> 연산자의 오버로딩

<<, >> 를 오버로딩하려면 ostream 클래스 멤버함수에 정정하거나 전역함수에 의한 방법 둘 중하나를 해야하는데 전자는 불가능하므로 전역함수를 사용하는 방법을 선택!

PointConsoleOutput.cpp

#include <iostream>
using namespace std;

class Point
{
private:
	int xpos, ypos;
public:
	Point(int x = 0, int y = 0) : xpos(x), ypos(y)
	{

	}
	void ShowPosition() const
	{
		cout << '[' << xpos << ", " << ypos << ']' << endl;
	}
	friend ostream& operator<< (ostream&, const Point&);
};

ostream& operator<<(ostream& os, const Point& pos)
{
	os << '[' << pos.xpos << ", " << pos.ypos << ']' << endl;
	return os;
}

int main(void)
{
	Point pos1(1, 3);
	cout << pos1;
	Point pos2(101, 303);
	cout << pos2;
	return 0;
}

cout가 ostream의 클래스 객체이고 인자로 ostream형 객체와 Point 참조형을 받아 다음과 같이 함수를 구성하면 된다.

profile
It is possible for ordinary people to choose to be extraordinary.

0개의 댓글