C++ Chapter 9.3 : 입출력 연산자 오버로딩

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!


chapter 9. 연산자 오버로딩 : 입출력 연산자 오버로딩

입출력 연산자 : <<, >>

추가적인 설명은 이전 포스트인 9.1 연산자 오버로딩 시작하기 참고하기

🔔 입출력 클래스 istream, ostream

구조

  • istream : 입력을 수행하는 클래스
  • ostream : 출력을 수행하는 클래스
  • iostream
    • #include <iostream>으로 우리에게 익숙한 그 클래스
    • istreamostream의 자식이다. 두 클래스를 상속 받음(다중 상속)
    • 입출력과 관련된 모든 데이터와 기능을 담고 있다.

std::cout

std::coutostream타입의 객체이다.

std::cout << 100 << endl;   // 100 출력
std::cout << 3.14f << endl; // 3.14f 출력
std::cout << "Hello, World!" << endl; // "Hello, World!" 출력
...

위 코드를 실행했을 때 생기는 일 👉 ostream 클래스 안에 구현되어 있는 « 연산자 오버로딩이 호출된다.

  • ostream클래스 안에 여러가지 자료형 타입에 대하여 << 연산자 오버로딩이 구현 되어 있다.
  • ostream클래스의 멤버 함수로서
    • 자기 자신(ostream타입 객체)
    • 인수로 받은 것
      • (이 인수의 자료형 종류에 따라 여러가지 형태로 ostream 클래스 내에 오버로딩 되어있다)
  • ostream타입의 객체가 출력 연산자(<<)를 만나면 출력연산자 우측에 있는 것을 인수로 하는, ostream 클래스의 멤버 출력 연산자 오버로딩을 호출한다.
    • ostream 클래스의 멤버 출력 연산자 오버로딩은 인수로 넘겨받은 ,출력연산자 우측에 있는 데이터를 출력하는 일을 수행한다.
      • ostream 클래스 안에 그렇게 일을 하도록 구현이 되어 있다.

📜ostream 클래스 내부

ostream& operator<<(bool& val);
ostream& operator<<(short& val);
ostream& operator<<(unsigned short& val);
ostream& operator<<(int& val);
ostream& operator<<(unsigned int& val);
ostream& operator<<(long& val);
ostream& operator<<(unsigned long& val);
ostream& operator<<(float& val);
ostream& operator<<(double& val);
ostream& operator<<(long double& val);
ostream& operator<<(void* val);
...

std::cin

std::cinistream타입의 객체이다.

int a;
float f;
string s;

std::cin >> a;
std::cin >> f;
std::cin >> s;
...

위 코드를 실행했을 때 생기는 일 👉 istream 클래스 안에 구현되어 있는 » 연산자 오버로딩이 호출된다.

  • istream클래스 안에 여러가지 자료형 타입에 대하여 `` 연산자 오버로딩이 구현 되어 있다.
  • istream클래스의 멤버 함수로서
    • 자기 자신(istream타입 객체)
    • 인수로 받은 것
      • (이 인수의 자료형 종류에 따라 여러가지 형태로 오버로딩 되어있다)
  • istream타입의 객체가 입력 연산자(>>)를 만나면 출력연산자 우측에 있는 것을 인수로 하는, istream 클래스의 멤버 출력 연산자 오버로딩을 호출한다.
    • istream 클래스의 멤버 출력 연산자 오버로딩은 인수로 넘겨받은 ,입력연산자 우측에 있는 데이터를 입력하는 일을 수행한다.
      • istream 클래스 안에 그렇게 일을 하도록 구현이 되어 있다.

📜istream 클래스 내부

istream& operator>>(bool& val);
istream& operator>>(short& val);
istream& operator>>(unsigned short& val);
istream& operator>>(int& val);
istream& operator>>(unsigned int& val);
istream& operator>>(long& val);
istream& operator>>(unsigned long& val);
istream& operator>>(float& val);
istream& operator>>(double& val);
istream& operator>>(long double& val);
istream& operator>>(void* val);
...


🔔 전역 함수로 구현하기

입출력 연산자는 전역 함수로만 구현되는 이유

>> << 입출력 연산자 오버로딩은 멤버 함수로는 구현할 수 없고 전역 함수로만 구현할 수 있다.

A a(3);  // A타입의 a 객체 생성

std::cout << a;
  • 만약 << 오버로딩을 멤버 함수로 구현하려면 ostream 타입의 객체이자 왼쪽 피연산자인 std::cout이 호출하여야 한다.
    • 왼쪽 피연산자가 멤버 함수를 호출하는 객체가 되고 오른쪽 피연산자는 인수가 되기 때문!
  • 따라서 멤버 함수로 만들기 위해선 ostream 클래스의 멤버 함수로 구현을 해야 하는데, ostream 클래스는 C++ 표준이기 때문에 직접 ostream 클래스의 멤버 함수로 A 타입의 객체를 인수로 받는 <<연산자 오버로딩을 만들어 줄 수는 없다.
    • C++ 표준으로 구현된 것들은 사용자가 수정할 수 없기 떄문이다.
  • 따라서 입출력 연산자 오버로딩은 전역함수로 구현할 수 밖에 없다.


출력 연산자 « 오버로딩

private 멤버 사용을 위해 전역 함수로 구현한 후 Point 클래스의 friend로 지정하기

#include <iostream>
using namespace std;

class Point
{
private:
	double m_x, m_y, m_z;

public:
	Point(double x = 0.0, double y = 0.0, double z = 0.0)
		: m_x(x), m_y(y), m_z(z)
	{}

	friend std::ostream& operator << (std::ostream &out, const Point &point)
	{
		out << "( " << point.m_x << ", " << point.m_y << ", " << point.m_z << " )";

		return out;
	}
};
friend std::ostream& operator << (std::ostream &out, const Point &point)
{
	out << "( " << point.m_x << ", " << point.m_y << ", " << point.m_z << " )";

	return out;
}
  • << 연산자 오버로딩이 Point 클래스 안에 구현되어 있어 멤버 함수라고 착각할 수도 있으나 전역 함수다!
    • 전역 함수인데 Point 클래스의 friend로 지정될 때 함수의 바디를 같이 구현해준 것 뿐이다.
    • Point 클래스 안에서 구현 됐지만 멤버 함수가 아닌 외부의 전역 함수이다.
  • 인수가 되는 두 피연산자를 참조로 넘겨 받는다.
    • 왼쪽 피연산자 👉 out : ostream 타입 객체 (대표적으로 cout)
    • 오른쪽 피연산자 👉 point : Point 타입 객체
  • out « ”( “ « point.m_x « ”, “ « point.m_y « ”, “ « point.m_z « ” )”;
    • ostream 클래스 안에는 char, int, double 등등 이런 기본적인 자료형들에 대한 출력 연산자 오버로딩이 구현되어 있다.
      • ostream 클래스 안에 미리 구현되어 있는 것들이 호출되어 출력을 수행한다.
  • 인수로 넘겨 받은 ostream 객체를 참조하는 outostream& ostream 참조 타입으로 리턴하게 된다.
    • 리턴을 ostream 타입으로 하는 이유
      • cout « p1 « p2; 이런 경우에 chaining 식으로 출력 연산자를 호출하기 위해서이다.
        1. cout << p1 부분에서 ostream 객체를 리턴받아야
        2. (cout « p1의 결과인 ostream타입 객체) << p2 이렇게 해서 p2도 출력해야 하기 때문이다.
int main()
{
	Point p1(0.0, 0.1, 0.2), p2(3.4, 1.5, 2.0);

	cout << p1 << " " << p2 << endl;

	return 0;
}
💎출력💎

(0.0, 0.1, 0.2) (3.4, 1.5, 2.0)
  • 다시 한번 더 강조하지만! coutostream 타입의 객체이다. 👀
  • Point 객체를 정의한대로 출력하도록 직접 오버로딩한 출력 연산자가 호출되는 부분과 처리 순서는 아래와 같다. << 부분이 위에서 직접 오버로딩한 출력 연산자가 호출되는 부분이다.

    ((((cout << p1) « ” “) << p2) « endl);


입력 연산자 » 오버로딩

입력 연산자도 출력 연산자와 똑같은 방식으로 오버로딩 해주면 된다. 다만, 오른쪽 피연산자로 들어오는 객체에 입력을 해주어야 하기 때문에 오른쪽 피연산자를 const 타입으로 쓰면 안된다.

  • friend std::istream& operator » (std::istream &in, Point &point)
    • 두번째 매개 변수에 const 써주면 안된다.
    • 변수에 입력을 해준다는건 변수의 값을 바꾼다는 의미인데 const면 입력을 할 수가 없음!

마찬가지로 istream 타입의 참조 객체를 리턴한다.

#include <iostream>

using namespace std;

class Point
{
private:
	double m_x, m_y, m_z;

public:
	Point(double x = 0.0, double y = 0.0, double z = 0.0)
		: m_x(x), m_y(y), m_z(z)
	{}

	friend std::istream& operator >> (std::istream &in, Point &point)   // 👈 입력 연산자 오버로딩 
	{
		in >> "( " >> point.m_x >> " " >> point.m_y >> " " >> point.m_z >> " )";
		return in; 
	}

    friend std::ostream& operator << (std::ostream &out, const Point &point)  // 출력 연산자 오버로딩
	{
		out << "( " << point.m_x << ", " << point.m_y << ", " << point.m_z << " )";

		return out;
	}
};

int main()
{
	Point p1, p2;   // 각각 (0.0, 0.0, 0.0)으로 초기화 된 상태

	cin >> p1 >> p2;
    cout << p1 << " " << p2 << endl; 


	return 0;
}
💎입력💎 1 2 3 4 5 6
💎출력💎 (1, 2, 3) (4, 5, 6)


파일 입출력 해보기

cin, cout, 즉 Console 입출력 말고 파일에 입출력을 해보자!

#include <fstream>을 해주어야 하며 ofstream, ifstream 타입의 객체를 정의해야 한다.

#include <iostream>
#include <fstream>
#include "Point.h"

using namespace std;

int main()
{
	ofstream of("out.txt"); // 출력할 파일 스트림

	Point p1(0.0, 0.1, 0.2), p2(3.4, 1.5, 2.0);

	cout << p1 << " " << p2 << endl; // 콘솔로 출력
	of << p1 << " " << p2 << endl; // "out.txt" 파일로 출력
	
    of.close(); 

	return 0;
}
  • 실행하면 프로젝트 로컬 디렉터리에 가보면 “out.txt”라는 이름의 텍스트 파일이 생긴다.
    • 파일의 내용은 “(0.0, 0.1, 0.2) (3.4, 1.5, 2.0)”


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기


댓글남기기