2011-02-04 11 views
167

नीचे दिए गए struct परिभाषा के साथ ...सी ++ "वर्चुअल" कीवर्ड। क्या ये ज़रूरी हैं?

struct A { 
    virtual void hello() = 0; 
}; 

कार्य # 1:

struct B : public A { 
    virtual void hello() { ... } 
}; 

कार्य # 2:

struct B : public A { 
    void hello() { ... } 
}; 

वहाँ ओवरराइड करने के लिए इन दो तरीकों के बीच कोई अंतर है हैलो समारोह?

+55

सी ++ में आप "वर्चुअल हैलो() ओवरराइड {}" लिख सकते हैं ताकि आप स्पष्ट रूप से घोषणा कर सकें कि आप वर्चुअल विधि को ओवरराइड कर रहे हैं। यदि आधार वर्चुअल विधि मौजूद नहीं है, तो संकलक विफल हो जाएगा, और यह वंश की कक्षा पर "वर्चुअल" रखने के समान पठनीयता है। – ShadowChaser

+0

असल में, जीसीसी के सी ++ 11 में, व्युत्पन्न वर्ग में शून्य हैलो() ओवरराइड {} लिखना ठीक है क्योंकि बेस क्लास ने निर्दिष्ट किया है कि विधि हैलो() वर्चुअल है। दूसरे शब्दों में, gder/g ++ के लिए _derived_ कक्षा में आभासी शब्द का उपयोग आवश्यक/अनिवार्य नहीं है। (मैं आरपीआई 3 पर जीसीसी संस्करण 4.9.2 का उपयोग कर रहा हूं) लेकिन किसी भी तरह से व्युत्पन्न वर्ग की विधि में वर्चुअल वर्चुअल को शामिल करना अच्छा अभ्यास है। – Will

उत्तर

138

वे बिल्कुल वही हैं। उनके अलावा कोई अंतर नहीं है कि पहले दृष्टिकोण को अधिक टाइपिंग की आवश्यकता है और संभावित रूप से स्पष्ट है।

+18

यह सच है, लेकिन [मोज़िला सी ++ पोर्टेबिलिटी गाइड] (https://developer.mozilla.org/en/C___Portability_Guide#Use_virtual_declaration_on_all_subclass_virtual_member_functions) हमेशा वर्चुअल का उपयोग करने की अनुशंसा करता है क्योंकि "कुछ कंपाइलर्स" समस्या चेतावनियां यदि आप नहीं करते हैं। बहुत बुरा वे इस तरह के कंपाइलरों के किसी भी उदाहरण का जिक्र नहीं करते हैं। –

+4

मैं यह भी जोड़ता हूं कि इसे स्पष्ट रूप से चिह्नित करने के रूप में वर्चुअल आपको विनाशक वर्चुअल को भी याद दिलाने में मदद करेगा। – lfalin

+1

केवल उल्लेख करने के लिए, [वर्चुअल विनाशक] पर लागू (http: // stackoverflow।कॉम/प्रश्न/21 9 837 9/वर्चुअल-डिस्ट्रक्टर्स-विरासत में हैं) – Atul

7

कंपाइलर के लिए कोई अंतर नहीं है, जब आप व्युत्पन्न कक्षा में virtual लिखते हैं या इसे छोड़ देते हैं।

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

67

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

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

+9

यह कौन सा संकलक है? –

+27

@ जेम्स: armcc (एआरएम उपकरणों के लिए एआरएम का कंपाइलर) – Clifford

+1

दिलचस्प। जानकारी के लिए धन्यवाद। –

9

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

0

मैं निश्चित रूप से बाल वर्ग के लिए वर्चुअल कीवर्ड शामिल करूंगा, क्योंकि i। पठनीयता। ii। इस बच्चे वर्ग को और नीचे ले लिया गया है, आप नहीं चाहते हैं कि आगे व्युत्पन्न कक्षा के निर्माता को इस वर्चुअल फ़ंक्शन को कॉल करें।

+0

मुझे बिंदु 'ii' समझ में नहीं आता है। क्या आप इसका विस्तार कर सकते हैं? –

+0

मुझे लगता है कि उसका मतलब है कि बच्चे के कार्य को वर्चुअल के रूप में चिह्नित किए बिना, एक प्रोग्रामर जो बाद में बच्चे वर्ग से निकलता है, यह महसूस नहीं कर सकता कि वास्तव में कार्य वर्चुअल है (क्योंकि उसने बेस क्लास को कभी नहीं देखा) और शायद इसे निर्माण के दौरान कॉल कर सकता है (जो सही चीज़ कर सकता है या नहीं कर सकता है)। – PfhorSlayer

29

virtual व्युत्पन्न कक्षा में कीवर्ड आवश्यक नहीं है। यहाँ मानक (N3337) (जोर मेरा) ड्राफ्ट C++ से समर्थन दस्तावेज है,:

10,3 आभासी कार्यों

2 एक आभासी सदस्य समारोह vf एक वर्ग Base में और एक कक्षा में घोषित किया जाता है तो Derived, Base से प्रत्यक्ष या परोक्ष रूप से व्युत्पन्न, एक सदस्य फ़ंक्शन vf उसी नाम, पैरामीटर-प्रकार-सूची (8.3.5), सीवी-योग्यता, और पुन: क्वालीफायर (या उसी की अनुपस्थिति) Base::vf घोषित किया गया है, तो Derived::vf वर्चुअल (चाहे या n भी है ओटी यह घोषित किया गया है) और यह Base::vf ओवरराइड करता है।

23

नहीं, virtual व्युत्पन्न कक्षाओं के वर्चुअल फ़ंक्शन ओवरराइड पर कीवर्ड आवश्यक नहीं है।लेकिन यह संबंधित पतन का उल्लेख करने लायक है: वर्चुअल फ़ंक्शन को ओवरराइड करने में विफलता।

ओवरराइड करने के लिए विफलता होता है अगर आप एक व्युत्पन्न वर्ग में एक आभासी समारोह को ओवरराइड, लेकिन हस्ताक्षर में कोई त्रुटि हो तो यह है कि यह एक नया और अलग आभासी समारोह वाणी करना चाहते हैं। यह फ़ंक्शन बेस क्लास फ़ंक्शन के अधिभार हो सकता है, या यह नाम में भिन्न हो सकता है। व्युत्पन्न वर्ग फ़ंक्शन घोषणा में virtual कीवर्ड का उपयोग करना चाहे या नहीं, संकलक यह बताने में सक्षम नहीं होगा कि आप बेस क्लास से किसी फ़ंक्शन को ओवरराइड करना चाहते हैं।

यह ख़तरा है, तथापि, शुक्र है जो स्रोत कोड स्पष्ट रूप से है कि एक सदस्य समारोह एक आधार वर्ग समारोह को ओवरराइड करने का इरादा है निर्दिष्ट कर सकते हैं सी ++ 11 explicit override भाषा सुविधा, ने संबोधित किया जाता है:

struct Base { 
    virtual void some_func(float); 
}; 

struct Derived : Base { 
    virtual void some_func(int) override; // ill-formed - doesn't override a base class method 
}; 

संकलक एक संकलन-समय त्रुटि जारी करेगा और प्रोग्रामिंग त्रुटि तुरंत स्पष्ट हो जाएगी (शायद डेरिवेट में फ़ंक्शन को float तर्क के रूप में लिया जाना चाहिए था)।

WP:C++11 का संदर्भ लें। आप टेम्पलेट्स और टेम्पलेट पैरामीटर (रों) के रूप में आधार वर्ग (ते) लेना शुरू जब

1

वहाँ काफी फर्क है:

struct None {}; 

template<typename... Interfaces> 
struct B : public Interfaces 
{ 
    void hello() { ... } 
}; 

struct A { 
    virtual void hello() = 0; 
}; 

template<typename... Interfaces> 
void t_hello(const B<Interfaces...>& b) // different code generated for each set of interfaces (a vtable-based clever compiler might reduce this to 2); both t_hello and b.hello() might be inlined properly 
{ 
    b.hello(); // indirect, non-virtual call 
} 

void hello(const A& a) 
{ 
    a.hello(); // Indirect virtual call, inlining is impossible in general 
} 

int main() 
{ 
    B<None> b;   // Ok, no vtable generated, empty base class optimization works, sizeof(b) == 1 usually 
    B<None>* pb = &b; 
    B<None>& rb = b; 

    b.hello();   // direct call 
    pb->hello();  // pb-relative non-virtual call (1 redirection) 
    rb->hello();  // non-virtual call (1 redirection unless optimized out) 
    t_hello(b);   // works as expected, one redirection 
    // hello(b);  // compile-time error 


    B<A>  ba;  // Ok, vtable generated, sizeof(b) >= sizeof(void*) 
    B<None>* pba = &ba; 
    B<None>& rba = ba; 

    ba.hello();   // still can be a direct call, exact type of ba is deducible 
    pba->hello();  // pba-relative virtual call (usually 3 redirections) 
    rba->hello();  // rba-relative virtual call (usually 3 redirections unless optimized out to 2) 
    //t_hello(b);  // compile-time error (unless you add support for const A& in t_hello as well) 
    hello(ba); 
} 

इसके बारे में मज़ा हिस्सा है कि अब आप इंटरफ़ेस और गैर इंटरफेस को परिभाषित कर सकते है वर्गों को परिभाषित करने के लिए बाद में कार्य करता है। यह पुस्तकालयों के बीच इंटरवर्किंग इंटरफेस के लिए उपयोगी है (एकल लाइब्रेरी की मानक डिज़ाइन प्रक्रिया के रूप में इस पर भरोसा न करें)। यह आपको अपनी सभी कक्षाओं के लिए अनुमति देने के लिए कुछ भी नहीं खर्च करता है - यदि आप चाहें तो typedef बी को कुछ भी हो सकता है।

ध्यान दें कि, यदि आप ऐसा करते हैं, तो आप प्रतिलिपि/चालक कन्स्ट्रक्टर को टेम्पलेट्स के रूप में घोषित करना चाहेंगे: विभिन्न इंटरफेस से निर्माण करने की अनुमति आपको अलग-अलग B<> प्रकारों के बीच 'कास्ट' करने की अनुमति देती है।

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

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