연산자<<를 친구로 구현해야 할까요, 아니면 멤버 함수로 구현해야 할까요?

StackOverflow https://stackoverflow.com/questions/236801

  •  04-07-2019
  •  | 
  •  

문제

그것은 기본적으로 질문입니다. 구현하는 "올바른" 방법이 있습니까? 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 관계 연산자를 올바르게 정의하는 데 문제가있는 사람에 관한 것입니다.

운영자 :

  • 평등 == 및! =
  • 관계 <> <=> =

이 연산자는 동일한 유형의 두 객체를 비교할 때 Bool을 반환해야합니다. 일반적으로 이러한 연산자를 클래스의 일부로 정의하는 것이 가장 쉽습니다. 이는 수업이 자동으로 그 자체의 친구이기 때문에 유형 단락의 대상이 서로를 검사 할 수 있기 때문입니다 (서로 개인 회원조차도).

이러한 독립형 함수를 만들 겠다는 주장이 있습니다.이를 통해 자동 변환이 동일한 유형이 아닌 경우 양면을 변환 할 수있는 반면, 멤버 함수는 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 매개 변수는 왼쪽입니다 <<-운영자. (따라서 회원 기능으로 추가해야합니다. ostream-수업. 안좋다 :)

당신은없이 무료 기능으로 할 수 있습니까? friendIT? 이것이 제가 선호하는 것입니다. 왜냐하면 이것이 통합이라는 것이 분명하기 때문입니다. ostream, 수업의 핵심 기능이 아닙니다.

가능하다면 비회원, 비친구 기능으로.

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

C++ 스트림과 같은 경우에는 선택의 여지가 없으며 비멤버 함수를 사용해야 합니다.

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

연산자 << 및 >> 프로토타입 정보

귀하의 질문에 제시된 예가 잘못되었다고 생각합니다.예를 들어;

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

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

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

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

그리고 단락 유형 개체의 관련 데이터를 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의 경우

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

// 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는 char 기반 코드가 그 위에 있는 일반 코드의 "전문화"일 뿐이라는 사실에 대해 언급했습니다.물론 Rhys의 말이 옳습니다.char 기반 예제를 사용하지 않는 것이 좋습니다.읽기가 더 쉽기 때문에 여기에만 제공됩니다.char 기반 스트림으로만 작업하는 경우에만 실행 가능하므로 wchar_t 코드가 일반적인 플랫폼(예:Windows에서).

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

특히 대부분의 것들과 마찬가지로 출력이 주로 진단 및 로깅에 사용되는 경우, 특히 무료 비 친구 기능으로 구현해야합니다. 출력에 들어가는 데 필요한 모든 것들에 대해 Const 액세서를 추가 한 다음 아웃 퍼터가 전화를 걸고 서식을 수행하도록하십시오.

실제로 "OstreamHelpers"헤더 및 구현 파일에서 이러한 Ostream 출력 자유 함수를 모두 수집하는 데 사용되었으므로 2 차 기능이 클래스의 실제 목적에서 멀리 떨어져 있습니다.

서명 :

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;
}

따라서 요약하려면 - 당신은 할 수 있지만, 아마도 당신은 아마도 아마도 안됩니다 :)

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 Hello를 누르십시오.

객체가 오른쪽에 있기 때문에 이것은 친구 기능 일 수 있습니다. operator<< 그리고 논쟁 cout 왼쪽에 있습니다. 따라서 이것은 클래스의 멤버 기능이 될 수 없으며 친구 기능 일 수 있습니다.

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

friend std::ostream& operator<<(std::ostream& os, const Object& object) {
    os << object._atribute1 << " " << object._atribute2 << " " << atribute._atribute3 << std::endl;
    return os;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top