هل يجب تنفيذ عامل التشغيل << كصديق أو كوظيفة عضو؟

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) 

حيث الفقرة سلسلة.

هل كانت مفيدة؟

المحلول

المشكلة هنا في تفسيرك للمقالة أنت وصلة.

تتناول هذه المقالة شخصًا يواجه مشكلات في تحديد عوامل تشغيل العلاقة المنطقية بشكل صحيح.

المشغل:

  • المساواة == و !=
  • العلاقة < > <= >=

يجب أن تقوم هذه العوامل بإرجاع منطقي لأنها تقارن كائنين من نفس النوع.عادة ما يكون من الأسهل تعريف عوامل التشغيل هذه كجزء من الفئة.وذلك لأن الفصل يكون تلقائيًا صديقًا لذاته، لذا يمكن للكائنات من النوع فقرة فحص بعضها البعض (حتى الأعضاء الخاصين لبعضهم البعض).

هناك حجة لجعل هذه الوظائف المستقلة لأن هذا يتيح للتحويل التلقائي تحويل كلا الجانبين إذا لم يكنا من نفس النوع، بينما تسمح وظائف الأعضاء فقط بالتحويل التلقائي لـ 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 ليست جيدة:)

هل يمكن أن تفعل ذلك بوصفها وظيفة الحرة دون friending ذلك؟ هذا ما كنت تفضل ذلك، لأنه يجعل من الواضح أن هذا هو التكامل مع ostream، وليس وظيفة أساسية من صفك.

إذا كان ذلك ممكنا، وظائف غير الأعضاء وغير الأصدقاء.

وكما وصفها عشب سوتر وسكوت مايرز، ويفضل وظائف غير الأعضاء غير صديق لوظائف الأعضاء، للمساعدة على زيادة التغليف.

في بعض الحالات، مثل تيارات 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 ;
}

لاحظ أن استخدام هذه الرموز، يجب توسيع تعريف فئة تي. لكائنات 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 ;
}

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

وعلق ريس Ulerich عن حقيقة رمز القائمة على شار ليس سوى "التخصص" من قانون عام فوقه. وبطبيعة الحال، ريس هو الحق: أنا لا أنصح باستخدام المثال القائم على شار. من المسلم به هنا فقط لأنها أسهل للقراءة. كما هو الحال قابلة للحياة إلا إذا كنت تعمل فقط مع تيارات القائم على شار، يجب تجنب ذلك على منصات حيث كود wchar_t هو شائع (أي على ويندوز).

وهذا الأمل سوف يساعد.

وينبغي أن تنفذ على أنها حرة، وظائف غير صديق، وخاصة إذا، مثل معظم الأشياء في هذه الأيام، ويستخدم الناتج أساسا لتشخيص وقطع الأشجار. إضافة يمكنهم الدخول CONST لجميع الأشياء التي تحتاج للذهاب إلى الإخراج، ثم لديك outputter مجرد استدعاء هؤلاء والقيام التنسيق.

ولقد اتخذت فعلا لجمع كل هذه الوظائف حرة الناتج ostream في "ostreamhelpers" رأس وملف التنفيذ، وتبقي هذه الوظيفة الثانوية بعيدا عن الهدف الحقيقي للطبقات.

والتوقيع:

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

ويبدو المشتبه به بدلا، وهذا لا يتناسب مع اتفاقية stream ولا اتفاقية أحادي المعامل بحيث يبدو وكأنه حالة من المشغل الحمولة الزائدة الاعتداء، يجب operator < عودة bool لكن يجب operator << ربما يعود شيء آخر.

إذا يعني القول:

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

وبعد ذلك لأنك لا تستطيع إضافة وظائف إلى ostream بحكم الضرورة وظيفة يجب أن يكون وظيفة خالية، سواء كان ذلك على friend أو لا يعتمد على ما لديها للوصول (إذا كان لا تحتاج إلى الوصول إلى أعضاء من القطاع الخاص أو محمية هناك لا حاجة لجعله صديق).

وفقط من اجل الانتهاء، وأود أن أضيف أن كنت في الواقع على يمكن إنشاء وostream& operator << (ostream& os) المشغل داخل الصف، وأنها يمكن أن تعمل. من ما أنا أعلم أنها ليست فكرة جيدة لاستخدامه، لأنه معقد جدا وunintuitive.

لنفترض لدينا هذا الرمز:

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

} 
<اقتباس فقرة>   

وOUTPUT: 100 مرحبا مرحبا 100 اضغط على أي مفتاح للمتابعة ...

وهذا يمكن أن يكون وظيفة صديق فقط لأن الكائن على الجانب الأيمن من 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