operator <<는 친구 또는 멤버 함수로 구현해야합니까?


129

그것이 기본적으로 질문입니다. "올바른"구현 방법이 operator<<있습니까? 이것을 읽으면 다음과 같은 것을 볼 수 있습니다.

friend bool operator<<(obj const& lhs, obj const& rhs);

같은 것보다 선호됩니다

ostream& operator<<(obj const& rhs);

하지만 왜 둘 중 하나를 사용해야하는지 잘 모르겠습니다.

내 개인 사례는 다음과 같습니다.

friend ostream & operator<<(ostream &os, const Paragraph& p) {
    return os << p.to_str();
}

그러나 나는 아마도 할 수 있습니다.

ostream & operator<<(ostream &os) {
    return os << paragraph;
}

이 결정의 근거는 무엇입니까?

참고 :

 Paragraph::to_str = (return paragraph) 

여기서 단락은 문자열입니다.


4
BTW 당신은 ​​아마도 멤버 함수의 서명에 const를 추가해야합니다
Motti

4
operator <<에서 bool을 반환하는 이유는 무엇입니까? 스트림 연산자 또는 비트 시프트의 과부하로 사용하고 있습니까?
Martin York

답변:


120

여기서 문제는 링크 하는 기사에 대한 해석에 있습니다 .

평등

이 문서는 bool 관계 연산자를 올바르게 정의하는 데 문제가있는 사람에 대한 것입니다.

운영자 :

  • 같음 == 및! =
  • 관계 <> <=> =

이러한 연산자는 동일한 유형의 두 개체를 비교할 때 부울을 반환해야합니다. 일반적으로 이러한 연산자를 클래스의 일부로 정의하는 것이 가장 쉽습니다. 이는 클래스가 자동으로 자신의 친구가되므로 Paragraph 유형의 개체가 서로를 검사 할 수 있기 때문입니다 (서로 개인 멤버도 포함).

이러한 독립 함수를 만들기위한 인수가 있습니다. 이렇게하면 자동 변환이 동일한 유형이 아닌 경우 양쪽을 모두 변환 할 수 있고 멤버 함수는 rhs 만 자동 변환 할 수 있습니다. 나는 당신이 처음에 자동 변환이 일어나기를 정말로 원하지 않기 때문에 이것을 종이 남자의 주장이라고 생각합니다 (보통). 그러나 이것이 당신이 원하는 것이라면 (권장하지 않습니다) 비교기를 독립적으로 만드는 것이 유리할 수 있습니다.

스트리밍

스트림 연산자 :

  • 연산자 << 출력
  • 연산자 >> 입력

이진 시프트가 아닌 스트림 연산자로 사용할 때 첫 번째 매개 변수는 스트림입니다. 스트림 객체에 대한 액세스 권한이 없기 때문에 (수정할 수있는 것이 아님) 멤버 연산자가 될 수 없으며 클래스 외부에 있어야합니다. 따라서 그들은 클래스의 친구이거나 스트리밍을 수행 할 공용 메서드에 액세스 할 수 있어야합니다.

또한 이러한 개체가 스트림 개체에 대한 참조를 반환하여 스트림 작업을 함께 연결할 수 있습니다.

#include <iostream>

class Paragraph
{
    public:
        explicit Paragraph(std::string const& init)
            :m_para(init)
        {}

        std::string const&  to_str() const
        {
            return m_para;
        }

        bool operator==(Paragraph const& rhs) const
        {
            return m_para == rhs.m_para;
        }
        bool operator!=(Paragraph const& rhs) const
        {
            // Define != operator in terms of the == operator
            return !(this->operator==(rhs));
        }
        bool operator<(Paragraph const& rhs) const
        {
            return  m_para < rhs.m_para;
        }
    private:
        friend std::ostream & operator<<(std::ostream &os, const Paragraph& p);
        std::string     m_para;
};

std::ostream & operator<<(std::ostream &os, const Paragraph& p)
{
    return os << p.to_str();
}


int main()
{
    Paragraph   p("Plop");
    Paragraph   q(p);

    std::cout << p << std::endl << (p == q) << std::endl;
}

19
operator<< private:?
Matt Clarkson

47
@MattClarkson : 아닙니다. 따라서 friend 함수 선언은 클래스의 일부가 아니므로 액세스 지정자의 영향을받지 않습니다. 일반적으로 액세스하는 데이터 옆에 친구 함수 선언을 넣습니다.
Martin York

12
공용 기능을 사용하여 데이터에 액세스하는 경우 친숙한 기능이 필요한 이유는 무엇입니까? 질문이 어리 석다면 죄송합니다.
Semyon Danilov 2014 년

4
@SemyonDanilov : 캡슐화를 중단하고 게터를 추가하는 이유는 무엇입니까? freiend캡슐화를 중단하지 않고 공용 인터페이스를 확장하는 방법입니다. programmers.stackexchange.com/a/99595/12917
Martin York

3
@LokiAstari 그러나 확실히 그것은 to_str을 제거하거나 그것을 비공개로 만드는 것에 대한 논쟁입니다. 현재 스트리밍 운영자는 공개 기능 만 사용하므로 친구가 될 필요가 없습니다.
deworde

53

암시 적 this매개 변수가 <<-operator 의 왼쪽 이기 때문에 멤버 함수로 수행 할 수 없습니다 . (따라서 ostream-class에 멤버 함수로 추가해야합니다 . 좋지 않습니다. :)

없이 무료 기능으로 할 수 있습니까? friend 있습니까? 이것이 제가 선호하는 것입니다. 이것이 ostream클래스의 핵심 기능이 아니라 와의 통합임을 분명히하기 때문 입니다.


1
"반의 핵심 기능이 아닙니다." 그것이 "친구"가 의미하는 것입니다. 핵심 기능이라면 친구가 아닌 클래스에있을 것입니다.
xaxxon jul.

1
@xaxxon 내 첫 번째 문장이이 경우 함수를 멤버 함수로 추가하는 것이 불가능한 이유를 설명한다고 생각합니다. friend함수는 멤버 함수와 동일한 권한을 (가지고 무엇인가 friend가 그 필요 왜 그렇게 클래스의 사용자로, 내가 궁금해 할 것, 의미). 이것이 제가 "핵심 기능"이라는 표현으로 만들고자하는 구별입니다.
Magnus Hoff

32

가능하다면 비회원 및 친구가 아닌 기능으로 활동합니다.

Herb Sutter와 Scott Meyers가 설명했듯이 캡슐화를 늘리기 위해 멤버 함수보다 친구가 아닌 비 멤버 함수를 선호합니다.

C ++ 스트림과 같은 일부 경우에는 선택권이 없으며 비 멤버 함수를 사용해야합니다.

그러나 그렇다고해서 이러한 함수를 클래스의 친구로 만들어야한다는 의미는 아닙니다. 이러한 함수는 클래스 접근자를 통해 클래스에 액세스 할 수 있습니다. 이런 식으로 이러한 함수를 작성하는 데 성공하면 이겼습니다.

운영자 << 및 >> 프로토 타입 정보

귀하의 질문에 제시 한 예가 잘못되었다고 생각합니다. 예를 들면 다음과 같습니다.

ostream & operator<<(ostream &os) {
    return os << paragraph;
}

이 방법이 스트림에서 어떻게 작동 할 수 있는지 생각조차 할 수 없습니다.

다음은 << 및 >> 연산자를 구현하는 두 가지 방법입니다.

T 유형의 스트림과 유사한 객체를 사용한다고 가정 해 보겠습니다.

그리고 Paragraph 유형의 객체 관련 데이터를 T에서 추출 / 삽입하려고합니다.

일반 연산자 << 및 >> 함수 프로토 타입

첫 번째 기능은 다음과 같습니다.

// T << Paragraph
T & operator << (T & p_oOutputStream, const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return p_oOutputStream ;
}

// T >> Paragraph
T & operator >> (T & p_oInputStream, const Paragraph & p_oParagraph)
{
   // do the extraction of p_oParagraph
   return p_oInputStream ;
}

일반 연산자 << 및 >> 메서드 프로토 타입

두 번째는 방법으로서 :

// T << Paragraph
T & T::operator << (const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return *this ;
}

// T >> Paragraph
T & T::operator >> (const Paragraph & p_oParagraph)
{
   // do the extraction of p_oParagraph
   return *this ;
}

이 표기법을 사용하려면 T의 클래스 선언을 확장해야합니다. STL 객체의 경우 이것은 불가능합니다 (수정해서는 안됩니다 ...).

T가 C ++ 스트림이면 어떻게 될까요?

다음은 C ++ 스트림에 대한 동일한 << 및 >> 연산자의 프로토 타입입니다.

일반 basic_istream 및 basic_ostream의 경우

스트림의 경우 C ++ 스트림을 수정할 수 없으므로 함수를 구현해야합니다. 이는 다음과 같은 의미입니다.

// OUTPUT << Paragraph
template <typename charT, typename traits>
std::basic_ostream<charT,traits> & operator << (std::basic_ostream<charT,traits> & p_oOutputStream, const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return p_oOutputStream ;
}

// INPUT >> Paragraph
template <typename charT, typename traits>
std::basic_istream<charT,traits> & operator >> (std::basic_istream<charT,traits> & p_oInputStream, const CMyObject & p_oParagraph)
{
   // do the extract of p_oParagraph
   return p_oInputStream ;
}

char istream 및 ostream의 경우

다음 코드는 문자 기반 스트림에서만 작동합니다.

// OUTPUT << A
std::ostream & operator << (std::ostream & p_oOutputStream, const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return p_oOutputStream ;
}

// INPUT >> A
std::istream & operator >> (std::istream & p_oInputStream, const Paragraph & p_oParagraph)
{
   // do the extract of p_oParagraph
   return p_oInputStream ;
}

Rhys Ulerich는 문자 기반 코드가 그 위에있는 일반 코드의 "특수화"에 불과하다는 사실에 대해 언급했습니다. 물론 Rhys가 맞습니다. 문자 기반 예제를 사용하는 것은 권장하지 않습니다. 읽기가 더 간단하기 때문에 여기에만 제공됩니다. char 기반 스트림으로 만 작업하는 경우에만 실행 가능하므로 wchar_t 코드가 일반적인 플랫폼 (예 : Windows)에서는 피해야합니다.

이것이 도움이되기를 바랍니다.


일반 basic_istream 및 basic_ostream 템플릿 코드가 이미 std :: ostream- 및 std :: istream 특정 버전을 포함하지 않습니까? 후자의 두 가지는 문자를 사용하는 전자의 인스턴스 화일 뿐이 기 때문입니다.
Rhys Ulerich 2009

@Rhys Ulerich : 물론입니다. Windows에서는 char 및 wchar_t 코드를 모두 처리해야하기 때문에 일반 템플릿 버전 만 사용합니다. 두 번째 버전의 유일한 장점은 첫 번째 버전보다 더 단순하게 나타나는 것입니다. 그것에 대한 내 게시물을 명확히 할 것입니다.
paercebal 2009

10

특히 요즘 대부분의 경우와 같이 출력이 주로 진단 및 로깅에 사용되는 경우 친구가 아닌 무료 기능으로 구현해야합니다. 출력에 필요한 모든 것에 대한 const 접근자를 추가 한 다음 출력자가 해당 접근자를 호출하고 형식을 지정하도록합니다.

실제로 "ostreamhelpers"헤더 및 구현 파일에서 이러한 모든 ostream 출력 무료 함수를 수집했습니다.이 기능은 클래스의 실제 목적과는 거리가 먼 보조 기능을 유지합니다.


7

서명 :

bool operator<<(const obj&, const obj&);

이것은 오히려 의심 맞지 않는 것 같다 stream가 연산자 오버로딩 학대의 경우처럼 보이는 있도록 규칙이나 비트 규칙을 operator <반환해야 bool하지만,operator << 해야 아마도 다른 것을 반환해야합니다.

그렇게 의미했다면 다음과 같이 말하십시오.

ostream& operator<<(ostream&, const obj&); 

그런 다음 ostream필요에 따라 함수를 추가 할 수 없기 때문에 함수는 액세스해야하는 항목에 따라 자유 함수 여야합니다 friend(비공개 멤버 또는 보호 된 멤버에 액세스 할 필요가없는 경우 만들 필요가 없습니다). 친구).


주문을 ostream사용할 때 수정 권한 이 필요하다는 점을 언급 할 가치 가 있습니다 ostream.operator<<(obj&). 따라서 자유 기능. 그렇지 않으면 사용자 유형은 액세스를 수용하기 위해 스팀 유형이어야합니다.
wulfgarpro

2

완료를 위해 실제로 연산자를 만들 있음을 추가하고 싶습니다.ostream& operator << (ostream& os) 클래스 내에서 . 내가 아는 바에 따르면 사용하는 것은 좋지 않습니다. 왜냐하면 매우 복잡하고 직관적이지 않기 때문입니다.

다음 코드가 있다고 가정 해 보겠습니다.

#include <iostream>
#include <string>

using namespace std;

struct Widget
{
    string name;

    Widget(string _name) : name(_name) {}

    ostream& operator << (ostream& os)
    {
        return os << name;
    }
};

int main()
{
    Widget w1("w1");
    Widget w2("w2");

    // These two won't work
    {
        // Error: operand types are std::ostream << std::ostream
        // cout << w1.operator<<(cout) << '\n';

        // Error: operand types are std::ostream << Widget
        // cout << w1 << '\n';
    }

    // However these two work
    {
        w1 << cout << '\n';

        // Call to w1.operator<<(cout) returns a reference to ostream&
        w2 << w1.operator<<(cout) << '\n';
    }

    return 0;
}

그래서 요약하자면-할 수는 있지만 그렇게해서는 안됩니다. :)


0

친구 연산자 = 클래스와 동등한 권리

friend std::ostream& operator<<(std::ostream& os, const Object& object) {
    os << object._atribute1 << " " << object._atribute2 << " " << atribute._atribute3 << std::endl;
    return os;
}

0

operator<< 친구 기능으로 구현 :

#include <iostream>
#include <string>
using namespace std;

class Samp
{
public:
    int ID;
    string strName; 
    friend std::ostream& operator<<(std::ostream &os, const Samp& obj);
};
 std::ostream& operator<<(std::ostream &os, const Samp& obj)
    {
        os << obj.ID<<   << obj.strName;
        return os;
    }

int main()
{
   Samp obj, obj1;
    obj.ID = 100;
    obj.strName = "Hello";
    obj1=obj;
    cout << obj <<endl<< obj1;

} 

출력 :
100 Hello
100 Hello

이는 객체가 오른쪽에 operator<<있고 인수 cout가 왼쪽에 있기 때문에 친구 함수가 될 수 있습니다 . 따라서 이것은 클래스의 멤버 함수가 될 수 없으며 친구 함수 만 될 수 있습니다.


멤버 기능으로 쓸 방법이 없을 것 같아 !!
Rohit Vipin Mathews

왜 모든 것이 대담합니다. 제거하겠습니다.
Sebastian Mach
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.