2009-08-02 24 views
127

मैं आभासी विनाशक की आवश्यकता को समझता हूं। लेकिन हमें शुद्ध वर्चुअल विनाशक की आवश्यकता क्यों है? सी ++ लेखों में से एक में, लेखक ने उल्लेख किया है कि जब हम कक्षा सार बनाना चाहते हैं तो हम शुद्ध वर्चुअल विनाशक का उपयोग करते हैं।हमें सी ++ में शुद्ध आभासी विनाशक की आवश्यकता क्यों है?

लेकिन हम किसी भी सदस्य को शुद्ध आभासी के रूप में बनाकर एक वर्ग सार बना सकते हैं।

तो मेरे सवालों का

  1. जब हम वास्तव में एक नाशक शुद्ध आभासी करते हैं? क्या कोई अच्छा वास्तविक समय उदाहरण दे सकता है?

  2. जब हम अमूर्त कक्षाएं बना रहे हैं तो यह विनाशक को शुद्ध वर्चुअल बनाने का एक अच्छा अभ्यास है? यदि हां..तो क्यों?

+4

एकाधिक डुप्लीकेट: http://stackoverflow.com/questions/999340/question-about-pure-virtual-destructor और http://stackoverflow.com/questions/630950/pure-virtual-destructor-in-c उनमें से दो होने के नाते –

+12

@ डैनियल- उल्लिखित लिंक मेरे प्रश्न का उत्तर नहीं देते हैं। यह जवाब देता है कि एक शुद्ध आभासी विनाशक की परिभाषा क्यों होनी चाहिए। मेरा सवाल यह है कि हमें शुद्ध वर्चुअल विनाशक की आवश्यकता क्यों है। – Mark

+0

मैं कारण जानने का प्रयास कर रहा था, लेकिन आप पहले से ही सवाल पूछ चुके हैं। – user373215

उत्तर

98
  1. शायद असली कारण यह है कि शुद्ध आभासी विनाशकर्ता अनुमति दी जाती है कि प्रतिबंधित करने के लिए उन्हें भाषा के लिए एक और नियम जोड़ने का मतलब होगा और इस नियम के लिए कोई जरूरत नहीं क्योंकि कोई बुरे प्रभावों एक शुद्ध आभासी नाशक की इजाजत दी से आ सकता है वहाँ ।

  2. नहीं, सादा पुराना वर्चुअल पर्याप्त है।

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

ध्यान दें कि चूंकि संकलक व्युत्पन्न कक्षाओं के लिए एक निहित विनाशक उत्पन्न करेगा, यदि वर्ग का लेखक ऐसा नहीं करता है, तो कोई व्युत्पन्न कक्षा अमूर्त नहीं होगी। इसलिए बेस क्लास में शुद्ध वर्चुअल विनाशक होने से व्युत्पन्न कक्षाओं के लिए कोई फर्क नहीं पड़ता है। यह केवल बेस क्लास सार (@kappa की टिप्पणी के लिए धन्यवाद) बना देगा।

कोई यह भी मान सकता है कि प्रत्येक व्युत्पन्न वर्ग को शायद विशिष्ट क्लीन-अप कोड होना चाहिए और शुद्ध वर्चुअल विनाशक को एक लिखने के लिए अनुस्मारक के रूप में उपयोग करना होगा, लेकिन यह प्रतीत होता है (और अप्रशिक्षित)।

नोट: नाशक एकमात्र तरीका भले ही है कि यह शुद्ध आभासी है आदेश व्युत्पन्न वर्ग का दृष्टांत में एक कार्यान्वयन के लिए (हाँ शुद्ध आभासी कार्यों कार्यान्वयन हो सकता है) है।

struct foo { 
    virtual void bar() = 0; 
}; 

void foo::bar() { /* default implementation */ } 

class foof : public foo { 
    void bar() { foo::bar(); } // have to explicitly call default implementation. 
}; 
+9

"हाँ शुद्ध आभासी कार्यों में कार्यान्वयन हो सकते हैं" फिर यह शुद्ध वर्चुअल नहीं है। – GManNickG

+2

यदि आप एक वर्ग सार बनाना चाहते हैं, तो क्या सभी रचनाकारों को सुरक्षित बनाने के लिए यह आसान नहीं होगा? – bdonlan

+70

@GMan, आप गलत हैं, शुद्ध आभासी होने का अर्थ है व्युत्पन्न कक्षाओं को इस विधि को ओवरराइड करना चाहिए, यह कार्यान्वयन करने के लिए ऑर्थोगोनल है। यदि आप अपने लिए देखना चाहते हैं तो मेरा कोड देखें और 'foof :: bar' पर टिप्पणी करें। – Motti

-2

1) जब आप व्युत्पन्न कक्षाओं को साफ-सफाई करने की आवश्यकता चाहते हैं। यह दुर्लभ है।

2) नहीं, लेकिन आप इसे आभासी होना चाहते हैं, हालांकि।

18

आप एक सार आधार वर्ग बनाना चाहते हैं:

  • कि instantiated नहीं किया जा सकता है (हां, यह शब्द "सार" के साथ बेमानी है!)
  • लेकिन आभासी नाशक व्यवहार (यदि आप इसके बजाय व्युत्पन्न प्रकार के संकेत दिए गए से एबीसी की ओर इशारा चारों ओर ले जाने के लिए करना चाहते हैं, और उन के माध्यम से हटाना)
  • लेकिन अन्य के लिए किसी भी अन्य आभासी प्रेषण व्यवहार की जरूरत नहीं है की जरूरत है तरीकों (शायद वहाँ कोई अन्य तरीके हैं? एक साधारण संरक्षित "संसाधन" कंटेनर है कि एक कंस्ट्रक्टर्स/नाशक/काम की जरूरत है, लेकिन बहुत ज्यादा नहीं किसी और पर विचार)

... यह वर्ग सार बनाने के लिए सबसे आसान है बनाने के द्वारा विनाशक शुद्ध आभासी और इसके लिए एक परिभाषा (विधि निकाय) प्रदान करते हैं।

हमारे काल्पनिक एबीसी के लिए:

आप गारंटी है कि यह instantiated नहीं किया जा सकता है (यहां तक ​​कि कक्षा में ही करने के लिए आंतरिक, इस कारण है कि निजी निर्माताओं के लिए पर्याप्त नहीं हो सकता है), तो आप आभासी व्यवहार आप नाशक के लिए चाहते, और आपको किसी अन्य विधि को खोजने और टैग करने की आवश्यकता नहीं है जिसे वर्चुअल प्रेषण को "वर्चुअल" के रूप में नहीं चाहिए।

27

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

+1

लेकिन फिर भी शुद्ध virtaul विनाशक के कार्यान्वयन प्रदान करने के लिए क्यों। संभवतः गलत क्या हो सकता है मैं एक विनाशक शुद्ध वर्चुअल बना देता हूं और इसका कार्यान्वयन प्रदान नहीं करता है। मुझे लगता है कि केवल बेस क्लास पॉइंटर्स घोषित किए जाते हैं और इसलिए अमूर्त वर्ग के लिए विनाशक को कभी नहीं कहा जाता है। –

+3

@ सर्फिंग: क्योंकि व्युत्पन्न वर्ग का विनाश निस्संदेह अपनी बेस क्लास के विनाशक को बुलाता है, भले ही वह विनाशक शुद्ध आभासी हो। इसलिए यदि इसके लिए कोई कार्यान्वयन नहीं है तो अपरिभाषित व्यवहार होने जा रहा है। –

3

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

-2

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

+0

-1: सवाल यह नहीं है कि एक विनाशक आभासी क्यों होना चाहिए। – Troubadour

+0

इसके अलावा, कुछ स्थितियों में विनाशकों को सही विनाश प्राप्त करने के लिए आभासी होने की आवश्यकता नहीं है। आभासी विनाशकों को केवल तभी जरूरी होता है जब आप बेस क्लास में पॉइंटर पर 'डिलीट' कॉल करना समाप्त करते हैं, वास्तव में यह इसके व्युत्पन्न को इंगित करता है। – CygnusX1

+0

आप 100% सही हैं। यह सी ++ कार्यक्रमों में लीक और दुर्घटनाओं के पहले एक स्रोत में से एक है, तीसरा केवल नल पॉइंटर्स के साथ चीजों को करने और सरणी की सीमाओं से अधिक करने की कोशिश करने के लिए। एक गैर-वर्चुअल बेस क्लास विनाशक को जेनेरिक पॉइंटर पर कॉल किया जाएगा, उप-वर्ग विनाशक को पूरी तरह से बाईपास करना अगर यह आभासी चिह्नित नहीं है। यदि उप-वर्ग से संबंधित कोई गतिशील रूप से निर्मित वस्तुएं हैं, तो उन्हें हटाने के लिए कॉल पर आधार विनाशक द्वारा पुनर्प्राप्त नहीं किया जाएगा। आप ठीक से ब्लूअरक के साथ चिपक रहे हैं! (यह भी खोजने के लिए मुश्किल है।) –

0

आपने एक उदाहरण मांगा, और मेरा मानना ​​है कि निम्नलिखित शुद्ध आभासी विनाशक का कारण प्रदान करता है। मैं कि क्या यह एक अच्छा कारण है के रूप में उत्तर के लिए तत्पर हैं ...

मैं किसी error_base प्रकार फेंकने के लिए सक्षम होने के लिए नहीं करना चाहते हैं, लेकिन अपवाद टाइप error_oh_shucks और error_oh_blast समान कार्यक्षमता है और मैं डॉन ' टी इसे दो बार लिखना नहीं चाहता। मेरे ग्राहकों को std::string को उजागर करने से बचने के लिए पीआईएमपीएल जटिलता आवश्यक है, और std::auto_ptr के उपयोग की प्रतिलिपि बनाने की आवश्यकता है।यहाँ

// error.h 

#include <exception> 
#include <memory> 

class exception_string; 

class error_base : public std::exception { 
public: 
    error_base(const char* error_message); 
    error_base(const error_base& other); 
    virtual ~error_base() = 0; // Not directly usable 

    virtual const char* what() const; 
private: 
    std::auto_ptr<exception_string> error_message_; 
}; 

template<class error_type> 
class error : public error_base { 
public: 
    error(const char* error_message) : error_base(error_message) {} 
    error(const error& other) : error_base(other) {} 
    ~error() {} 
}; 

// Neither should these classes be usable 
class error_oh_shucks { virtual ~error_oh_shucks() = 0; } 
class error_oh_blast { virtual ~error_oh_blast() = 0; } 

और साझा दिया गया है::

सार्वजनिक हैडर मेरे पुस्तकालय द्वारा फेंका जा रहा है अपवाद विनिर्देशों कि अपवाद के विभिन्न प्रकार के भेद करने के लिए ग्राहक के लिए उपलब्ध हो जाएगा शामिल

// error.cpp 

#include "error.h" 
#include "exception_string.h" 

error_base::error_base(const char* error_message) 
    : error_message_(new exception_string(error_message)) {} 

error_base::error_base(const error_base& other) 
    : error_message_(new exception_string(other.error_message_->get())) {} 

error_base::~error_base() {} 

const char* error_base::what() const { 
    return error_message_->get(); 
} 

अपवाद_स्ट्रिंग क्लास, निजी रखा गया है, मेरे सार्वजनिक इंटरफेस से std :: स्ट्रिंग छुपाता है:

// exception_string.h 

#include <string> 

class exception_string { 
public: 
    exception_string(const char* message) : message_(message) {} 

    const char* get() const { return message_.c_str(); } 
private: 
    std::string message_; 
}; 
,210

मेरे कोड तो एक त्रुटि फेंकता के रूप में:

#include "error.h" 

throw error<error_oh_shucks>("That didn't work"); 

error के लिए एक टेम्पलेट के उपयोग के एक छोटे से नि: शुल्क है। यह रूप में त्रुटियों को पकड़ने के लिए ग्राहकों की आवश्यकता होती है की कीमत पर कोड का एक सा बचाता है:

// client.cpp 

#include <error.h> 

try { 
} catch (const error<error_oh_shucks>&) { 
} catch (const error<error_oh_blast>&) { 
} 
1

यहाँ मैं बताने के लिए जब हम आभासी नाशक जरूरत चाहते हैं और जब हम शुद्ध आभासी नाशक की जरूरत

class Base 
{ 
public: 
    Base(); 
    virtual ~Base() = 0; // Pure virtual, now no one can create the Base Object directly 
}; 

Base::Base() { cout << "Base Constructor" << endl; } 
Base::~Base() { cout << "Base Destructor" << endl; } 


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

Derived::Derived() { cout << "Derived Constructor" << endl; } 
Derived::~Derived() { cout << "Derived Destructor" << endl; } 


int _tmain(int argc, _TCHAR* argv[]) 
{ 
    Base* pBase = new Derived(); 
    delete pBase; 

    Base* pBase2 = new Base(); // Error 1 error C2259: 'Base' : cannot instantiate abstract class 
} 
  1. जब आप चाहते हैं कि कोई भी बेस क्लास का ऑब्जेक्ट सीधे बनाने में सक्षम न हो, तो शुद्ध आभासी विनाशक virtual ~Base() = 0 का उपयोग करें। आम तौर पर कम-से-कम एक शुद्ध वर्चुअल फ़ंक्शन की आवश्यकता होती है, चलिए इस फ़ंक्शन के रूप में virtual ~Base() = 0 लेते हैं।

  2. आप ऊपर बात की जरूरत नहीं है, तो केवल आप व्युत्पन्न वर्ग वस्तु के सुरक्षित विनाश

    बेस जरूरत * pBase = नए व्युत्पन्न(); पीबीएस हटाएं; शुद्ध वर्चुअल विनाशक की आवश्यकता नहीं है, केवल आभासी विनाशक ही काम करेगा।

7

जवाब मैं आपके सवाल का पढ़ा है से, मैं एक अच्छा कारण वास्तव में एक शुद्ध आभासी नाशक उपयोग करने के लिए अनुमान नहीं कर सका। उदाहरण के लिए, निम्नलिखित कारण मुझे बिल्कुल विश्वास नहीं करता है:

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

मेरी राय में, शुद्ध वर्चुअल विनाशक उपयोगी हो सकते हैं। उदाहरण के लिए, मान लीजिए कि आपके कोड में दो क्लास myClassA और myClassB हैं, और MyClassB को MyClassA से प्राप्त होता है। स्कॉट मेयर्स द्वारा अपनी पुस्तक "मोर इफेक्टिव सी ++" में वर्णित कारणों के लिए, आइटम 33 "गैर-पत्ते वर्गों को अमूर्त बनाना", वास्तव में एक अमूर्त वर्ग myAbstractClass बनाने के लिए बेहतर अभ्यास है जिससे myClassA और myClassB का उत्तराधिकारी है। यह बेहतर अमूर्तता प्रदान करता है और कुछ समस्याएं उत्पन्न करता है, उदाहरण के लिए, ऑब्जेक्ट प्रतियां।

अमूर्त प्रक्रिया (क्लास myAbstractClass बनाने के) में, यह हो सकता है कि MyClassA या MyClassB का कोई तरीका शुद्ध वर्चुअल विधि (जो मेरे एबस्ट्रक्लास के लिए सारणी के लिए एक पूर्व शर्त है) के लिए एक अच्छा उम्मीदवार है। इस मामले में, आप अमूर्त वर्ग के विनाशक शुद्ध वर्चुअल को परिभाषित करते हैं।

इसके बाद मैंने कुछ कोड से एक ठोस उदाहरण लिखा है। मेरे पास दो वर्ग हैं, न्यूमेरिक्स/भौतिकी पैराम जो आम गुण साझा करते हैं। इसलिए मैं उन्हें अमूर्त वर्ग आईपैम से प्राप्त करने देता हूं। इस मामले में, मेरे हाथ में बिल्कुल कोई विधि नहीं थी जो पूरी तरह आभासी हो सकती है। सेट पैरामीटर विधि, उदाहरण के लिए, प्रत्येक उपclass के लिए एक ही शरीर होना चाहिए। आईपीरम्स के विनाशक शुद्ध आभासी बनाने के लिए मेरे पास एकमात्र विकल्प था।

struct IParams 
{ 
    IParams(const ModelConfiguration& aModelConf); 
    virtual ~IParams() = 0; 

    void setParameter(const N_Configuration::Parameter& aParam); 

    std::map<std::string, std::string> m_Parameters; 
}; 

struct NumericsParams : IParams 
{ 
    NumericsParams(const ModelConfiguration& aNumericsConf); 
    virtual ~NumericsParams(); 

    double dt() const; 
    double ti() const; 
    double tf() const; 
}; 

struct PhysicsParams : IParams 
{ 
    PhysicsParams(const N_Configuration::ModelConfiguration& aPhysicsConf); 
    virtual ~PhysicsParams(); 

    double g()  const; 
    double rho_i() const; 
    double rho_w() const; 
}; 
+1

मुझे यह उपयोग पसंद है, लेकिन विरासत को "लागू करने" का एक और तरीका 'आईपीराम' के संरक्षक को संरक्षित करने के लिए घोषित करना है, जैसा कि किसी अन्य टिप्पणी में उल्लेख किया गया था। – rwols

+0

सच बात दोस्त। –

0

आप इन जवाब के साथ hypotheticals में हो रही है, तो मैं स्पष्टता की खातिर एक सरल, पृथ्वी स्पष्टीकरण के लिए नीचे अधिक बनाने के लिए कोशिश करेंगे।

वस्तु उन्मुख डिजाइन के बुनियादी रिश्तों दो हैं: IS-एक है और एक। मैंने उन्हें नहीं बनाया। यही वह है जिसे उन्हें बुलाया जाता है।

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

है-एक संकेत दिया है कि एक वस्तु एक समग्र वर्ग का हिस्सा है और एक स्वामित्व संबंध नहीं है। इसका अर्थ यह है कि सी ++ में यह एक सदस्य वस्तु है और इस तरह के मालिक स्वयं को नष्ट करने से पहले स्वामित्व को बंद करने के लिए स्वामित्व वाले वर्ग पर हैं।

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

वर्चुअल फ़ंक्शंस, सबसे पहले, रन-टाइम चीज हैं। यह बहुरूपता का हिस्सा है जिसमें इसका उपयोग यह तय करने के लिए किया जाता है कि उस समय चलने वाले कार्यक्रम में किस समारोह को चलाने के लिए चलाया जाता है।

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

एक फल वर्ग एक आभासी समारोह रंग() कि "कुछ नहीं" देता है डिफ़ॉल्ट रूप से हो सकता था। केला वर्ग रंग() फ़ंक्शन "पीला" या "ब्राउन" देता है।

लेकिन समारोह एक फल सूचक ले रही है यह करने के लिए भेजा केले वर्ग पर रंग() कॉल - जो रंग() फ़ंक्शन लागू हो जाता है? फ़ंक्शन सामान्य रूप से फल ऑब्जेक्ट के लिए Fruit :: color() को कॉल करेगा।

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

यह सबक्लास में किसी फ़ंक्शन को ओवरराइड करने से अलग है। उस मामले में फल सूचक फलों :: रंग() को कॉल करेगा यदि यह सब जानता है कि यह फल के लिए एक सूचक है।

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

अब पहेली का अंतिम भाग: रचनाकार और विनाशक।

शुद्ध वर्चुअल कंस्ट्रक्टर अवैध, पूरी तरह से अवैध हैं। वह बस बाहर है।

लेकिन शुद्ध वर्चुअल विनाशक इस मामले में काम करते हैं कि आप बेस क्लास इंस्टेंस के निर्माण को रोकना चाहते हैं। बेस वर्ग के विनाशक शुद्ध वर्चुअल होने पर केवल उप वर्गों को तत्काल किया जा सकता है। सम्मेलन 0.

virtual ~Fruit() = 0; // pure virtual 
Fruit::~Fruit(){}  // destructor implementation 

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

तो आप इस मामले में फल के उदाहरण बनाने के लिए मना कर रहे हैं, लेकिन केले के उदाहरण बनाने की अनुमति दी।

केला के उदाहरण को इंगित करने वाले फल सूचक को हटाने के लिए कॉल केले :: ~ केले() पहले कॉल करेगा और फिर हमेशा Fuit :: ~ Fruit() को कॉल करेगा। कोई फर्क नहीं पड़ता कि, जब आप एक उप-वर्ग विनाशक कहते हैं, तो बेस क्लास विनाशक का पालन करना चाहिए।

क्या यह एक खराब मॉडल है? यह डिज़ाइन चरण में अधिक जटिल है, हां, लेकिन यह सुनिश्चित कर सकता है कि रन-टाइम पर सही लिंकिंग किया जाता है और एक उप-वर्ग कार्य निष्पादित किया जाता है जहां अस्पष्टता है कि वास्तव में किस सबक्लास का उपयोग किया जा रहा है।

यदि आप सी ++ लिखते हैं ताकि आप केवल सामान्य क्लाइंट पॉइंटर्स के पास पास न हों, न कि कोई सामान्य और न ही अस्पष्ट पॉइंटर्स, तो वर्चुअल फ़ंक्शंस की वास्तव में आवश्यकता नहीं होती है। लेकिन यदि आपको प्रकार के रन-टाइम लचीलापन की आवश्यकता होती है (जैसे ऐप्पल केले ऑरेंज ==> फल) फ़ंक्शन कम अनावश्यक कोड के साथ आसान और अधिक बहुमुखी हो जाते हैं। आपको अब प्रत्येक प्रकार के फल के लिए कोई फ़ंक्शन लिखना नहीं है, और आप जानते हैं कि प्रत्येक फल रंग() को अपने स्वयं के सही कार्य के साथ प्रतिसाद देगा।

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

0

हो सकता है कि वहाँ शुद्ध आभासी नाशक जो मैं वास्तव में अन्य उत्तर :)

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

के लिए बुला रहा है

पहले सोच भी नहीं है इस:

class Printable { 
    virtual void print() const = 0; 
    // virtual destructor should be here, but not to confuse with another problem 
}; 

और कुछ की तरह:

class Printer { 
    void queDocument(unique_ptr<Printable> doc); 
    void printAll(); 
}; 

सीधे शब्दों में - हम इंटरफ़ेस Printable और कुछ "कंटेनर "इस इंटरफ़ेस के साथ कुछ भी पकड़ो। मुझे लगता है कि यह बिल्कुल स्पष्ट है कि क्यों print() विधि शुद्ध वर्चुअल है। इसमें कुछ शरीर हो सकता है लेकिन यदि कोई डिफ़ॉल्ट कार्यान्वयन नहीं होता है, तो शुद्ध वर्चुअल एक आदर्श "कार्यान्वयन" (= "एक वंश वर्ग द्वारा प्रदान किया जाना चाहिए")।

और अब यह बिल्कुल समान सोच भी मुद्रण के लिए लेकिन विनाश के लिए नहीं है:

class Destroyable { 
    virtual ~Destroyable() = 0; 
}; 

और यह भी एक ऐसी ही समान कंटेनर हो सकता है:

class PostponedDestructor { 
    // Queues an object to be destroyed later. 
    void queObjectForDestruction(unique_ptr<Destroyable> obj); 
    // Destroys all already queued objects. 
    void destroyAll(); 
}; 

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

मुझे लगता है कि यह कोई बड़ा विचार नहीं है, केवल अधिक स्पष्टीकरण कि शुद्ध गुण वास्तव में समान रूप से समान रूप से काम करता है - विनाशकों के लिए भी।

0

यह एक दशक पुराना विषय है :) विवरण के लिए "प्रभावी सी ++" पुस्तक पर आइटम # 7 के अंतिम 5 पैराग्राफ पढ़ें, "कभी-कभी कक्षा को शुद्ध वर्चुअल विनाशक देने के लिए सुविधाजनक हो सकता है .... "

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