2015-04-02 11 views
34

जैसा कि मैं इसे समझता हूं, override कीवर्ड बताता है कि एक दी गई घोषणा एक आधार virtual विधि लागू करती है, और अगर मिलान मिलान विधि नहीं मिलती है तो संकलन विफल होना चाहिए।क्या अंतिम रूप से ओवरराइड करता है?

final कीवर्ड की मेरी समझ यह है कि यह संकलक को बताता है कि कोई भी वर्ग इस virtual फ़ंक्शन को ओवरराइड नहीं करेगा।

तो override final अनावश्यक है? It seems to compile fineoverride final पर कौन सी जानकारी बताती है कि final नहीं है? इस तरह के संयोजन के लिए उपयोग केस क्या है?

उत्तर

29

final फ़ंक्शन को पहले स्थान पर ओवरराइड करने की आवश्यकता नहीं है। इसके प्रभाव के रूप में 4

कुछ वर्ग B में एक आभासी समारोह f virt-विनिर्देशकfinal साथ और एक वर्ग DB एक समारोह से व्युत्पन्न में चिह्नित है, तो [class.virtual] में परिभाषित किया गया है/D::f ओवरराइड B::f, कार्यक्रम खराब है।

यही है। अब override final बस का मतलब होगा
अपने आप पर "यह समारोह एक आधार वर्ग एक (override) को ओवरराइड करता है और खुद को ओवरराइड नहीं किया जा सकता (final)।"
final एक कमजोर आवश्यकता थोपना चाहते हैं। override और final स्वतंत्र व्यवहार है।


ध्यान दें कि final केवल हालांकि आभासी कार्यों के लिए इस्तेमाल किया जा सकता - [वर्ग।मेम]/8

एक virt-विनिर्देशक-सेक केवल एक आभासी सदस्य समारोह (10.3) की घोषणा में प्रदर्शित होगी।

इसलिए घोषणा

void foo() final; 

प्रभावी रूप से एक ही है

virtual void foo() final override; 

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

+5

का उपयोग करें, मुझे आपका उत्तर पसंद है, लेकिन मैं इसे एक व्यावहारिक बिंदु से स्पष्ट करना चाहता हूं 'वर्चुअल शून्य एफ() अंतिम ओवरराइड' और 'शून्य एफ() फ़ाइनल' को इस अर्थ में समतुल्य समझा जाता है कि अगर वे कुछ ओवरराइड नहीं करते हैं तो दोनों विफल हो जाते हैं। 'अंतिम' केवल वर्चुअल फ़ंक्शंस के लिए मान्य है और 'f' की बाद की घोषणा केवल वर्चुअल है यदि यह किसी फ़ंक्शन को ओवरराइड करता है। बाद वाले के लिए त्रुटि संदेश हालांकि कम सटीक हो सकता है। –

+1

* बाद की घोषणा इरादे को स्पष्ट करती है * - मुझे ऐसा नहीं लगता है। आपके विवरण ने मुझे पहली घोषणा शैली ** को प्राथमिकता दी है, उसी कारण से मैं वर्चुअल विधि को ओवरराइड करते समय 'आभासी' जोड़ने से बचूंगा: इसमें कोई मान नहीं है। लेकिन यह निश्चित रूप से 'वर्चुअल शून्य foo() फ़ाइनल लिखने से बेहतर है; ' – Wolf

+0

@Wolf आपको नहीं लगता कि यह स्पष्ट मंशा व्यक्त करता है? क्या आप पहले घोषणापत्र द्वारा व्यक्त किए गए इरादे को स्पष्ट रूप से व्यक्त कर सकते हैं? – Columbo

13

final यह आवश्यक नहीं है कि फ़ंक्शन ओवरराइड हो। यह वर्चुअल फ़ंक्शन को के रूप में पर पहले विरासत पदानुक्रम में घोषणा के रूप में एक वर्चुअल फ़ंक्शन घोषित करने के लिए पूरी तरह से मान्य है (यदि कुछ हद तक संदिग्ध मूल्य है)।

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

+1

+1 वर्चुअल/तत्काल-अंतिम फ़ंक्शन का व्यवहार्य उपयोग-केस दिखाने के लिए। मुझे आश्चर्य है कि संकलक अभी भी एक vtable उत्पन्न करता है जब यह किया जाता है? – Carlton

+1

@ कार्टल यह कंपाइलर पर निर्भर है। जहां तक ​​मानक का संबंध है, यह कक्षा polymorphic बनाता है, हालांकि - जिसका मतलब है 'गतिशील_कास्ट' इसके लिए काम करना चाहिए, उदाहरण के लिए। मेरा मानना ​​है कि आभासी कार्यों के vtable कार्यान्वयन को 'dynamic_cast' काम करने के लिए vtable की भी आवश्यकता है। – Angew

+0

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

1

नहीं final आवश्यक नहीं है override। वास्तव में, आप virtual फ़ंक्शन घोषित कर सकते हैं जिसे आप तुरंत finalsee here घोषित करते हैं। final कीवर्ड बस बताता है कि class कोई भी व्युत्पन्न इस फ़ंक्शन का ओवरराइड बना सकता है।

override कीवर्ड महत्वपूर्ण है जिसमें यह लागू होता है कि आप वास्तव में वास्तव में वर्चुअल फ़ंक्शन को ओवरराइड कर रहे हैं (एक नया असंबंधित घोषित करने के बजाय)। देखें this post regarding override

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

+3

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

+1

सीपीपी कोर दिशानिर्देशों का कहना है कि केवल एक https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rh-override –

0

निम्नलिखित कोड (final विनिर्देशक के साथ) संकलित करता है। लेकिन संकलन विफल रहता है जब finaloverride final के साथ प्रतिस्थापित किया गया है। इस प्रकार override finalfinal की तुलना में अधिक जानकारी (और संकलन को रोकता है) बताता है।

class Base 
{ 
public: 
    virtual ~Base() {} 
}; 

class Derived : public Base 
{ 
public: 
    virtual void foo() final 
    { 
     std::cout << "in Derived foo\n"; 
    } 
}; 

अनिवार्य रूप से, override final कहते हैं इस विधि किसी भी व्युत्पन्न वर्ग और इस विधि एक आधार वर्ग में एक आभासी विधि ओवरराइड करता है में ओवरराइड नहीं किया जा सकता है। final अकेले बेस क्लास ओवरराइडिंग भाग निर्दिष्ट नहीं करता है।

+0

"अंतिम अकेले बेस क्लास ओवरराइडिंग भाग निर्दिष्ट नहीं करता है।" सही, लेकिन 'शून्य फू() अंतिम 'करता है। –

3

(निष्कर्ष देखने के लिए अगर आप जल्दी में हैं अंत पर जाएं।)

दोनों override और final एक आभासी समारोह में केवल घोषणा में दिखाई दे सकता है। और दोनों महत्वपूर्ण शब्दों का उपयोग उसी कार्य घोषणा में किया जा सकता है, लेकिन क्या यह दोनों का उपयोग करने के लिए उपयोगी है, दोनों स्थितियों पर निर्भर करता है।

एक उदाहरण के रूप में निम्नलिखित कोड डालें:

#include <iostream> 
using std::cout; using std::endl; 

struct B { 
    virtual void f1() { cout << "B::f1() "; } 
    virtual void f2() { cout << "B::f2() "; } 
    virtual void f3() { cout << "B::f3() "; } 
    virtual void f6() final { cout << "B::f6() "; } 
    void f7() { cout << "B::f7() "; } 
    void f8() { cout << "B::f8() "; } 
    void f9() { cout << "B::f9() "; } 
}; 

struct D : B { 
    void f1() override { cout << "D::f1() "; } 
    void f2() final { cout << "D::f2() "; } 
    void f3() override final { cout << "D::f3() "; } // need not have override 
    // should have override, otherwise add new virtual function 
    virtual void f4() final { cout << "D::f4() "; } 
    //virtual void f5() override final; // Error, no virtual function in base class 
    //void f6(); // Error, override a final virtual function 
    void f7() { cout << "D::f7() "; } 
    virtual void f8() { cout << "D::f8() "; } 
    //void f9() override; // Error, override a nonvirtual function 
}; 

int main() { 
    B b; D d; 
    B *bp = &b, *bd = &d; D *dp = &d; 
    bp->f1(); bp->f2(); bp->f3(); bp->f6(); bp->f7(); bp->f8(); bp->f9(); cout << endl; 
    bd->f1(); bd->f2(); bd->f3(); bd->f6(); bd->f7(); bd->f8(); bd->f9(); cout << endl; 
    dp->f1(); dp->f2(); dp->f3(); dp->f6(); dp->f7(); dp->f8(); dp->f9(); cout << endl; 
    return 0; 
} 

उत्पादन

B::f1() B::f2() B::f3() B::f6() B::f7() B::f8() B::f9() 
D::f1() D::f2() D::f3() B::f6() B::f7() B::f8() B::f9() 
D::f1() D::f2() D::f3() B::f6() D::f7() D::f8() B::f9() 
  1. f1() और f6() की तुलना है। हम जानते हैं कि override और final सैद्धांतिक रूप से अप्रिय है।

    • override का अर्थ है कि फ़ंक्शन अपनी बेस क्लास में वर्चुअल फ़ंक्शन ओवरराइड कर रहा है। f1() और f3() देखें।
    • final का अर्थ है कि फ़ंक्शन को व्युत्पन्न कक्षा से ओवरराइड नहीं किया जा सकता है। (लेकिन फ़ंक्शन को बेस क्लास वर्चुअल फ़ंक्शन को ओवरराइड करने की आवश्यकता नहीं है।) f6() और f4() देखें।
  2. f2() और f3() की तुलना करें। हम जानते हैं कि यदि सदस्य फ़ंक्शन को virtual के बिना घोषित किया गया है और final के साथ, इसका मतलब है कि यह पहले से ही बेस क्लास में वर्चुअल फ़ंक्शन को ओवरराइड करता है। इस मामले में, कुंजी शब्द override अनावश्यक है।

  3. f4() और f5() की तुलना करें। हम जानते हैं कि यदि सदस्य फ़ंक्शन virtual के साथ घोषित किया गया है और यदि यह पहले विरासत पदानुक्रम में वर्चुअल फ़ंक्शन नहीं है, तो हमें ओवरराइड संबंध निर्दिष्ट करने के लिए override का उपयोग करना चाहिए। अन्यथा, हम गलती से व्युत्पन्न कक्षा में नए वर्चुअल फ़ंक्शन को जोड़ सकते हैं।

  4. f1() और f7() की तुलना करें। हम जानते हैं कि किसी भी सदस्य फ़ंक्शन, न केवल वर्चुअल वाले, को व्युत्पन्न कक्षा में ओवरराइड किया जा सकता है। virtual निर्दिष्ट करता है पॉलिमॉर्फिज्म, जिसका अर्थ है कि चलाने के लिए कौन सा फ़ंक्शन संकलित समय के बजाए रन टाइम तक देरी हो रही है। (यह अभ्यास में से बचना चाहिए।)

  5. f7() और f8() की तुलना करें। हम जानते हैं कि हम बेस क्लास फ़ंक्शन को ओवरराइड भी कर सकते हैं और इसे एक नया आभासी बना सकते हैं। (किसी भी सदस्य समारोह D से प्राप्त आभासी हो जाएगा वर्ग के f8()। इसका मतलब है) (यह व्यवहार में भी बचने की जानी चाहिए।)

  6. f7() और f9() की तुलना करें। हम जानते हैं कि override हमें त्रुटि प्राप्त करने में मदद कर सकता है जब हम व्युत्पन्न कक्षा में वर्चुअल फ़ंक्शन को ओवरराइड करना चाहते हैं, जबकि बेस क्लास में virtual कुंजी शब्द जोड़ना भूल गए हैं।

निष्कर्ष में, अपने ही ध्यान में रखते हुए सबसे अच्छा अभ्यास है: आधार वर्ग में पहले आभासी समारोह की घोषणा में

  • केवल उपयोग virtual;
  • व्युत्पन्न कक्षा में वर्चुअल फ़ंक्शन ओवरराइड निर्दिष्ट करने के लिए हमेशा override का उपयोग करें, जब तक कि final निर्दिष्ट न हो।
संबंधित मुद्दे