2011-01-08 7 views
18

이 잘 사용자가이 같은 스트림 조종을 정의 할 수 있습니다 것으로 알려져있다 :스트림 조작기는 어떻게 작동합니까?

ostream& tab(ostream & output) 
{ 
    return output<< '\t'; 
} 

그리고는이 같은 주()에 사용할 수 있습니다

cout<<'a'<<tab<<'b'<<'c'<<endl; 

이 작업을 수행하는 방법을 설명해주십시오 모두 작동합니까? 연산자 < <이 두 번째 매개 변수로 사용하여 ostream &을 가져오고 반환하는 함수를 가리키는 포인터 인 경우 왜 필요한지 설명하십시오. 어떤 기능이 걸릴 ostream에 &을 반환하지 않을 경우 잘못하지만 무효 대신 ostream에 &이었다 것인가?

"dec", "hex"manipulators가 변경되지 않으면 효과가 발생하는 이유는 흥미 롭습니다.하지만 사용자 정의 조작자는 항상 각 스트리밍에 적용해야합니까?

+0

마지막 단락에서 질문을 명확하게 할 수 있습니까? 나는 네가 의미하는 것을 이해하지 못한다. –

+0

내 말은 만약 당신이 cout << hex << a << b << endl << c; 모든 숫자는 헥스베이스로 표시됩니다. 그러나 만약 내가 cout << 'a'<< tab << 'b'<< 'c'<< endl로 쓰면 "tab"은 오직 'b ',하지만'c '는 아닙니다. – Narek

+0

@Narek :'tab'에 대한 정의는 탭을 인쇄하고'hex'처럼 스트림 설정을 변경하지 않습니다. –

답변

20

표준은 basic_ostream 클래스 템플릿에서 다음 operator<< 과부하를 정의

basic_ostream<charT,traits>& operator<<(
    basic_ostream<charT,traits>& (*pf) (basic_ostream<charT,traits>&)); 

효과 : 없음. 형식화 된 출력 함수로 동작하지 않습니다 (27.6.2.5.1에서 설명).

반품 : pf(*this).

매개 변수는 std::ostream에 대한 참조를 가져 와서 반환하는 함수에 대한 포인터입니다.

즉,이 서명이 포함 된 함수를 ostream 개체로 "스트리밍"할 수 있으며 해당 함수를 스트림에서 호출하는 효과가 있음을 의미합니다. 표현식에서 함수 이름을 사용하면 함수의 포인터로 변환됩니다 (일반적으로).

std::hex은 다음과 같이 정의 된 조작자이다.

ios_base& hex(ios_base& str); 

효과 : str.setf(ios_base::hex, ios_base::basefield)를 호출합니다.

반환 값 : str.

ostreamhex를 스트리밍 진수 출력 숫자 플래그 포맷 출력 기본 설정 것을 의미한다. 조작자는 아무 것도 출력하지 않습니다.

+1

나는 "조작자 자신이 어떻게'void '를 반환하도록 정의되지 않았고'manipulator에 대한'operator << 과부하가'pf (* this)'를 호출하고'* this'를 반환하도록 정의 된 것"이라고 생각했습니다. " ? –

+0

@Steve Jessop : 나는 질문이 "어떻게이 모든 것이 효과가 있는지 설명해 주시겠습니까?"라고 생각했습니다. 그것은'basic_ostream'이 특정 오버로드를 정의하고 주어진 서명이 필요한 이유입니다.나는 마지막 단락에서 질문을 따르지 않았다. –

+0

오, 오케이. 나는 보통이 질문을 "표준에 대한 동기는 무엇입니까?"라고 읽습니다. "표준은 무엇을 말하는가?"보다는 오히려 그것은 내가 스스로 읽을 수있는 표준 사본을 가지고 있기 때문에, 그렇지 않은 사람들과 공감하지 못하는 것일 수도있다. –

4

일반적으로 스트림 조작기는 스트림 객체에 몇 가지 플래그 (또는 다른 설정)를 설정하므로 다음에 사용하면 플래그에 따라 작동합니다. 따라서 조작자는 동일한 오브젝트를 리턴합니다. 물론 manipulator를 호출 한 operator<< 오버로드에는이 객체가 이미 있습니다. 따라서주의 할 수 있듯이 반환 값은 엄격하게 필요하지 않습니다. 저는 이것이 모든 표준 조작기를 다루고 있다고 생각합니다 - 그들은 모두 그들의 입력을 돌려줍니다.

그러나 반환 값을 사용하면 프레임 워크가 유연하므로 사용자 지정 스트림 조작자 이 다른 개체 (예 : 지정된 개체의 래퍼)를 반환 할 수 있습니다. 이 다른 개체는 cout << 'a' << tab에서 반환되며 내장 된 ostream 서식 설정에서 지원하지 않는 작업을 수행 할 수 있습니다.

이 다른 객체를 어떻게 풀어 주는지 확신 할 수 없으므로 이것이 실제로 얼마나 실용적인지 알 수 없습니다. 프록시 객체가 ostream에 의해 관리되는 것과 같이 뭔가 특이해야 할 수도 있습니다. 그런 다음 매니퓰레이터는 일반적으로 매니퓰레이터의 포인트가 아닌,이를 적극적으로 지원하는 맞춤 스트림 클래스에서만 작동합니다.

8

오버로드 된 < < 연산자가 정의되어 있지 않으면 예외는 없습니다. < <의 기존 오버로드는 ostream & (* fp) (ostream &)의 조작자를 예상합니다.

당신이 그것을 유형 ostream에 & (* FP)와 조작을 준 경우 그렇지 않은 ()가 당신은 컴파일러 오류를 얻을 것운영자 < < (ostream에 &, ostream에 &에 대한 정의 (* fp)()). 이 기능을 원하면 < < 연산자에이 유형의 조작기를 허용하도록 과부하를 걸어야합니다.

당신은 이것에 대한 정의를 작성해야 :
ostream에 & ostream에 :: 연산자를 < < (ostream에 & (* m)())

마법 아무것도 여기에 일이되지 않도록 여기 명심 . 스트림 라이브러리는 표준 C++ 기능 (연산자 오버로딩, 클래스 및 참조)에 크게 의존합니다.

우리가 조작하려고하는 스트림에 대한 참조를 통과하지 않고, 우리는 스트림에 수정을 할 수 없습니다 : 이제

당신은 우리가하지 않는 이유는 여기에, 당신은 당신이 설명 된 기능을 만드는 방법을 알고 최종 장치 (cin, out, err, fstream 등)에 연결됩니다. 함수 (수식어가 공상 이름을 가진 모든 함수)는 < < 연산자의 왼쪽에있는 것과는 아무 관계가없는 새로운 ostream을 반환해야하거나 매우 못생긴 메커니즘을 통해 ostream이 무엇인지 알아 내야합니다 다른 것과 연결하면 수정 자의 오른쪽에있는 모든 것이 최종 장치로 전달되지 않지만 오히려 함수/수정자가 반환 한 모든 ostream으로 전송됩니다. 이

cout << "something here" << tab << "something else"<< endl; 

같은 스트림의

생각해 정말 괄호의 각 세트는 뭔가 다음 (, 쓰기 등 수정)를 법원 및합니까

(((cout << "something here") << tab) << "something else") << endl); 

괄호의 cout을 그래서 다음 집합을 반환 의미 그것으로 작업 할 수 있습니다.

탭 수정 자/기능이 ostream을 참조하지 않은 경우 < < 운영자가 해당 작업을 수행하는 데 ostream이 무엇이 었는지 추측해야합니다. 법정에서 일 했니, 세르, 파일 스트림 ..? 함수의 내부는 정보가 전달되지 않는 한 절대로 알 수 없습니다. 어떻게 정보를 전달했는지, 왜 그렇게 간단하지 않은가? 이제

정말 이제 ENDL 정말 무엇인지 살펴 보자하고있는 점 가정을 구동하기 위해 우리가 사용하고있는 < < 연산자의 버전을 오버로드 :

이 연산자는 다음과 같습니다

:

ostream& ostream::operator<<(ostream& (*m)(ostream&)) 
    { 
     return (*m)(*this); 
    } 

ENDL은 다음과 같습니다

ostream& endl(ostream& os)  
    { 
     os << '\n'; 
     os.flush();  
     return os; 
    } 

ENDL의 목적은 newli을 추가하는 것입니다 스트림의 내부 버퍼의 모든 내용이 장치에 기록되었는지 확인하면서 스트림을 플러시합니다. 이렇게하기 위해서는 먼저이 스트림에 '\ n'을 써야합니다. 그런 다음 스트림을 플러시하도록 지시해야합니다. endl이 쓰기 및 스트림 할 스트림을 알 수있는 유일한 방법은 호출자가 endl 함수를 호출 할 때 해당 정보를 전달하는 것입니다. 그것은 내 차를 씻으라고 말하는 것과 같지만 어느 차가 내 전체 주차장에 있는지는 알려주지 않습니다. 당신은 결코 당신의 일을 끝낼 수 없을 것입니다. 내 차를 내게 주거나 나 스스로 씻을 수 있어야한다.

나는 그

PS 물건을 정리할 희망 - 당신이 실수로 내 차를 찾기 위해 일어날 경우, 그것을 씻어주세요.

관련 문제