연산자<<를 친구로 구현해야 할까요, 아니면 멤버 함수로 구현해야 할까요?
-
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
-수업. 안좋다 :)
당신은없이 무료 기능으로 할 수 있습니까? friend
IT? 이것이 제가 선호하는 것입니다. 왜냐하면 이것이 통합이라는 것이 분명하기 때문입니다. 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;
}