सवाल ऑपरेटर ओवरलोडिंग के लिए बुनियादी नियम और मुहावरे क्या हैं?


नोट: जवाब दिए गए थे एक विशिष्ट आदेश, लेकिन चूंकि कई उपयोगकर्ता वोट के अनुसार उत्तर देते हैं, उन्हें दिए गए समय के बजाय, यहां एक है उत्तरों की अनुक्रमणिका क्रम में वे सबसे अधिक समझ में आता है:

(नोट: यह एक प्रविष्टि के लिए है स्टैक ओवरफ़्लो का सी ++ एफएक्यू। यदि आप इस फॉर्म में एक एफएक्यू प्रदान करने के विचार की आलोचना करना चाहते हैं, तो मेटा पर पोस्टिंग जो यह सब शुरू हुई ऐसा करने के लिए जगह होगी। उस प्रश्न के उत्तर में निगरानी की जाती है सी ++ चैट रूम, जहां पहली बार एफएक्यू विचार शुरू हुआ, इसलिए आपके उत्तर को उन लोगों द्वारा पढ़ा जाने की संभावना है जो विचार के साथ आए थे।)  


1845
2017-12-12 12:44


मूल


अगर हम सी ++ - एफएक्यू टैग के साथ जारी रखने जा रहे हैं, तो इस तरह प्रविष्टियों को प्रारूपित किया जाना चाहिए। - John Dibling
मैंने जर्मन सी ++ समुदाय के लिए ऑपरेटर ओवरलोडिंग के बारे में लेखों की एक छोटी सी श्रृंखला लिखी है: भाग 1: ऑपरेटर ओवरलोडिंग सी ++ में सभी ऑपरेटरों के लिए अर्थशास्त्र, विशिष्ट उपयोग और विशिष्टताओं को शामिल करता है। यहां आपके उत्तरों के साथ कुछ ओवरलैपिंग हैं, फिर भी कुछ अतिरिक्त जानकारी है। पार्ट्स 2 और 3 Boost.Operators का उपयोग करने के लिए एक ट्यूटोरियल बनाते हैं। क्या आप मुझे उनका अनुवाद करना चाहते हैं और उन्हें उत्तर के रूप में जोड़ना चाहते हैं? - Arne Mertz
ओह, और एक अंग्रेजी अनुवाद भी उपलब्ध है: मूल बातें तथा आम प्रक्रिया - Arne Mertz


जवाब:


अधिभार के लिए आम ऑपरेटरों

ओवरलोडिंग ऑपरेटरों में अधिकांश काम बॉयलर-प्लेट कोड है। यह आश्चर्य की बात नहीं है, क्योंकि ऑपरेटर केवल वाक्य रचनात्मक चीनी हैं, उनके वास्तविक काम को सादे कार्यों (और अक्सर भेजा जाता है) द्वारा किया जा सकता है। लेकिन यह महत्वपूर्ण है कि आपको यह बॉयलर-प्लेट कोड सही हो। यदि आप असफल होते हैं, तो आपके ऑपरेटर का कोड संकलित नहीं होगा या आपके उपयोगकर्ता का कोड संकलित नहीं होगा या आपके उपयोगकर्ता का कोड आश्चर्यजनक रूप से व्यवहार करेगा।

असाइनमेंट ऑपरेटर

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

X& X::operator=(X rhs)
{
  swap(rhs);
  return *this;
}

बिट्सफ़्ट ऑपरेटर (स्ट्रीम I / O के लिए उपयोग किया जाता है)

बिट्सफ़्ट ऑपरेटर << तथा >>, हालांकि अभी भी बिट-मैनिपुलेशन फ़ंक्शंस के लिए हार्डवेयर इंटरफेसिंग में उपयोग किया जाता है, वे सी से प्राप्त होते हैं, अधिकतर अनुप्रयोगों में अधिभारित स्ट्रीम इनपुट और आउटपुट ऑपरेटरों के रूप में अधिक प्रचलित हो जाते हैं। बिट-मैनिपुलेशन ऑपरेटर के रूप में ओवरलोडिंग के लिए, बाइनरी अंकगणितीय ऑपरेटरों पर नीचे दिया गया अनुभाग देखें। Iostreams के साथ आपकी ऑब्जेक्ट का उपयोग होने पर अपने स्वयं के कस्टम प्रारूप को लागू करने और तर्क को पार्स करने के लिए, जारी रखें।

स्ट्रीम ऑपरेटर, आमतौर पर अधिभारित ऑपरेटरों में से एक हैं, बाइनरी इंफिक्स ऑपरेटर हैं जिनके लिए सिंटैक्स कोई प्रतिबंध नहीं देता है कि वे सदस्य या गैर-सदस्य होना चाहिए या नहीं। चूंकि वे अपने बाएं तर्क को बदलते हैं (वे धारा के राज्य को बदलते हैं), उन्हें अंगूठे के नियमों के अनुसार, अपने बाएं ऑपरेंड के प्रकार के सदस्यों के रूप में लागू किया जाना चाहिए। हालांकि, उनके बाएं ऑपरेशंस मानक लाइब्रेरी से स्ट्रीम होते हैं, और मानक लाइब्रेरी द्वारा परिभाषित अधिकांश स्ट्रीम आउटपुट और इनपुट ऑपरेटरों को वास्तव में स्ट्रीम क्लास के सदस्यों के रूप में परिभाषित किया जाता है, जब आप आउटपुट और इनपुट ऑपरेशंस को अपने प्रकार के लिए लागू करते हैं, तो आप मानक पुस्तकालय के स्ट्रीम प्रकारों को बदल नहीं सकते हैं। यही कारण है कि आपको इन ऑपरेटरों को गैर-सदस्य कार्यों के रूप में अपने स्वयं के प्रकारों के लिए लागू करने की आवश्यकता है। दोनों के कैनोलिक रूप ये हैं:

std::ostream& operator<<(std::ostream& os, const T& obj)
{
  // write obj to stream

  return os;
}

std::istream& operator>>(std::istream& is, T& obj)
{
  // read obj from stream

  if( /* no valid object of T found in stream */ )
    is.setstate(std::ios::failbit);

  return is;
}

कार्यान्वित करते समय operator>>, स्ट्रीम की स्थिति को मैन्युअल रूप से सेट करना केवल तभी जरूरी है जब पठन स्वयं सफल हो, लेकिन नतीजा यह नहीं होगा कि अपेक्षित क्या होगा।

फंक्शन कॉल ऑपरेटर

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

वाक्यविन्यास का एक उदाहरण यहां दिया गया है:

class foo {
public:
    // Overloaded call operator
    int operator()(const std::string& y) {
        // ...
    }
};

उपयोग:

foo f;
int a = f("hello");

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

तुलना ऑपरेटर

बाइनरी इंफिक्स तुलना ऑपरेटरों को अंगूठे के नियमों के अनुसार, गैर-सदस्य कार्यों के रूप में लागू किया जाना चाहिए1। यूनरी उपसर्ग निषेध ! (एक ही नियम के अनुसार) एक सदस्य समारोह के रूप में लागू किया जाना चाहिए। (लेकिन इसे अधिभारित करना आमतौर पर एक अच्छा विचार नहीं है।)

मानक पुस्तकालय के एल्गोरिदम (उदा। std::sort()) और प्रकार (उदा। std::map) हमेशा ही उम्मीद करेंगे operator< उपस्थित होने के लिए। हालांकि आपके प्रकार के उपयोगकर्ता अन्य सभी ऑपरेटरों को उपस्थित होने की उम्मीद करेंगे, भी, अगर आप परिभाषित करते हैं operator<, ऑपरेटर ओवरलोडिंग के तीसरे मौलिक नियम का पालन करना सुनिश्चित करें और अन्य सभी बूलियन तुलना ऑपरेटर को भी परिभाषित करें। उन्हें लागू करने के लिए वैचारिक तरीका यह है:

inline bool operator==(const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator!=(const X& lhs, const X& rhs){return !operator==(lhs,rhs);}
inline bool operator< (const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator> (const X& lhs, const X& rhs){return  operator< (rhs,lhs);}
inline bool operator<=(const X& lhs, const X& rhs){return !operator> (lhs,rhs);}
inline bool operator>=(const X& lhs, const X& rhs){return !operator< (lhs,rhs);}

यहां ध्यान देने योग्य महत्वपूर्ण बात यह है कि इन ऑपरेटरों में से केवल दो ही वास्तव में कुछ भी करते हैं, अन्य लोग वास्तविक कार्य करने के लिए इन दोनों में से किसी एक को अपने तर्कों को अग्रेषित कर रहे हैं।

शेष बाइनरी बुलियन ऑपरेटर को अधिभारित करने के लिए वाक्यविन्यास (||, &&) तुलना ऑपरेटर के नियमों का पालन करता है। हालांकि यह है बहुत संभावना नहीं है कि आप इनके लिए उचित उपयोग केस पाएंगे2

1  अंगूठे के सभी नियमों के साथ, कभी-कभी इसे तोड़ने के कारण भी हो सकते हैं। यदि ऐसा है, तो यह न भूलें कि बाइनरी तुलना ऑपरेटर के बाएं हाथ के ऑपरेंड, जो सदस्य कार्यों के लिए होंगे *this, होने की जरूरत constभी तो एक सदस्य समारोह के रूप में लागू एक तुलना ऑपरेटर को यह हस्ताक्षर होना होगा:

bool operator<(const X& rhs) const { /* do actual comparison with *this */ }

(ध्यान दें const अतं मै।)

2  यह ध्यान दिया जाना चाहिए कि अंतर्निहित संस्करण || तथा && शॉर्टकट अर्थशास्त्र का प्रयोग करें। जबकि उपयोगकर्ता परिभाषित लोगों (क्योंकि वे विधि कॉल के लिए वाक्य रचनात्मक चीनी हैं) शॉर्टकट अर्थशास्त्र का उपयोग नहीं करते हैं। उपयोगकर्ता इन ऑपरेटरों को शॉर्टकट अर्थशास्त्र रखने की अपेक्षा करेगा, और उनका कोड इस पर निर्भर हो सकता है, इसलिए अत्यधिक सलाह दी जाती है कि उन्हें कभी परिभाषित न करें।

अंकगणितीय आपरेटर

यूनरी अंकगणितीय ऑपरेटरों

यूनरी वृद्धि और कमी ऑपरेटर दोनों उपसर्ग और पोस्टफिक्स स्वाद में आते हैं। एक दूसरे से बताने के लिए, पोस्टफिक्स वेरिएंट एक अतिरिक्त डमी int तर्क लेते हैं। यदि आप वृद्धि या कमी को अधिभारित करते हैं, तो हमेशा उपसर्ग और पोस्टफिक्स संस्करण दोनों को लागू करना सुनिश्चित करें। वृद्धि के कैननिक कार्यान्वयन यहां है, कमी उसी नियमों का पालन करती है:

class X {
  X& operator++()
  {
    // do actual increment
    return *this;
  }
  X operator++(int)
  {
    X tmp(*this);
    operator++();
    return tmp;
  }
};

ध्यान दें कि उपसर्ग के संदर्भ में पोस्टफिक्स संस्करण लागू किया गया है। यह भी ध्यान रखें कि पोस्टफिक्स एक अतिरिक्त प्रति करता है।2

यूनरी माइनस और प्लस ओवरलोडिंग बहुत आम नहीं है और शायद सबसे अच्छी तरह से बचा है। यदि आवश्यक हो, तो उन्हें शायद सदस्य कार्यों के रूप में अधिभारित किया जाना चाहिए।

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

बाइनरी अंकगणितीय ऑपरेटरों

बाइनरी अंकगणितीय ऑपरेटरों के लिए, तीसरे मूल नियम ऑपरेटर ओवरलोडिंग का पालन करना न भूलें: यदि आप प्रदान करते हैं +, भी प्रदान करते हैं +=, यदि आप प्रदान करते हैं -छोड़ो मत -=, आदि। एंड्रयू कोएनिग यह मानने वाले पहले व्यक्ति थे कि यौगिक असाइनमेंट ऑपरेटरों को उनके गैर-कंपाउंड समकक्षों के आधार के रूप में उपयोग किया जा सकता है। वह ऑपरेटर है + के संदर्भ में लागू किया गया है +=, - के संदर्भ में लागू किया गया है -= आदि।

अंगूठे के हमारे नियमों के मुताबिक, + और उसके साथी गैर-सदस्य होना चाहिए, जबकि उनके यौगिक असाइनमेंट समकक्ष (+= इत्यादि), उनके बाएं तर्क को बदलना, एक सदस्य होना चाहिए। यहां अनुकरणीय कोड है += तथा +, अन्य बाइनरी अंकगणितीय ऑपरेटरों को उसी तरह लागू किया जाना चाहिए:

class X {
  X& operator+=(const X& rhs)
  {
    // actual addition of rhs to *this
    return *this;
  }
};
inline X operator+(X lhs, const X& rhs)
{
  lhs += rhs;
  return lhs;
}

operator+= प्रति संदर्भ इसके परिणाम देता है, जबकि operator+ इसके परिणाम की एक प्रति देता है। बेशक, एक संदर्भ लौटने से आमतौर पर एक प्रतिलिपि वापस करने से अधिक कुशल होता है, लेकिन मामले में operator+, प्रतिलिपि के आसपास कोई रास्ता नहीं है। जब आप लिखते हैं a + b, आप परिणाम एक नया मूल्य होने की उम्मीद करते हैं, यही कारण है कि operator+ एक नया मूल्य वापस करना है।3 यह भी ध्यान दें operator+ अपने बाएं ऑपरेंड लेता है प्रतिलिपि द्वारा कॉन्स संदर्भ के बजाए। इसका कारण यही कारण है operator= प्रति प्रति अपनी तर्क लेना।

बिट हेरफेर ऑपरेटर ~  &  |  ^  <<  >> अंकगणितीय ऑपरेटरों के रूप में उसी तरह लागू किया जाना चाहिए। हालांकि, (अधिभार के अलावा << तथा >> आउटपुट और इनपुट के लिए) इन अधिभार के लिए बहुत कम उचित उपयोग मामले हैं।

3  फिर से, इस से लिया जाने वाला सबक यह है a += b सामान्य रूप से, अधिक कुशल है a + b और यदि संभव हो तो प्राथमिकता दी जानी चाहिए।

ऐरे सब्सक्राइबिंग

सरणी सबस्क्रिप्ट ऑपरेटर एक बाइनरी ऑपरेटर है जिसे क्लास सदस्य के रूप में कार्यान्वित किया जाना चाहिए। इसका उपयोग कंटेनर-जैसी प्रकारों के लिए किया जाता है जो किसी कुंजी द्वारा उनके डेटा तत्वों तक पहुंच की अनुमति देता है। इन्हें उपलब्ध कराने का वैचारिक रूप यह है:

class X {
        value_type& operator[](index_type idx);
  const value_type& operator[](index_type idx) const;
  // ...
};

जब तक आप नहीं चाहते कि आपकी कक्षा के उपयोगकर्ता डेटा तत्वों को बदल सकें operator[] (इस मामले में आप गैर-कॉन्स संस्करण को छोड़ सकते हैं), आपको हमेशा ऑपरेटर के दोनों प्रकार प्रदान करना चाहिए।

यदि value_type को अंतर्निहित प्रकार के संदर्भ में जाना जाता है, तो ऑपरेटर के कॉन्स्ट संस्करण को कॉन्स्ट संदर्भ के बजाय प्रतिलिपि वापस करनी चाहिए।

सूचक-प्रकार के प्रकार के लिए ऑपरेटर

अपने खुद के इटरेटर्स या स्मार्ट पॉइंटर्स को परिभाषित करने के लिए, आपको यूनरी उपसर्ग डीरेंसेंस ऑपरेटर को अधिभारित करना होगा * और बाइनरी इंफिक्स पॉइंटर सदस्य एक्सेस ऑपरेटर ->:

class my_ptr {
        value_type& operator*();
  const value_type& operator*() const;
        value_type* operator->();
  const value_type* operator->() const;
};

ध्यान दें कि इन्हें भी हमेशा एक कॉन्स और गैर-कॉन्स संस्करण दोनों की आवश्यकता होगी। के लिए -> ऑपरेटर, अगर value_type उसका है class (या struct या union) टाइप करें, दूसरा operator->() एक तक, recursively कहा जाता है operator->() गैर-वर्ग प्रकार का मूल्य देता है।

ऑपरेटर का यूनरी पता कभी अधिभारित नहीं किया जाना चाहिए।

के लिये operator->*() देख यह प्रश्न। इसका शायद ही कभी उपयोग किया जाता है और इस प्रकार शायद ही कभी अधिभारित होता है। वास्तव में, यहां तक ​​कि इटेटर भी इसे अधिभारित नहीं करते हैं।


करने के लिए जारी रूपांतरण ऑपरेटर


898
2017-12-12 12:47



operator->() वास्तव में है अत्यंत अजीब। इसे वापस करने की आवश्यकता नहीं है value_type* वास्तव में, यह एक और वर्ग प्रकार वापस कर सकते हैं, बशर्ते कि कक्षा के प्रकार में एक है operator->(), जिसे बाद में बुलाया जाएगा। यह रिकर्सिव कॉलिंग operator->()एक तक की कमाई value_type* वापसी का प्रकार होता है। पागलपन! :) - j_random_hacker
मैं आपके पॉइंटर-जैसे ऑपरेटर के कॉन्स्ट / गैर-कॉन्स संस्करणों से असहमत हूं, उदा। `const value_type और ऑपरेटर * () const;` - यह एक होने जैसा होगा T* const एक लौट रहा है const T& dereferencing पर, जो मामला नहीं है। या दूसरे शब्दों में: एक कॉन्स पॉइंटर एक कॉन्स पॉइंट का संकेत नहीं देता है। वास्तव में, यह नकल करने के लिए तुच्छ नहीं है T const * - जो पूरे कारण है const_iterator मानक पुस्तकालय में सामान। निष्कर्ष: हस्ताक्षर होना चाहिए reference_type operator*() const; pointer_type operator->() const - Arne Mertz
एक टिप्पणी: सुझाव दिया गया बाइनरी अंकगणितीय ऑपरेटरों का कार्यान्वयन ऐसा नहीं हो सकता है। से बूस्ट ऑपरेटर हेडर सिमेट्री नोट: boost.org/doc/libs/1_54_0/libs/utility/operators.htm#symmetry यदि आप पहले पैरामीटर की स्थानीय प्रतिलिपि का उपयोग करते हैं, तो + = और स्थानीय प्रतिलिपि वापस लौटें, तो एक और प्रतिलिपि से बचा जा सकता है। यह एनआरवीओ अनुकूलन को सक्षम बनाता है। - Manu343726
जैसा कि मैंने बातचीत में उल्लेख किया था, L <= R के रूप में भी व्यक्त किया जा सकता है !(R < L) के बजाय !(L > R)। हार्ड-टू-ऑप्टिमाइज़ एक्सप्रेशन में इनलाइनिंग की एक अतिरिक्त परत को बचा सकता है (और यह भी है कि बूस्ट.ऑपरेटर्स इसे कैसे लागू करते हैं)। - TemplateRex
@ थॉमथॉम: यदि किसी वर्ग में अपने राज्य में आने के लिए सार्वजनिक रूप से सुलभ एपीआई नहीं है, तो आपको या तो अपने राज्य को किसी सदस्य या किसी तक पहुंचने के लिए आवश्यक सब कुछ करना होगा friend कक्षा का यह, ज़ाहिर है, सभी ऑपरेटरों के लिए भी सच है। - sbi


सी ++ में ऑपरेटर ओवरलोडिंग के तीन मूल नियम

जब सी ++ में ऑपरेटर ओवरलोडिंग की बात आती है, तो वहां हैं आपको तीन बुनियादी नियमों का पालन करना चाहिए। ऐसे सभी नियमों के साथ, वास्तव में अपवाद हैं। कभी-कभी लोग उनसे विचलित हो जाते हैं और परिणाम खराब कोड नहीं था, लेकिन ऐसे सकारात्मक विचलन कुछ और बहुत दूर हैं। कम से कम, मैंने देखा है कि 100 में से 99 विचलन में से 99 अन्यायपूर्ण थे। हालांकि, यह 1000 में से 999 भी हो सकता है। तो आप बेहतर नियमों के साथ बेहतर रहेंगे।

  1. जब भी ऑपरेटर का अर्थ स्पष्ट रूप से स्पष्ट और निर्विवाद नहीं होता है, तो उसे ओवरलोड नहीं किया जाना चाहिए।  इसके बजाय, एक अच्छी तरह से चुने गए नाम के साथ एक समारोह प्रदान करें।
    असल में, ओवरलोडिंग ऑपरेटरों के लिए पहला और सबसे बड़ा नियम, अपने दिल पर, कहता है: ऐसा मत करो। यह अजीब लग सकता है, क्योंकि ऑपरेटर ओवरलोडिंग के बारे में बहुत कुछ पता होना चाहिए और इसलिए बहुत सारे लेख, पुस्तक अध्याय, और अन्य ग्रंथ इस सब से निपटते हैं। लेकिन इस प्रतीत होता है कि स्पष्ट सबूत के बावजूद, केवल आश्चर्यजनक रूप से कुछ मामले हैं जहां ऑपरेटर अधिभार उचित है। इसका कारण यह है कि वास्तव में ऑपरेटर के आवेदन के पीछे अर्थशास्त्र को समझना मुश्किल होता है जब तक कि एप्लिकेशन डोमेन में ऑपरेटर का उपयोग अच्छी तरह से ज्ञात और निर्विवाद नहीं होता है। लोकप्रिय धारणा के विपरीत, यह शायद ही कभी मामला है।

  2. हमेशा ऑपरेटर के जाने-माने अर्थशास्त्र से चिपके रहें।
    सी ++ ओवरलोडेड ऑपरेटरों के अर्थशास्त्र पर कोई सीमा नहीं है। आपका कंपाइलर खुशी से कोड स्वीकार करेगा जो बाइनरी लागू करता है + ऑपरेटर अपने दाएं ऑपरेंड से घटाने के लिए। हालांकि, ऐसे ऑपरेटर के उपयोगकर्ता कभी अभिव्यक्ति पर संदेह नहीं करेंगे a + b घटाना a से b। बेशक, यह मानता है कि एप्लिकेशन डोमेन में ऑपरेटर का अर्थशास्त्र निर्विवाद है।

  3. हमेशा संबंधित संचालन के एक सेट से बाहर प्रदान करें।
    ऑपरेटर एक दूसरे से संबंधित हैंऔर अन्य परिचालनों के लिए। यदि आपका प्रकार का समर्थन करता है a + b, उपयोगकर्ताओं को कॉल करने में सक्षम होने की उम्मीद होगी a += bभी अगर यह उपसर्ग वृद्धि का समर्थन करता है ++a, वे उम्मीद करेंगे a++ साथ ही काम करने के लिए। अगर वे जांच सकते हैं कि क्या a < b, वे निश्चित रूप से यह भी जांचने में सक्षम होने की उम्मीद करेंगे कि क्या a > b। यदि वे आपके प्रकार की प्रतिलिपि बना सकते हैं, तो वे असाइनमेंट को भी काम करने की उम्मीद करते हैं।


करने के लिए जारी सदस्य और गैर-सदस्य के बीच निर्णय


441
2017-12-12 12:45



केवल एक चीज जिसे मैं जानता हूं जो इनमें से किसी का उल्लंघन करता है boost::spirit जबरदस्त हंसी। - Billy ONeal
@ बिली: कुछ के मुताबिक, दुर्व्यवहार + स्ट्रिंग कॉन्सटेनेशन के लिए एक उल्लंघन है, लेकिन अब यह अच्छी तरह से स्थापित प्रैक्सिस बन गया है, ताकि यह प्राकृतिक प्रतीत हो। हालांकि मुझे याद है कि घर-ब्रू स्ट्रिंग क्लास मैंने 90 के दशक में देखा जो बाइनरी का इस्तेमाल करता था &इस उद्देश्य के लिए (स्थापित praxis के लिए बेसिक का जिक्र)। लेकिन, हाँ, इसे std lib में डालकर मूल रूप से इसे पत्थर में सेट करें। दुर्व्यवहार के लिए भी यही है << तथा >> आईओ, बीटीडब्ल्यू के लिए। बाएं-स्थानांतरण क्यों स्पष्ट आउटपुट ऑपरेशन होगा? क्योंकि हम सभी ने इसके बारे में सीखा जब हमने अपना पहला "हैलो, दुनिया!" देखा आवेदन। और किसी अन्य कारण के लिए। - sbi
@curiousguy: अगर आपको इसे समझाना है, तो यह स्पष्ट रूप से स्पष्ट और निर्विवाद नहीं है। इसी प्रकार यदि आपको ओवरलोडिंग पर चर्चा या बचाव करने की आवश्यकता है। - sbi
@ एसबीआई: "सहकर्मी समीक्षा" हमेशा एक अच्छा विचार है। मेरे लिए एक बुरी तरह से चुनिंदा ऑपरेटर बुरी तरह से चुनिंदा कार्य नाम से अलग नहीं है (मैंने कई देखा)। ऑपरेटर सिर्फ काम कर रहे हैं। न आधिक न कम। नियम वही हैं। और यह समझने के लिए कि क्या कोई विचार अच्छा है, सबसे अच्छा तरीका समझ में आता है कि इसे समझने में कितना समय लगता है। (इसलिए, सहकर्मी समीक्षा एक जरूरी है, लेकिन साथियों को कुत्ते और पूर्वाग्रह से मुक्त लोगों के बीच चुना जाना चाहिए।) - Emilio Garavaglia
@ एसबीआई मेरे लिए, केवल एकमात्र बिल्कुल स्पष्ट और निर्विवाद तथ्य है operator== यह है कि यह एक समानता संबंध होना चाहिए (आईओयू, आपको गैर सिग्नलिंग NaN का उपयोग नहीं करना चाहिए)। कंटेनरों पर कई उपयोगी समकक्ष संबंध हैं। समानता का क्या अर्थ है? "a के बराबर होती है b" मतलब कि a तथा b वही गणितीय मूल्य है। एक (गैर-NaN) के गणितीय मूल्य की अवधारणा float स्पष्ट है, लेकिन एक कंटेनर के गणितीय मूल्य में कई अलग-अलग (प्रकार रिकर्सिव) उपयोगी परिभाषाएं हो सकती हैं। समानता की सबसे मजबूत परिभाषा "वे एक ही वस्तुएं हैं", और यह बेकार है। - curiousguy


सी ++ में ऑपरेटर ओवरलोडिंग का सामान्य सिंटेक्स

आप C ++ में अंतर्निहित प्रकारों के लिए ऑपरेटरों का अर्थ नहीं बदल सकते हैं, ऑपरेटरों को केवल उपयोगकर्ता द्वारा परिभाषित प्रकारों के लिए अधिभारित किया जा सकता है1। यही है, कम से कम एक ऑपरेटरों को उपयोगकर्ता द्वारा परिभाषित प्रकार का होना चाहिए। अन्य अधिभारित कार्यों के साथ, ऑपरेटरों को केवल एक बार पैरामीटर के एक निश्चित सेट के लिए अधिभारित किया जा सकता है।

सभी ऑपरेटरों को सी ++ में ओवरलोड नहीं किया जा सकता है। उन ऑपरेटरों में से जिन्हें ओवरलोड नहीं किया जा सकता है: .  ::  sizeof  typeid  .* और सी ++ में एकमात्र टर्नरी ऑपरेटर, ?: 

ऑपरेटरों में से जिन्हें सी ++ में ओवरलोड किया जा सकता है ये हैं:

  • अंकगणितीय आपरेटर: +  -  *  /  % तथा +=  -=  *=  /=  %= (सभी बाइनरी इंफिक्स); +  - (यूनरी उपसर्ग); ++  -- (यूनरी उपसर्ग और पोस्टफिक्स)
  • बिट हेरफेर: &  |  ^  <<  >> तथा &=  |=  ^=  <<=  >>= (सभी बाइनरी इंफिक्स); ~ (यूनरी उपसर्ग)
  • बूलियन बीजगणित: ==  !=  <  >  <=  >=  ||  && (सभी बाइनरी इंफिक्स); ! (यूनरी उपसर्ग)
  • स्मृति प्रबंधन: new  new[]  delete  delete[]
  • निहित रूपांतरण ऑपरेटर
  • मिश्रण: =  []  ->  ->*  ,  (सभी बाइनरी इंफिक्स); *  & (सभी यूनरी उपसर्ग) () (फ़ंक्शन कॉल, एन-आरी इंफिक्स)

हालांकि, तथ्य यह है कि आप कर सकते हैं इन सभी का अधिभार आप का मतलब नहीं है चाहिए ऐसा करो। ऑपरेटर ओवरलोडिंग के बुनियादी नियम देखें।

सी ++ में, ऑपरेटर के रूप में अधिभारित होते हैं विशेष नामों के साथ काम करता है। अन्य कार्यों के साथ, अधिभारित ऑपरेटरों को आम तौर पर या तो एक के रूप में लागू किया जा सकता है उनके बाएं ऑपरेंड के प्रकार का सदस्य कार्य या के रूप में गैर-सदस्य कार्य। चाहे आप चुनने के लिए स्वतंत्र हैं या किसी एक का उपयोग करने के लिए बाध्य हैं, कई मानदंडों पर निर्भर करता है।2 एक यूनरी ऑपरेटर @3, किसी ऑब्जेक्ट एक्स पर लागू होता है, जिसे या तो कहा जाता है operator@(x) या के रूप में x.operator@()। एक बाइनरी इंफिक्स ऑपरेटर @, वस्तुओं पर लागू किया x तथा y, या तो कहा जाता है operator@(x,y) या के रूप में x.operator@(y)4 

गैर-सदस्य फ़ंक्शंस के रूप में लागू किए जाने वाले ऑपरेटर कभी-कभी अपने ऑपरेंड के प्रकार का मित्र होते हैं।

1  "उपयोगकर्ता परिभाषित" शब्द थोड़ा भ्रामक हो सकता है। सी ++ अंतर्निर्मित प्रकारों और उपयोगकर्ता परिभाषित प्रकारों के बीच भेद बनाता है। उदाहरण के लिए पूर्व int, char, और double; उत्तरार्द्ध में सभी संरचना, वर्ग, संघ और enum प्रकार हैं, जिनमें मानक लाइब्रेरी के शामिल हैं, भले ही वे उपयोगकर्ता द्वारा परिभाषित नहीं हैं।

2  इसमें शामिल है बाद का हिस्सा इस FAQ के बारे में।

3  @ सी ++ में वैध ऑपरेटर नहीं है, इसलिए मैं इसे प्लेसहोल्डर के रूप में उपयोग करता हूं।

4  सी ++ में एकमात्र टर्नरी ऑपरेटर ओवरलोड नहीं किया जा सकता है और केवल एन-एरी ऑपरेटर को सदस्य फ़ंक्शन के रूप में लागू किया जाना चाहिए।


करने के लिए जारी सी ++ में ऑपरेटर ओवरलोडिंग के तीन मूल नियम


230
2017-12-12 12:46



%= एक "बिट मैनिपुलेशन" ऑपरेटर नहीं है - curiousguy
~ यूनरी उपसर्ग है, बाइनरी इंफिक्स नहीं। - mrkj
.* गैर-अधिभार योग्य ऑपरेटरों की सूची से गायब है। - celticminstrel
@celticminstrel: दरअसल, और 4.5 साल के लिए कोई भी ध्यान नहीं दिया ... इसे इंगित करने के लिए धन्यवाद, मैंने इसे अंदर रखा। - sbi
@ एचआर .: क्या आपने इस गाइड को पढ़ा था, आपको पता चलेगा कि क्या गलत है। मैं आम तौर पर सुझाव देता हूं कि आपको प्रश्न से जुड़े पहले तीन उत्तरों को पढ़ना चाहिए। यह आपके जीवन के आधे घंटे से अधिक नहीं होना चाहिए, और आपको बुनियादी समझ देता है। ऑपरेटर-विशिष्ट वाक्यविन्यास आप बाद में देख सकते हैं। आपकी विशिष्ट समस्या से पता चलता है कि आप अधिभारित करने का प्रयास करते हैं operator+() एक सदस्य समारोह के रूप में, लेकिन इसे एक मुफ्त समारोह का हस्ताक्षर दिया। देख यहाँ। - sbi


सदस्य और गैर-सदस्य के बीच निर्णय

बाइनरी ऑपरेटरों = (काम), [] (सरणी सदस्यता), -> (सदस्य पहुंच), साथ ही एन-आरी ()(फ़ंक्शन कॉल) ऑपरेटर, हमेशा के रूप में लागू किया जाना चाहिए सदस्य कार्य, क्योंकि भाषा के वाक्यविन्यास के लिए उन्हें आवश्यक है।

अन्य ऑपरेटरों को या तो सदस्यों या गैर-सदस्यों के रूप में लागू किया जा सकता है। उनमें से कुछ को, हालांकि, आमतौर पर गैर-सदस्य कार्यों के रूप में लागू किया जाना चाहिए, क्योंकि उनके बाएं ऑपरेंड को आपके द्वारा संशोधित नहीं किया जा सकता है। इनमें से सबसे प्रमुख इनपुट और आउटपुट ऑपरेटर हैं << तथा >>, जिनके बाएं ऑपरेशंस मानक पुस्तकालय से स्ट्रीम कक्षाएं हैं जिन्हें आप नहीं बदल सकते हैं।

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

  1. अगर यह एक है यूनरी ऑपरेटरइसे एक के रूप में लागू करें सदस्य समारोह।
  2. यदि एक बाइनरी ऑपरेटर का इलाज होता है दोनों समान रूप से संचालित होते हैं (यह उन्हें अपरिवर्तित छोड़ देता है), इस ऑपरेटर को ए के रूप में कार्यान्वित करें गैर - सदस्य समारोह।
  3. यदि एक बाइनरी ऑपरेटर करता है नहीं अपने दोनों ऑपरेटरों का इलाज करें समान रूप से (आमतौर पर यह अपने बाएं ऑपरेंड को बदल देगा), यह इसे बनाने के लिए उपयोगी हो सकता है सदस्य अपने बाएं ऑपरेंड के प्रकार का कार्य, अगर इसे ऑपरेंड के निजी हिस्सों तक पहुंचना है।

बेशक, अंगूठे के सभी नियमों के साथ, अपवाद हैं। यदि आपके पास एक प्रकार है

enum Month {Jan, Feb, ..., Nov, Dec}

और आप इसके लिए वृद्धि और कमी ऑपरेटर को अधिभारित करना चाहते हैं, आप इसे सदस्य कार्यों के रूप में नहीं कर सकते हैं, क्योंकि सी ++ में, enum प्रकारों में सदस्य कार्य नहीं हो सकते हैं। तो आपको इसे मुफ्त फ़ंक्शन के रूप में अधिभारित करना होगा। तथा operator<() क्लास टेम्पलेट में निहित क्लास टेम्पलेट के लिए कक्षा परिभाषा में सदस्य फ़ंक्शन इनलाइन के रूप में लिखने और पढ़ने के लिए बहुत आसान है। लेकिन ये वास्तव में दुर्लभ अपवाद हैं।

(तथापि, अगर आप अपवाद करते हैं, इस मुद्दे को मत भूलना constऑपरेंड के लिए मजबूती है कि, सदस्य कार्यों के लिए, निहित हो जाता है this तर्क। यदि ऑपरेटर गैर-सदस्य फ़ंक्शन के रूप में अपना बायां सबसे अधिक तर्क लेता है const संदर्भ, एक सदस्य समारोह के रूप में एक ही ऑपरेटर एक होना चाहिए const बनाने के अं