본문 바로가기
  • 쓸쓸한 개발자의 공부방
C++

[C++] cout, endl 원리, operator<< 재정의

by 심찬 2022. 1. 2.

 

cout 원리

 

cout 은 ostream 타입의 객체이다.

모든 primitive 타입에 대해서 operator << 연산자 함수가 구현되어 있다.

cout << 3      ==>    cout.operator<<(int)

cout << 3.4    ==>    cout.operator<<(double)

 

#include <iostream>
using namespace std;

int main()
{
    printf("%d\n", 3); // 함수
    
    cout << 3;   // cout.operator<<(3)   => operator<<(int)
    cout << endl;
    
    cout << 3.4; // cout.operator<<(3.4)
    cout << endl;
    
    cout.operator<<(3);
    cout << endl;
    
    cout.operator<<(3.4);
    
}

 

cout 원리 이해를 위한 ostream 클래스 정의

 

아래와 같이 간단하게 std::cout 연산자 재정의를 해볼 수 있다.

#include <cstdio>

namespace std 
{
    class ostream
    {
    public:
        ostream& operator<<(int n) { printf("%d", n); return *this;}
        ostream& operator<<(double d) 
        {
             printf("%f", d);
             return *this;
        }
    };
    ostream cout;    
}

int main()
{
    std::cout << 3;  // cout.operator<<(3)
    std::cout << 3.4;
    std::cout << 3 << 3.4;
}

 

ostream과 c++ 표준

 

1998년 표준화 이전에는 ostream 클래스

  유니코드 등의 문자열 출력 불가

 

1998년 표준화 이후

  basic_ostream<> 클래스 탬플릿

  유니코드 등의 문자열 출력 가능

 

/*

// 1998년 C++이 표준화 되기 이전에 사용하던 클래스
class ostream 
{
    //...
};

// 1998년 표준화 이후  -- 코드가 복잡해졌지만 다양한 형태를 처리해줌.
template<typename T, typename Tratis = char_traits<T>>
class basic_ostream
{
};
typedef basic_ostream<char> ostream;
typedef basic_ostream<wchar_t> wostream;
ostream cout;
wostream wcout;
*/

#include <iostream>

int main()
{
    std::cout << "hello";
    std::wcout << L"hello";  //L은 유니코드를 의미함. 그래서 wcout를 사용해야 함.
}

사용자 정의 타입과 cout

 

cout의 특징

다양한 타입의 값을 출력할 때 서식 문자열(%d, %f ... ) 전달할 필요가 없다.

operator<< 함수가 다양한 타입에 대해서 오버로딩 되어 있다.

   cout.operator<<(int)

   cout.operator<<(double)

 

#include <iostream>

class Point
{
    int x;
    int y;
public:
    Point(int a = 0, int b = 0) : x(a), y(b) {}
};

int main()
{
    int    n = 0;
    double d = 3.4;    

    std::cout << n; // cout.operator<<(int)
    std::cout << d; // cout.operator<<(double)

    Point pt(1,2);
    std::cout << pt; // cout.operator<<(Point)
    
                // operator<<(cout, pt)
                // operator<<(ostream, Point)               
}

 

아래와 같이 에러가 발생된다.

main.cpp:20:15: error: no match for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream’} and ‘Point’)
   20 |     std::cout << pt; // cout.operator<<(Point)
      |     ~~~~~~~~~ ^~ ~~
      |          |       |
      |          |       Point
      |          std::ostream {aka std::basic_ostream<char>}
In file included from /usr/include/c++/9/iostream:39,
                 from main.cpp:1:
/usr/include/c++/9/ostream:108:7: note: candidate: ‘std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__ostream_type& (*)(std::basic_ostream<_CharT, _Traits>::__ostream_type&)) [with _CharT = char; _Traits = std::char_traits; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream]’
  108 |       operator<<(__ostream_type& (*__pf)(__ostream_type&))
      |       ^~~~~~~~

 

 

사용자 정의 타입을 cout으로 출력할 수 있게 하려면

 

operator<< 연산자 함수를 멤버가 아닌 일반 함수로 제공한다.

private 멤버에 접근하기 위해서는 friend 함수로 선언한다.

#include <iostream>

class Point
{
    int x;
    int y;
public:
    Point(int a = 0, int b = 0) : x(a), y(b) {}
    
    friend std::ostream& 
    operator<<(std::ostream& os, const Point& pt);
};

//std::ostream& operator<<(const std::ostream& os, const Point& pt)
//                         const 있어면 에러 주의
//  const의 경우 상수 함수 호출이 가능한데 const에 의해 
//  os.operator<<(pt.x) 호출시 문제가 된다. 그래서 const 넣어주면 안된다.

std::ostream& operator<<(std::ostream& os, const Point& pt)
{
    os << pt.x << ", " << pt.y;
    return os;
}

int main()
{
    Point pt(1,2);
    std::cout << pt << std::endl;  // operator<<(cout, pt)
            
                    
}

 

 

cout과 endl

std::cout은 객체 이므로 다양한 멤버 함수가 있다.

 

endl 역시 함수이다.

  인자로 cout을 전달받아서 개행 문자를 출력하고 출력 버퍼를 비우고 cout을 반환함.

#include <iostream>
using namespace std;

int main()
{
    cout << endl; 
    
    cout.put('\n');
    cout.flush();
}

 

endl이 함수이기 때문에 아래와 같이 다양한 방식으로 사용이 가능하다.

#include <iostream>
using namespace std;

ostream& myendl( ostream& os)
{
    os.put('\n');
    os.flush();
    return os;
}
int main()
{
    cout << endl << "A";
    endl(cout) << "B";
    myendl(cout) << "C";
    cout << endl; // cout.operator<<(endl)
                  // cout.operator<<(함수포인터)
    cout << myendl;
    cout << "------------------" <<endl;
}

'C++' 카테고리의 다른 글

[C++] Design Patterns (디자인 패턴) - GoF  (0) 2022.01.26
[C++] exception (예외처리)  (0) 2022.01.10
[C++] 연산자 재정의 (operator overloading)  (0) 2022.01.01
[C++] 다중 상속 (multiple inheritance)  (0) 2021.12.31
[C++] 상속과 RTTI  (0) 2021.12.30

댓글