operator <<는 친구 또는 멤버 함수로 구현해야합니까? ostream& operator<<(obj const&

그것이 기본적으로 질문입니다. “올바른”구현 방법이 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) 

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



답변

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

평등

이 문서는 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;
}

답변

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

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


답변

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

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)에서는 피해야합니다.

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


답변

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

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


답변

서명 :

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

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

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

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

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


답변

완료를 위해 실제로 연산자를 만들 있음을 추가하고 싶습니다.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;
}

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


답변

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

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