क्या ऑपरेटर << को एक दोस्त के रूप में या सदस्य के कार्य के रूप में लागू किया जाना चाहिए?


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 आपको संभवतः सदस्य कार्यों के हस्ताक्षरों में
योगदान

4
ऑपरेटर से बूल क्यों लौटाए <<? क्या आप इसे स्ट्रीम ऑपरेटर के रूप में या बिटकॉइन शिफ्ट के ओवरलोड के रूप में उपयोग कर रहे हैं?
मार्टिन यॉर्क

जवाबों:


120

यहां समस्या आपके द्वारा लिंक किए गए लेख की व्याख्या में है ।

समानता

यह लेख किसी ऐसे व्यक्ति के बारे में है जिसे बूल संबंध ऑपरेटरों को ठीक से परिभाषित करने में समस्या हो रही है।

परिचालक:

  • समानता == और! =
  • संबंध <> <=> =

इन ऑपरेटरों को एक बूल वापस करना चाहिए क्योंकि वे एक ही प्रकार की दो वस्तुओं की तुलना कर रहे हैं। आमतौर पर इन ऑपरेटरों को कक्षा के हिस्से के रूप में परिभाषित करना सबसे आसान है। ऐसा इसलिए है क्योंकि एक वर्ग अपने आप में एक दोस्त है इसलिए प्रकार की वस्तुएं एक दूसरे की जांच कर सकती हैं (यहां तक ​​कि प्रत्येक अन्य निजी सदस्य भी)।

इन मुक्त कार्यों को करने के लिए एक तर्क है क्योंकि इससे ऑटो रूपांतरण दोनों पक्षों को परिवर्तित कर सकता है यदि वे एक ही प्रकार के नहीं हैं, जबकि सदस्य फ़ंक्शन केवल आरएच को ऑटो परिवर्तित करने की अनुमति देते हैं। मुझे यह एक पेपर मैन तर्क लगता है क्योंकि आप वास्तव में पहली जगह (आमतौर पर) में ऑटो रूपांतरण नहीं चाहते हैं। लेकिन अगर यह कुछ ऐसा है जो आप चाहते हैं (मैं इसकी अनुशंसा नहीं करता हूं) तो तुलनित्रों को मुक्त खड़ा करना लाभप्रद हो सकता है।

स्ट्रीमिंग

स्ट्रीम ऑपरेटर:

  • ऑपरेटर << आउटपुट
  • संचालक >> इनपुट

जब आप इन्हें स्ट्रीम ऑपरेटर्स (बाइनरी शिफ्ट के बजाय) के रूप में उपयोग करते हैं तो पहला पैरामीटर एक स्ट्रीम होता है। चूँकि आपके पास स्ट्रीम ऑब्जेक्ट (इसकी संशोधित करने के लिए आपकी नहीं) तक पहुंच नहीं है, ये सदस्य ऑपरेटर नहीं हो सकते हैं जिन्हें उन्हें कक्षा के लिए बाहरी होना है। इस प्रकार उन्हें या तो कक्षा का दोस्त होना चाहिए या आपके पास एक सार्वजनिक विधि तक पहुंच होनी चाहिए जो आपके लिए स्ट्रीमिंग करेगी।

इन ऑब्जेक्ट्स के लिए स्ट्रीम ऑब्जेक्ट के संदर्भ को वापस करना भी पारंपरिक है ताकि आप एक साथ स्ट्रीम ऑपरेशन को चेन कर सकें।

#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:?
मैट क्लार्कसन

47
@MattClarkson: इसकी नहीं। इसका एक मित्र कार्य घोषणा इस प्रकार वर्ग का हिस्सा नहीं है और इस तरह एक्सेस स्पेसर्स से प्रभावित नहीं है। मैं आमतौर पर डेटा तक पहुंच के लिए फ्रेंड फंक्शन की घोषणाएं करता हूं।
मार्टिन

12
यदि आप डेटा तक पहुंचने के लिए सार्वजनिक फ़ंक्शन का उपयोग कर रहे हैं, तो इसे एक मैत्रीपूर्ण फ़ंक्शन होने की आवश्यकता क्यों है? क्षमा करें, यदि प्रश्न मूर्खतापूर्ण है।
शिमोन दानिलोव

4
@SemyonDanilov: आप एनकैप्सुलेशन को क्यों तोड़ेंगे और गेटर्स जोड़ेंगे! freiendएनकैप्सुलेशन को तोड़े बिना सार्वजनिक इंटरफ़ेस को विस्तारित करने का एक तरीका है। प्रोग्रामर.स्टैकएक्सचेंज.
मार्टिन यॉर्क

3
@ लोकीअस्तरी लेकिन निश्चित रूप से यह एक तर्क है कि या तो इसे हटा दें या इसे निजी बना दें। जैसा कि यह खड़ा है, स्ट्रीमिंग ऑपरेटर का मित्र होना जरूरी नहीं है, क्योंकि यह केवल सार्वजनिक कार्यों का उपयोग करता है।
डेवॉर्ड

53

आप इसे सदस्य फ़ंक्शन के रूप में नहीं कर सकते हैं, क्योंकि अंतर्निहित thisपैरामीटर <<-ओपरेटर के बाएं हाथ की ओर है । (इसलिए, आपको इसे एक सदस्य फ़ंक्शन के रूप में ostream-class में जोड़ना होगा । अच्छा नहीं :)

क्या आप इसे friendआईएनजी के बिना एक नि: शुल्क समारोह के रूप में कर सकते हैं ? यही मैं पसंद करता हूं, क्योंकि यह स्पष्ट करता है कि यह एक एकीकरण है ostream, न कि आपकी कक्षा की मुख्य कार्यक्षमता।


1
"आपकी कक्षा की मुख्य कार्यक्षमता नहीं है।" यही "मित्र" का अर्थ है। यदि यह मुख्य कार्यशीलता होती, तो यह कक्षा में होता, मित्र नहीं।
xaxxon

1
@xaxxon मुझे लगता है कि मेरा पहला वाक्य बताता है कि इस मामले में सदस्य फ़ंक्शन के रूप में फ़ंक्शन को जोड़ना असंभव क्यों होगा। एक friendफ़ंक्शन में एक सदस्य फ़ंक्शन के रूप में समान अधिकार हैं ( इसका मतलब यह है friend), इसलिए कक्षा के उपयोगकर्ता के रूप में, मुझे आश्चर्य होगा कि इसकी आवश्यकता क्यों होगी। यह वह अंतर है जिसे मैं "मुख्य कार्यक्षमता" शब्द के साथ बनाने की कोशिश कर रहा हूं।
मैग्नस हॉफ

32

यदि संभव हो तो, गैर-सदस्य और गैर-मित्र कार्यों के रूप में।

हर्ब सटर और स्कॉट मेयर्स द्वारा वर्णित के रूप में, इनकैप्सुलेशन को बढ़ाने में मदद करने के लिए गैर-सदस्य गैर-सदस्य कार्यों को प्राथमिकता देते हैं।

कुछ मामलों में, 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 एक C ++ स्ट्रीम है?

यहां सी ++ धाराओं के लिए समान << और >> ऑपरेटरों के प्रोटोटाइप हैं।

जेनेरिक बेसिक_स्ट्रीम और बेसिक_स्ट्रीम के लिए

ध्यान दें कि धाराओं के मामले में, जैसा कि आप 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 ;
}

चार 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 सही है: मैं चार-आधारित उदाहरण के उपयोग की अनुशंसा नहीं करता हूं। यह केवल यहाँ दिया गया है क्योंकि यह पढ़ने में सरल है। जैसा कि यह केवल व्यवहार्य है यदि आप केवल चार-आधारित धाराओं के साथ काम करते हैं, तो आपको इसे उन प्लेटफार्मों पर नहीं बचना चाहिए जहां wchar_t कोड आम है (यानी विंडोज पर)।

आशा है कि यह मदद करेगा।


क्या आपका जेनेरिक बेसिक_स्ट्रीम और बेसिक_स्ट्रीम टेम्पर्ड कोड पहले से ही std को कवर नहीं करता है :: ostream- और std :: istream-specific वर्जन चूंकि बाद के दो वर्जन केवल चार्ट का उपयोग करके पूर्व के इंस्टेंटिएशन हैं?
Rhys Ulerich

@ राइस उलेरिच: बिल्कुल। मैं केवल सामान्य, टेम्प्लेटेड संस्करण का उपयोग करता हूं, यदि केवल विंडोज पर, तो आपको चार और wchar_t कोड से निपटना होगा। दूसरे संस्करण की एकमात्र योग्यता पहले की तुलना में अधिक सरल दिखाई देना है। मैं उस बारे में अपनी पोस्ट स्पष्ट करूँगा।
paercebal

10

इसे एक नि: शुल्क, गैर-मित्र कार्यों के रूप में लागू किया जाना चाहिए, खासकर अगर, इन दिनों ज्यादातर चीजों की तरह, आउटपुट मुख्य रूप से निदान और लॉगिंग के लिए उपयोग किया जाता है। उन सभी चीजों के लिए कॉन्स्ट एक्सेसर्स जोड़ें, जो आउटपुट में जाने की जरूरत है, और फिर आउटपुटेटर के पास बस उन्हें कॉल करना और फॉर्मेटिंग करना है।

मैं वास्तव में "ostreamhelpers" हेडर और कार्यान्वयन फ़ाइल में इन सभी ओस्ट्रीम आउटपुट फ्री फ़ंक्शंस को इकट्ठा करने के लिए ले गया हूं, यह उस माध्यमिक कार्यक्षमता को कक्षाओं के वास्तविक उद्देश्य से बहुत दूर रखता है।


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;

} 

OUTPUT:
100 नमस्ते
100 नमस्कार

यह केवल एक मित्र कार्य हो सकता है क्योंकि वस्तु दाएं हाथ की ओर है operator<<और तर्क coutबाएं हाथ की तरफ है। तो यह कक्षा के लिए एक सदस्य कार्य नहीं हो सकता है, यह केवल एक मित्र कार्य हो सकता है।


मुझे नहीं लगता कि एक सदस्य funtion के रूप में यह लिखने का एक तरीका है !!
रोहित विपिन मैथ्यूज

क्यों सब कुछ बोल्ड है। मुझे इसे दूर करने दो।
सेबेस्टियन मच
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.