스트림 삽입 및 추출 연산자 오버로딩


  • 구현하고자 하는 예시
Point p1{10,20};
Point p2{30,40};

//복잡한 멤버함수를 호출하는 것 대신
~~p1.ShowPosition();~~
~~p2.ShowPosition();~~

//기본 자료형 처럼 stream 출력 가능하도록 한다.
std::cout << p1 << std::endl; // [10,20] 출력
std::cout << p2 << std::endl; // [30,40] 출력

Point p3;
std::cin >>p3; //50,60 입력

객체를 cout, cin을 사용 할 수 있도록 해보자. 좀 더 편해지지 않을까?

cout 간단한 예제


  • 구체적으로 들어가기 전에! ‘cout이란 무엇인가..?’ 알아보자!
  • << 은 cout 객체 (ostream class)의 Overloading된 연산자이다.
#include <iostream>

class MyOstream
{
public:
	void operator << (int val)// << 연산자 정의 해줘야함
	{
		printf("%d", val);
	}
};

int main()
{

	MyOstream mycout;

	mycout << 10; //mycout.operator<<(10)로 변환 함
	std::cout << 10;//즉, 이는 cout이 ostream의 객체였다는 것!
	
	return 0;
}

우리가 MyOstream에 정의한 내용을 컴파일러가 변환을 하여 해석을 한다. 여기서 코드를 보면
’Mycout << 10;’ 과 ‘std::cout <<10;’이 같은 표현이라는 것을 알 수 있다.

  • 즉, std::cout의 cout은 MyOstream의 객체 mycout과 같이 ‘어떠한 클래스의 객체라는 것’을 알 수 있다.
  • 마우스 커서를 올려서 확인해보면 아래와 같이 std namespace안의 ostream 클래스의 객체라는 것을 알 수 있다.

스트림 삽입 연산자


이제 우리가 원래 해 보려던 객체를 스트림에 적용해 보자! 근데 한 가지 문제점이 있다.

int main()
{
	std::cout << p1;//cout.operator<<(p1) or opeartor<<(cout,p1)으로 컴파일러가 해석
}
💡

위의 코드가 성립하도록 하기 위해서는

두 가지 방법으로 접근이 가능하다.

  1. cout의 클래스 ostream의 내용을 변경하는 것
    1. 이미 표준으로 설정된 클래스를 수정하는 것은 위험하다!
  2. 전역 함수로 Overloading하는 것
    1. 이 방법을 사용할 것이다.

예시


void operator<<(std::ostream& os, Point rhs)//전역 함수
{
	
}

int main()
{
	Point p1{2,3};
	
	std::cout << p1;
}
💡

여기서 잠깐! 위와 같은 형식으로 진행하지만 알아두어야 할 것이 있다.

cout은 복사가 방지 되어있기 때문에 반드시 참조자를 사용해야한다.

#include <iostream>

class Point
{
	// 중략
	//......
	
	friend void operator<<(std::ostream& os,const Point& rhs)
	{
		os << "[" << rhs.xpos << "," << rhs.ypos << "]" << std::endl;
	}
};

int main()
{
	Point p1{ 2,3 };
	std::cout << p1;//operator<<(cout,p1);으로 변환 됨

}

os는 cout을 매개변수로 받기 때문에 os가 cout의 역할을 하고 있다. 이제 어떻게 접근해야 하는지 알아봤으니 좀 더 자세하게 들어가 보자.

스트립 삽입 연산자 오버로딩, 구현


그럼 연속적으로 객체 2개를 넣을때는 어떻게해야 할까?

std::cout << p1 << p2;
  • ostream의 참조자를 반환하여 chain insertion이 가능하도록 구현해야 함
    • ‘참조자’를 반환하지 않으면 cout << p1 << p2 와 같은 연산 불가하기 때문에
    • 또한 기본적으로 cout 객체는 복사가 불가
friend ostream& operator<<(ostream& os, const Point& rhs)
{
		os<<"[" << rhs.xpos << "," << rhs.ypos << "]";
		return os;
}

예시 코드


int main()
{
	Point p1{ 2,3 };
	Point p2{ 3,4 };

	std::cout << p1;//operator<<(cout,p1);으로 변환 됨

	std::cout << p1 << p2 << std::endl;//불가능
}

불가능한 이유가 뭘까?

return 타입이 없을 때의 컴파일러의 해석을 단계별 접근해 보자!

(std::cout << p1) << p2 << std:: endl;
 operator(cout, p1) << p2 << std::endl;
void << p2 << std::endl;

4 .

operator<<(void,p2) << std::endl; //불가능!

//정답) std::cout << p2 << std::endl;로 변경이 되어야 함

즉, return 값이 std::ostream& 이 되어야 한다. ( cout은 복사가 되지 않는다는 것을 명심해야 함)

스트림 추출 연산자 오버로딩, 구현

  • 우측 매개변수(rhs)는 const가 아님에 유의
    • rhs의 값이 변경이 되기 때문에!
friend istream& operator>>(istream& is , Point& rhs)
{
		int x = 0 , y = 0;
		is >> x >> y;
		rhs = Point{x,y};
		return  is; //for chain insertion
}

int main()
{
	int a;
	std::cin >> a;

	Point p3{ 0,0 };
	std::cin >> p3; //operator>>(cin,p3);
}

rhs = Point{x,y};에서 살짝 헷갈린 부분이 있어서 간단하게 정리를 해보겠다.

  • rhs는 p3 그 자체다, 즉 rhs = Point{x,y}의 의미는 “p3에 Point{x,y}의 객체를 대입하라.”라는 의미이다.
profile
개발로그

0개의 댓글