C++ Chapter 9.3 : 입출력 연산자 오버로딩
카테고리: Cpp
태그: Cpp Programming
인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!
chapter 9. 연산자 오버로딩 : 입출력 연산자 오버로딩
입출력 연산자 :
<<
,>>
추가적인 설명은 이전 포스트인 9.1 연산자 오버로딩 시작하기 참고하기
🔔 입출력 클래스 istream, ostream
구조
istream
: 입력을 수행하는 클래스ostream
: 출력을 수행하는 클래스iostream
- #include <iostream>으로 우리에게 익숙한 그 클래스
istream
과ostream
의 자식이다. 두 클래스를 상속 받음(다중 상속)- 입출력과 관련된 모든 데이터와 기능을 담고 있다.
std::cout
std::cout은
ostream
타입의 객체이다.
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::cin은
istream
타입의 객체이다.
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 클래스 안에서 구현 됐지만 멤버 함수가 아닌 외부의 전역 함수이다.
- 전역 함수인데 Point 클래스의
- 인수가 되는 두 피연산자를 참조로 넘겨 받는다.
- 왼쪽 피연산자 👉 out : ostream 타입 객체 (대표적으로 cout)
- 오른쪽 피연산자 👉 point : Point 타입 객체
out
« ”( “ « point.m_x « ”, “ « point.m_y « ”, “ « point.m_z « ” )”;ostream
클래스 안에는 char, int, double 등등 이런 기본적인 자료형들에 대한 출력 연산자 오버로딩이 구현되어 있다.ostream
클래스 안에 미리 구현되어 있는 것들이 호출되어 출력을 수행한다.
- 인수로 넘겨 받은 ostream 객체를 참조하는 out 은
ostream&
ostream 참조 타입으로 리턴하게 된다.- 리턴을 ostream 타입으로 하는 이유
- cout « p1 « p2; 이런 경우에 chaining 식으로 출력 연산자를 호출하기 위해서이다.
- cout
<<
p1 부분에서 ostream 객체를 리턴받아야 - (cout « p1의 결과인 ostream타입 객체)
<<
p2 이렇게 해서 p2도 출력해야 하기 때문이다.
- cout
- cout « p1 « p2; 이런 경우에 chaining 식으로 출력 연산자를 호출하기 위해서이다.
- 리턴을 ostream 타입으로 하는 이유
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)
- 다시 한번 더 강조하지만!
cout
은 ostream 타입의 객체이다. 👀 - 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)”
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기