C ++ को विरासत में मिली मित्रता की अनुमति क्यों नहीं है?


93

C ++ में दोस्ती कम से कम वैकल्पिक रूप से अंतर्निहित क्यों नहीं है? मैं समझता हूं कि स्पष्ट कारणों के लिए परिवर्तनशीलता और संवेदनशीलता को निषिद्ध किया जाता है (मैं इसे केवल सरल FAQ उद्धरण उत्तरों को बंद करने के लिए कहता हूं), लेकिन virtual friend class Foo;पहेली की रेखाओं के साथ कुछ की कमी । क्या कोई इस फैसले के पीछे की ऐतिहासिक पृष्ठभूमि को जानता है? क्या दोस्ती वास्तव में सिर्फ एक सीमित हैक थी जिसने तब से कुछ अस्पष्ट सम्मानजनक उपयोगों में अपना रास्ता खोज लिया है?

स्पष्टीकरण के लिए संपादित करें: मैं निम्नलिखित परिदृश्य के बारे में बात कर रहा हूं, कि जहां ए के बच्चे बी या उसके दोनों बच्चों के संपर्क में हैं। मैं फ्रेंडली फंक्शन के ओवरराइड्स के लिए वैकल्पिक रूप से अनुदान देने की कल्पना भी कर सकता हूं, आदि।

class A {
  int x;
  friend class B;
};

class B {
  // OK as per friend declaration above.
  void foo(A& a, int n) { a.x = n; }
};

class D : public B { /* can't get in A w/o 'friend class D' declaration. */ };

स्वीकृत उत्तर: जैसा कि लोकी कहता है , फ्रेंडली बेस क्लास में संरक्षित प्रॉक्सी फ़ंक्शंस बनाकर प्रभाव को कम या ज्यादा अनुकरण किया जा सकता है, इसलिए किसी वर्ग या आभासी पद्धति से मित्रता को स्वीकार करने की कोई सख्त ज़रूरत नहीं है । मुझे बॉयलरप्लेट प्रॉक्सिज़ (जो प्रभावी रूप से मित्रवत आधार बन जाता है) की आवश्यकता नापसंद है, लेकिन मुझे लगता है कि यह एक भाषा तंत्र पर बेहतर समझा जाता था जिसका अधिक बार दुरुपयोग किया जाएगा। मुझे लगता है कि शायद यह समय है जब मैंने खरीदा और पढ़ने के लिए Stroupstrup की The Design and Evolution of C ++ , जो मैंने यहां देखा है कि बहुत से लोग इस प्रकार के प्रश्नों के बारे में बेहतर जानकारी प्राप्त करने के लिए सलाह देते हैं ...

जवाबों:


93

क्योंकि मैं लिख सकता हूँ Fooऔर उसके दोस्त Bar(इस प्रकार एक विश्वास संबंध है)।

लेकिन क्या मैं उन लोगों पर भरोसा करता हूं जो उन वर्गों को लिखते हैं जो इससे प्राप्त होते हैं Bar?
ज़रुरी नहीं। इसलिए उन्हें दोस्ती विरासत में नहीं मिलनी चाहिए।

किसी वर्ग के आंतरिक प्रतिनिधित्व में किसी भी परिवर्तन से उस प्रतिनिधित्व पर निर्भर किसी भी चीज के लिए एक संशोधन की आवश्यकता होगी। इस प्रकार एक वर्ग के सभी सदस्यों और कक्षा के सभी दोस्तों को संशोधन की आवश्यकता होगी।

इसलिए यदि आंतरिक प्रतिनिधित्व Fooको संशोधित किया जाता है, तो Barइसे संशोधित भी किया जाना चाहिए (क्योंकि मित्रता कसकर बांधती Barहै Foo)। यदि दोस्ती विरासत में मिली थी, तो Barउससे प्राप्त सभी वर्ग भी कसकर बंधे होंगे Fooऔर इस प्रकार संशोधन की आवश्यकता होगी यदि Fooआंतरिक प्रतिनिधित्व को बदल दिया जाए। लेकिन मुझे व्युत्पन्न प्रकारों का कोई ज्ञान नहीं है (और न ही आई। वे भी विभिन्न कंपनियों आदि द्वारा विकसित किए जा सकते हैं)। इस प्रकार मैं ऐसा करने में असमर्थ हो सकता हूं Fooक्योंकि ऐसा करने से कोड आधार में परिवर्तन तोड़ना शुरू हो जाएगा (जैसा कि मैं सभी वर्ग व्युत्पन्न को संशोधित नहीं कर सकता था Bar)।

इस प्रकार यदि दोस्ती विरासत में मिली थी तो आप अनजाने में एक वर्ग को संशोधित करने की क्षमता पर प्रतिबंध लगा रहे हैं। यह अवांछनीय है क्योंकि आप मूल रूप से सार्वजनिक एपीआई की अवधारणा को बेकार करते हैं।

ध्यान दें: का Barउपयोग Fooकरके का एक बच्चा पहुंच सकता है Bar, बस Barसंरक्षित में विधि बना सकता है। फिर अपने मूल वर्ग के माध्यम से कॉल करके बच्चे का Barउपयोग कर सकते हैं Foo

क्या ये वही है जो तुम चाहते हो?

class A
{
    int x;
    friend class B;
};

class B
{
    protected:
       // Now children of B can access foo
       void foo(A& a, int n) { a.x = n; }
};

class D : public B
{
    public:
        foo(A& a, int n)
        {
            B::foo(a, n + 5);
        }
};

4
बिंगो। यह सब उस नुकसान को सीमित करने के बारे में है जो आप एक वर्ग के आंतरिक को बदलकर कर सकते हैं।
j_random_hacker

सच कहूँ तो, मैं इस बारे में वास्तव में सोच रहा हूं अटॉर्नी-क्लाइंट पैटर्न है, जहां एक मध्यवर्ती इंटरफ़ेस सीमित प्रतिबंधित-पहुंच वर्ग के लिए आवरण के तरीकों को प्रस्तुत करके कक्षाओं को आउटसोर्स करने के लिए एक सीमित इंटरफ़ेस के रूप में कार्य करता है। यह कहना कि एक सटीक क्लास के बजाय अन्य कक्षाओं के सभी बच्चों के लिए एक इंटरफ़ेस उपलब्ध है, वर्तमान में सिस्टम की तुलना में बहुत अधिक उपयोगी होगा।
जेफ

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

@ मर्टिन: सही है, इस योजना में मित्र आधार को मित्र वर्ग में लाने के लिए इस्तेमाल किया जा सकता है, जो कई (यदि नहीं तो) मामलों में सरल इनकैप्सुलेशन उल्लंघन हो सकता है। हालांकि, ऐसी स्थितियों में जहां मित्र आधार एक सार वर्ग है, किसी भी व्युत्पन्न वर्ग को अपने स्वयं के पारस्परिक इंटरफ़ेस को लागू करने के लिए बाध्य किया जाएगा। मुझे यकीन नहीं है कि क्या इस परिदृश्य में एक 'थोपा हुआ' वर्ग को इनकैप्सुलेशन को तोड़ने या इंटरफ़ेस अनुबंध का उल्लंघन करने के लिए माना जाएगा यदि यह ईमानदारी से अपनी दावा की गई भूमिका को ठीक से करने की कोशिश नहीं करता है।
जेफ

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

48

C ++ में दोस्ती कम से कम वैकल्पिक रूप से अंतर्निहित क्यों नहीं है?

मुझे लगता है कि आपके पहले सवाल का जवाब इस सवाल में है: "क्या आपके पिता के दोस्तों की पहुंच आपके निजी लोगों तक है?"


36
निष्पक्ष होना, यह सवाल आपके पिता के बारे में परेशान करता है। । ।
इहानी

3
इस उत्तर का क्या मतलब है? सबसे अच्छी तरह से एक संदिग्ध हालांकि संभवतः हल्के दिल की टिप्पणी
DeveloperChris

11

एक मित्र वर्ग अपने दोस्त को एक्सेसर कार्यों के माध्यम से उजागर कर सकता है, और फिर उन लोगों के माध्यम से पहुंच प्रदान कर सकता है।

class stingy {
    int pennies;
    friend class hot_girl;
};

class hot_girl {
public:
    stingy *bf;

    int &get_cash( stingy &x = *bf ) { return x.pennies; }
};

class moocher {
public: // moocher can access stingy's pennies despite not being a friend
    int &get_cash( hot_girl &x ) { return x.get_cash(); }
};

यह वैकल्पिक ट्रांज़िटिविटी की तुलना में बेहतर नियंत्रण की अनुमति देता है। उदाहरण के लिए, get_cashहो सकता है protectedया क्रम सीमित पहुँच के एक प्रोटोकॉल लगा सकता है।


@ सेक्टर: रिफैक्टिंग के लिए वोटिंग! ;)
अलेक्जेंडर शुकेव

7

सी ++ मानक, अनुभाग 11.4 / 8

मित्रता न तो विरासत में मिली है और न ही सकर्मक है।

अगर दोस्ती विरासत में मिलती है, तो एक ऐसा वर्ग जो दोस्त बनने के लिए नहीं था, अचानक आपकी कक्षा के छात्रों तक पहुंच होगी और यह बाधा का उल्लंघन करता है।


2
कहो क्यू "दोस्त" ए, और बी ए से लिया गया है। यदि बी ए से दोस्ती विरासत में मिली है, तो क्योंकि बी एक प्रकार का ए है, तकनीकी रूप से यह ए है जो क्यू के निजी लोगों तक पहुंच है। तो यह किसी भी व्यावहारिक कारण के साथ सवाल का जवाब नहीं देता है।
mdenton8

2

क्योंकि यह सिर्फ अनावश्यक है।

friendकीवर्ड का उपयोग स्वयं संदिग्ध है। युग्मन की अवधि में यह सबसे खराब संबंध है (वंशानुक्रम और संरचना से आगे)।

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

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

यह एक बहुत ही सरल नियम लाता है:

एक वर्ग के आंतरिक को बदलना केवल कक्षा को ही प्रभावित करना चाहिए

बेशक, आप शायद इसके दोस्तों को प्रभावित करेंगे, लेकिन यहां दो मामले हैं:

  • मित्र मुक्त समारोह: शायद वैसे भी एक सदस्य समारोह के अधिक (मुझे लगता std::ostream& operator<<(...)है कि यहाँ है, जो विशुद्ध रूप से भाषा नियमों के दुर्घटना से सदस्य नहीं है
  • मित्र वर्ग? आपको वास्तविक कक्षाओं पर मित्र कक्षाओं की आवश्यकता नहीं है।

मैं सरल विधि के उपयोग की सिफारिश करूंगा:

class Example;

class ExampleKey { friend class Example; ExampleKey(); };

class Restricted
{
public:
  void forExampleOnly(int,int,ExampleKey const&);
};

यह सरल Keyपैटर्न आपको एक दोस्त (एक तरह से) घोषित करने की अनुमति देता है, वास्तव में इसे आपके आंतरिक तक पहुंच प्रदान किए बिना, इस प्रकार इसे परिवर्तनों से अलग करता है। इसके अलावा यह इस मित्र को आवश्यकता पड़ने पर न्यासियों (जैसे बच्चों) को अपनी चाबी देने की अनुमति देता है।


0

एक अनुमान: यदि एक वर्ग किसी अन्य वर्ग / कार्य को एक मित्र के रूप में घोषित करता है, तो ऐसा इसलिए है क्योंकि दूसरी इकाई को पहली बार विशेषाधिकार प्राप्त करने की आवश्यकता है। पहली से प्राप्त वर्गों की मनमानी संख्या के लिए दूसरी इकाई विशेषाधिकार प्राप्त का उपयोग करने में क्या उपयोग है?


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

@ जेफ़: आह, फिर मैंने आपके इच्छित अर्थ को गलत समझा। मुझे लगता है कि आप का मतलब है कि Bसभी वर्गों से विरासत में मिला होगा A...
ओलिवर चार्ल्सवर्थ

0

एक व्युत्पन्न वर्ग को केवल कुछ विरासत में मिल सकता है, जो आधार का 'सदस्य' है। एक मित्र घोषणा , मित्रतापूर्ण वर्ग का सदस्य नहीं है।

$ 11.4 / 1- "... एक मित्र का नाम वर्ग के दायरे में नहीं है, और मित्र को सदस्य अभिगम संचालकों (5.2.5) के साथ नहीं बुलाया जाता है जब तक कि वह किसी अन्य वर्ग का सदस्य नहीं है।"

$ 11.4 - "इसके अलावा, क्योंकि मित्र वर्ग का आधार-खंड उसके सदस्य घोषणाओं का हिस्सा नहीं है, इसलिए मित्र वर्ग का आधार-खंड मित्रता प्रदान करने वाले वर्ग के निजी और संरक्षित सदस्यों के नामों तक नहीं पहुंच सकता है।"

और आगे

$ 10.3 / 7- "[नोट: वर्चुअल स्पेसियर सदस्यता का तात्पर्य करता है, इसलिए वर्चुअल फ़ंक्शन नॉनमेम्बर (7.1.2) फ़ंक्शन नहीं हो सकता है। न ही वर्चुअल फ़ंक्शन एक स्थिर सदस्य हो सकता है, क्योंकि वर्चुअल फ़ंक्शन कॉल किसी विशिष्ट ऑब्जेक्ट पर निर्भर करता है। यह निर्धारित करने के लिए कि किस फ़ंक्शन को लागू करना है। एक कक्षा में घोषित एक आभासी फ़ंक्शन को किसी अन्य कक्षा में एक मित्र घोषित किया जा सकता है।]

चूँकि place मित्र ’पहली जगह में बेस क्लास का सदस्य नहीं है, इसलिए यह व्युत्पन्न वर्ग को विरासत में कैसे मिल सकता है?


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

0

एक कक्षा में फ्रेंड फंक्शन, एक्सटर्नल प्रॉपर्टी को फंक्शन में असाइन करता है। यानी एक्सटर्नल का मतलब है कि फंक्शन घोषित किया गया है और क्लास से बाहर कहीं परिभाषित किया गया है।

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


0

दोस्त कंटेनर के लिए शैली इंटरफ़ेस की तरह विरासत में अच्छा है, लेकिन मेरे लिए, जैसा कि पहले कहते हैं, C ++ में प्रचार योग्य विरासत का अभाव है

class Thing;

//an interface for Thing container's
struct IThing {
   friend Thing;
   protected:
       int IThing_getData() = 0;
};

//container for thing's
struct MyContainer : public IThing {
    protected: //here is reserved access to Thing
         int IThing_getData() override {...}
};

struct Thing {
    void setYourContainer(IThing* aContainerOfThings) {
        //access to unique function in protected area 
        aContainerOfThings->IThing_getData(); //authorized access
    }
};

struct ChildThing : public Thing {
    void doTest() {
        //here the lack of granularity, you cannot access to the container.
        //to use the container, you must implement all 
        //function in the Thing class
        aContainerOfThings->IThing_getData(); //forbidden access
    }
};

मेरे लिए C ++ की समस्या किसी भी चीज़ के लिए कहीं से भी सभी पहुँच को नियंत्रित करने के लिए बहुत अच्छी ग्रैन्युलैरिटी की कमी है:

दोस्त बात दोस्त बन सकता है। * बात करने के लिए सभी बच्चे के लिए पहुँच प्रदान करते हैं

और अधिक, दोस्त [नामित क्षेत्र] बात। * एक सटीक के लिए उपयोग प्रदान करने के लिए कंटेनर वर्ग में दोस्त के लिए विशेष नामित क्षेत्र के माध्यम से कर रहे हैं।

ठीक है सपना बंद करो। लेकिन अब, आप दोस्त का एक दिलचस्प उपयोग जानते हैं।

एक अन्य क्रम में, आप यह भी जान सकते हैं कि ज्ञात सभी वर्ग स्वयं के अनुकूल हैं। दूसरे शब्दों में, एक वर्ग उदाहरण
प्रतिबंध के बिना एक ही नाम के अन्य उदाहरण के सभी सदस्यों को कॉल कर सकता है :

class Object {
     private:
         void test() {}
     protected:
         void callAnotherTest(Object* anotherObject) {
             //private, but yes you can call test() from 
             //another object instance
             anotherObject)->test(); 
         }
};

0

सरल तर्क: 'मेरे एक मित्र जेन हैं। सिर्फ इसलिए कि हम कल दोस्त बन गए, उसके सभी दोस्त मुझे नहीं बनाते। '

मुझे अब भी उन व्यक्तिगत दोस्ती को मंजूरी देने की जरूरत है, और विश्वास का स्तर उसी के अनुसार होगा।

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