2011-02-03 14 views
8

मुझे पता है कि व्युत्पन्न वर्ग बस "को फिर से परिभाषित" कर सकते हैं आधार वर्ग सदस्य काम करता है, और एक व्युत्पन्न वर्ग वस्तु की कि समारोह कहा जाता है जब, समारोह में परिभाषित किया गया है कि व्युत्पन्न वर्ग का उपयोग किया जाता है, लेकिन ... क्या यह "वर्चुअल" कीवर्ड अनावश्यक नहीं है? मैंने को इन दो मामलों के बीच कुछ स्पष्ट रूप से महत्वपूर्ण अंतर पढ़े हैं (यानी: यदि आपके पास एक क्लास क्लास पॉइंटर है जो व्युत्पन्न क्लास को इंगित करता है और आप पर फ़ंक्शन कहते हैं, तो वर्चुअल व्युत्पन्न क्लास फ़ंक्शन कहा जाएगा, लेकिन यदि नहीं, तो बेस क्लास फ़ंक्शन को कॉल किया जाएगा)।सी ++ बहुरूपता/विरासत प्रश्न: बनाम आभासी कार्यों आधार कार्य की पुनर्व्याख्या

एक और तरीका रखो, सदस्य गैर-आभासी कार्यों के रूप में कार्य को फिर से परिभाषित करने में सक्षम होने का उद्देश्य क्या है, और क्या यह आमतौर पर अभ्यास का उपयोग किया जाता है?

व्यक्तिगत रूप से, ऐसा लगता है कि यह बहुत भ्रमित हो जाएगा।

धन्यवाद!

+2

लगता है जैसे आपको [एक अच्छी सी ++ पुस्तक] की आवश्यकता है (http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) –

उत्तर

1

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

#include "stdafx.h" 
#include <conio.h> 
#include <iostream> 

class Base 
{ 
public: 
    int Foo(){return 5;} 
}; 

class Derived:public Base 
{ 
    int Foo(){return 6;} 
}; 

int Func(Base* base) 
{ 
    return base->Foo(); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    Derived asdf; 

    std::cout << Func(&asdf); 
    getch(); 

    return 0; 
} 

इसका कारण यह है जिस तरह से आभासी कार्यों की है वापस आ जाएगी। जब किसी ऑब्जेक्ट में वर्चुअल कॉल होता है, तो जब आप वर्चुअल फ़ंक्शन को कॉल करते हैं तो सही फ़ंक्शन v-table में देखा जाता है। अन्यथा आपके पास वास्तव में विरासत नहीं है, आपके पास बेस पॉइंटर बेस क्लास की तरह काम कर रहा है जो व्युत्पन्न वर्ग नहीं है।

4

सबसे आम ओओपी भाषाओं (जावा, स्मॉलटाक, पायथन, आदि) पर सबसे आम दृष्टिकोण डिफ़ॉल्ट रूप से virtual के रूप में कार्य करता है।

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

हालांकि, वर्चुअल और गैर वर्चुअल विधि के बीच एक बहुत ही महत्वपूर्ण अंतर है। उदाहरण के लिए:

class SomeClass { ... }; 
class SomeSubclassOfSomeClass : public SomeClass { ... }; 
class AnotherSubclassOfSomeClass : public SomeClass { ... }; 

SomeClass* p = ...; 

p->someVirtualMethod(); 

p->someNonVirtualMethod(); 

वास्तविक कोड निष्पादित जब someVirtualMethod कॉल किया जाता है संदर्भित सूचक p, पर SomeClass उपवर्गों परिभाषा पूरी तरह से निर्भर करता है की ठोस प्रकार पर निर्भर करता है।

लेकिन someNonVirtualMethod कॉल पर निष्पादित कोड स्पष्ट है: हमेशा SomeClass पर एक, क्योंकि पी चर के प्रकार SomeClass है।

0

यह सावधान रहना महत्वपूर्ण है कि विनाशकों को व्युत्पन्न कक्षाओं में गैर-आभासी और "ओवरराइड" के रूप में चिह्नित किया गया हो - यह संभव है कि कक्षा को आधार वर्ग के लिए पॉइंटर पर विनाशक के रूप में बुलाया जाने पर कक्षा ठीक से साफ न हो।

0

ऐसा लगता है कि आपको बहुरूपता की शक्ति नहीं मिली है।पॉलिमॉर्फिज्म इस तरह से काम करता है:

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

, आप इसे प्राप्त नहीं कर सकते हैं। और इस तंत्र को "आभासी"

1

संबोधित करना: "एक और तरीका रखें, सदस्य कार्यों को गैर-आभासी कार्यों के रूप में फिर से परिभाषित करने में सक्षम होने का उद्देश्य क्या है, और क्या यह आमतौर पर उपयोग किया जाने वाला अभ्यास है?"

ठीक है, आप नहीं कर सकते। यदि बेस क्लास विधि वर्चुअल है, तो यह संबंधित व्युत्पन्न क्लास विधि है, यदि यह मौजूद है, तो 'वर्चुअल' कीवर्ड का उपयोग किया जाता है या नहीं।

तो: "क्या यह" वर्चुअल "कीवर्ड अनावश्यक नहीं है?"। हां, यह व्युत्पन्न क्लास विधि में अनावश्यक है, लेकिन बेस क्लास में नहीं है।

हालांकि, ध्यान दें कि गैर-वर्चुअल विधि रखने के लिए यह असामान्य (विनम्र होना) है और फिर इसे एक व्युत्पन्न कक्षा को फिर से परिभाषित करें।

2

ऐसा लगता है कि आप वर्चुअल और गैर-आभासी तरीकों के बीच अंतर को पहले से ही जानते हैं, इसलिए मैं इसमें अन्य लोगों के रूप में नहीं जाऊंगा। सवाल यह है कि, एक गैर वर्चुअल विधि वर्चुअल एक से अधिक उपयोगी कब होगी?

मामलों में आप किसी vtable सूचक हर वस्तु में शामिल होने का अतिरिक्त व्यय नहीं चाहते हैं, तो आप यह सुनिश्चित करने के दर्द ले कक्षा में कोई आभासी तरीके हैं। उदाहरण के लिए एक वर्ग का प्रतिनिधित्व करें जो एक बिंदु का प्रतिनिधित्व करता है और इसमें दो सदस्य हैं, x और y - आपके पास इन बिंदुओं का एक बहुत बड़ा संग्रह हो सकता है, और एक vtable सूचक वस्तु के आकार को कम से कम 50% तक बढ़ा देगा।

0

सी ++ स्कॉइंग और नाम-लुकअप नियम बहुत अजीब चीज़ों की अनुमति देते हैं, और विधियां यहां अकेली नहीं हैं। दरअसल (या पीछा) छिपाई जा रही है सकते हैं कई अलग अलग स्थितियों में होते हैं:

int i = 3; 
for (int i = 0; i != 5; ++i) { ... } // the `i` in `for` hides the `i` out of it 

struct Base 
{ 
    void foo(); 
    int member; 
}; 

struct Derived: Base 
{ 
    void foo(); // hides Base::foo 
    int member; // hides Base::member 
}; 

तो क्यों? लचीलापन के लिए।

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

  • मेरे कॉल हमेशा Base विधि, चाहे कुछ बच्चे कॉल करेंगे इसे ओवरराइड या
  • नहीं बच्चे पर एक कॉल कोई प्रभाव नहीं पड़ेगा, इस प्रकार मैं अचानक ही किसी अन्य प्रोग्रामर का कोड क्रैश नहीं करेंगे

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

0

यह समझाने के लिए सबसे आसान तरीका शायद यह है:

आभासी, आप के लिए कुछ देखने करता है एक आभासी लुकअप तालिका जोड़कर।

दूसरे शब्दों में, अगर आप आभासी कीवर्ड नहीं था, और एक विधि overrode, तब भी आप [अगर सी ++ वाक्य रचना के लिए मेरी स्मृति स्थानों में कुछ पुरानी है मुझे माफ कर दो] कि विधि मैन्युअल रूप से फोन करना होगा :

typedef doSomethingWithAnA(A::*doSomethingPtr)(); 
map<type_info, doSomethingWithAnA> A_doSomethingVTable; 

void someOtherFuncA* thing) { 
    doSomethingWithAnA methodToCall = A_doSomethingVTable[typeid(thing)]; 
    thing->(*methodToCall)(); 
} 

अब, यह एक उच्च स्तरीय दृष्टिकोण की अधिक है:

class A { void doSomething() { cout << "1"; } } 
class B: public A { void doSomething() { cout << "2"; } } 
class C: public A { void doSomething() { cout << "3"; } } 

void someOtherFunc(A* thing) { 
    if (typeid(thing) == typeid(B)) { 
     static_cast<B*>(thing)->doSomething(); 
    } else if (typeid(thing) == typeid(C)) { 
     static_cast<C*>(thing)->doSomething(); 
    } else { 
     // not a derived class -- just call A's method 
     thing->doSomething(); 
    } 
} 

आप इस एक छोटे से, एक लुकअप तालिका का उपयोग कर (पठनीयता और प्रदर्शन, सबसे अधिक संभावना के लिए) का अनुकूलन कर सकते हैं। सी ++ कंपाइलर स्पष्ट रूप से यह जानने के लिए थोड़ा सा अनुकूलित कर सकता है कि "type_info" क्या है, और इसी तरह। तो शायद, उस "मानचित्र" और लुकअप के बजाय "methodToCall = aDoSomethingVTable [टाइपिड (चीज़)]", फिर कॉल करें, ", कंपाइलर कुछ छोटे और तेज़ डालने वाला है, जैसे" doSomethingWithAnA * A_doSomethingVTable; "इसके बाद" A_doSomethingTablething -> TYPE_NUMBER "

तो आप सही है कि सी ++ वास्तव में आभासी नहीं की जरूरत है कर रहे हैं, लेकिन यह आपके जीवन को आसान बनाने के लिए वाक्यात्मक चीनी का एक बहुत जोड़ने करता है, और यह बेहतर भी अनुकूलन कर सकते हैं

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

संबंधित मुद्दे