2014-07-07 20 views
13

से वर्चुअल फ़ंक्शन हटाएं मेरे पास वर्चुअल बेस क्लास फ़ंक्शन है जिसे किसी विशेष व्युत्पन्न क्लास में कभी भी उपयोग नहीं किया जाना चाहिए। क्या इसे 'हटाने' का कोई तरीका है? मैं निश्चित रूप से इसे एक खाली परिभाषा दे सकता हूं लेकिन मैं इसके प्रयास का उपयोग संकलन-समय त्रुटि को फेंकने के बजाय कर दूंगा। सी ++ 11 delete विनिर्देशक मैं क्या चाहते हो जाएगा की तरह लगता है, लेकिनव्युत्पन्न वर्ग

class B 
{ 
    virtual void f(); 
}; 

class D : public B 
{ 
    virtual void f() = delete; //Error 
}; 

संकलन नहीं होगा; gcc, कम से कम, स्पष्ट रूप से मुझे उस फ़ंक्शन को हटाने नहीं देगा जिसमें गैर-हटाए गए मूल संस्करण हैं। क्या समान कार्यक्षमता पाने का कोई और तरीका है?

+0

यह कैसे संभव है? क्या आप 'डी * डी = नई डी() में कंपाइलर त्रुटि चाहते हैं; static_cast (डी) -> एफ() '? –

+1

@ ब्रायन चेन यदि इसे बेस क्लास में पॉइंटर पर डाला जाता है तो इसे बेस क्लास के पॉइंटर की तरह कार्य करना चाहिए - जिस स्थिति में 'D :: f' अभी भी कॉल किया जाएगा, हां। –

+0

और उस मामले में आप किस व्यवहार की अपेक्षा करेंगे? – Theolodis

उत्तर

22

यह मानक द्वारा अनुमति नहीं है, फिर भी आप निम्न दो तरीके दिए से एक का उपयोग एक समान व्यवहार प्राप्त करने के लिए कर सकता है।

पहले निजी को usingपरिवर्तन करने के लिए विधि की दृश्यता का उपयोग करने, इस प्रकार का उपयोग करने से दूसरों को रोकने होगा। उस समाधान के साथ समस्या यह है कि सुपर-क्लास के पॉइंटर पर विधि को कॉल करने से संकलन त्रुटि नहीं होती है।

class B 
{ 
public: 
    virtual void f(); 
}; 

class D : public B 
{ 
private: 
    using B::f; 
}; 

सबसे अच्छा समाधान जब D विधि बुला मैं अब तक पाया है एक संकलन समय त्रुटि प्राप्त करने के लिए एक सामान्य struct कि false_type से विरासत के साथ एक static_assert उपयोग करना है। जब तक कोई भी विधि कभी कॉल नहीं करता है, तब तक संरचना अनिश्चित रहती है और static_assert विफल नहीं होगा।

यदि विधि कहा जाता है, तो संरचना परिभाषित की जाती है और इसका मान गलत है, इसलिए static_assert विफल रहता है।

विधि नहीं कहा है, लेकिन आप सुपर वर्ग के एक सूचक पर कॉल करने के लिए प्रयास करते हैं, तो D विधि से परिभाषित नहीं है और आप एक undefined reference संकलन त्रुटि मिलती है।

template <typename T> 
struct fail : std::false_type 
{ 
}; 

class B 
{ 
public: 
    virtual void f() 
    { 
    } 
}; 

class D : public B 
{ 
public: 
    template<typename T = bool> 
    void 
    f() 
    { 
     static_assert (fail<T>::value, "Do not use!"); 
    } 
}; 

एक और वैकल्पिक हल होगा जब विधि प्रयोग किया जाता है एक अपवाद फेंक, लेकिन वह केवल रन-टाइम पर फेंक होगा।

+1

अच्छा, लेकिन 'बी * डी = नया डी(); डी-> एफ(); 'बिना त्रुटि के चलाएगा (और' बी :: एफ' बुलाया जाएगा), लेकिन यह ऐसी स्थिति है जिसे मैं एक त्रुटि उत्पन्न करना चाहता हूं। –

+0

@MattPhillips जैसा कि आपने अपनी टिप्पणियों में कहा था कि वैसे भी व्यवहार की उम्मीद की जा सकती है, या क्या मैं गलत हूं? – Theolodis

+0

नहीं, मैं कभी भी बेस क्लास संस्करण नहीं कहूंगा। ब्रायन चेन का मेरा जवाब अपडेट करने के बाद अपडेट किया गया था, मैं इस मामले में एक कंपाइलर त्रुटि चाहता हूं। –

10

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

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

आप मुश्किल हो चाहते हैं, तो आपको एक त्रुटि मजबूर कर सकते हैं, लेकिन यह संकलन समय के बजाय लिंक समय होना होगा:
सदस्य समारोह घोषित लेकिन कभी इसे परिभाषित नहीं करते (यह 100 नहीं है वर्चुअल फ़ंक्शन के लिए काम करने की गारंटी%)।
बेहतर चेतावनी __attribute__ ((deprecated)) के लिए जीसीसी बहिष्कृत विशेषता को भी देखें।
विवरण और समान एमएस जादू के लिए: C++ mark as deprecated

+0

ठीक +1। मुझे विशेष रूप से 'हटाएं' का उपयोग करने की परवाह नहीं है, मैं बस कार्यक्षमता चाहता हूं। –

+0

दरअसल, ऐसा करने से 'एफ' को कभी भी नहीं कहा जाता है, भले ही 'एफ' कभी नहीं कहा जाता है, जैसा कि [यहां] (http://ideone.com/JW0uKU) दिखाया गया है। –

+0

@ मैटफिलिप्स: कुछ कंप्यूटर्स/लिंकर्स शिकायत नहीं करते हैं कि कुछ वर्चुअल विधि के कार्यान्वयन वहां नहीं हैं। कभी-कभी कंपाइलर विकल्पों के आधार पर। फिर भी, सबसे अच्छी शर्त हमेशा बहिष्कार है। – Deduplicator

0

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

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

B* b = new D(); 
b.f(); 

यहाँ, आप एक B* चर में एक D की दुकान। इसलिए, यहां तक ​​कि यदि संकलक को बताने का कोई तरीका था कि आपको पर D पर कॉल करने की अनुमति नहीं है, तो संकलक यहां इस त्रुटि की रिपोर्ट करने में असमर्थ होगा, क्योंकि यह केवल B देखता है।

1

"मेरे पास वर्चुअल बेस क्लास फ़ंक्शन है जिसका उपयोग किसी विशेष व्युत्पन्न वर्ग में कभी नहीं किया जाना चाहिए।"

कुछ मामलों में यह एक विरोधाभास है। वर्चुअल फ़ंक्शंस का पूरा बिंदु बेस क्लास द्वारा प्रदान किए गए अनुबंध के विभिन्न कार्यान्वयन प्रदान करना है। आप जो करने की कोशिश कर रहे हैं वह अनुबंध तोड़ना है। सी ++ भाषा आपको ऐसा करने से रोकने के लिए डिज़ाइन की गई है। यही कारण है कि जब आप किसी ऑब्जेक्ट को तुरंत चालू करते हैं तो यह आपको शुद्ध वर्चुअल फ़ंक्शंस को लागू करने के लिए मजबूर करता है। और यही कारण है कि यह आपको अनुबंध का हिस्सा हटाने नहीं देगा।

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

हालांकि :

कभी कभी यह एक खाली कार्यान्वयन करता है करने के लिए उपयुक्त हो सकता है कुछ भी नहीं:

void MyClass::my_virtual_function() 
{ 
    // nothing here 
} 

या एक खाली कार्यान्वयन कि रिटर्न एक "विफल" की स्थिति:

bool MyClass::my_virtual_function() 
{ 
    return false; 
} 

यह सब निर्भर करता है कि आप क्या करने की कोशिश कर रहे हैं। शायद अगर आप किसी और को प्राप्त करने की कोशिश कर रहे हैं, तो आपको अधिक जानकारी दे सकती है जो आपको सही दिशा में इंगित कर सकती है।

संपादित

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

+0

"बेस क्लास को कॉल करने का पूरा बिंदु ..." यह सामान्य मामले के लिए सही है, लेकिन हमेशा नहीं; यदि यह हमेशा सत्य था तो सी ++ भाषा में 'static_cast' और 'dynamic_cast' नहीं होगा। कभी-कभी व्युत्पन्न वर्ग पहचान कॉलिंग कोड और उन समयों में से एक के लिए मायने रखती है। –

+0

व्युत्पन्न कक्षा से सीधे निपटने के लिए निश्चित रूप से कभी-कभी महत्वपूर्ण होता है। यह वास्तव में गतिशील_कास्ट के लिए क्या उपयोग किया जाता है। लेकिन फिर आप बेस क्लास संदर्भ/पॉइंटर को कॉल नहीं कर रहे हैं, इसलिए मेरा कथन वास्तव में लागू नहीं होता है। लेकिन मुझे आशा थी कि यह आपके प्रश्न का उत्तर देगा कि इसकी अनुमति क्यों नहीं है। – Galik

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