2012-01-06 15 views
29
#include <iostream> 
using namespace std; 

class CPolygon { 
    protected: 
    int width, height; 
    public: 
    virtual int area() 
     { return (0); } 
    }; 

class CRectangle: public CPolygon { 
    public: 
    int area() { return (width * height); } 
    }; 

संकलन चेतावनी हैसी ++ संकलन के दौरान 'वर्चुअल विधि ... लेकिन गैर-वर्चुअल विनाशक' चेतावनी का क्या अर्थ है?

Class '[[email protected]' has virtual method 'area' but non-virtual destructor 

कैसे इस चेतावनी को समझने के लिए और कैसे कोड में सुधार करने के?

[संपादित करें] क्या यह संस्करण अभी सही है? (अपने आप अवधारणा के साथ स्पष्ट करना जवाब देने के लिए कोशिश कर रहा है)

#include <iostream> 
using namespace std; 

class CPolygon { 
    protected: 
    int width, height; 
    public: 
    virtual ~CPolygon(){}; 
    virtual int area() 
     { return (0); } 
    }; 

class CRectangle: public CPolygon { 
    public: 
    int area() { return (width * height); } 
    ~CRectangle(){} 
    }; 
+0

हां, नया संस्करण सही है। हालांकि इसे व्युत्पन्न कक्षाओं में कार्यों को फिर से घोषित करने के लिए अच्छा रूप माना जाता है, भले ही यह आवश्यक नहीं है। ऐसा इसलिए है कि जो लोग व्युत्पन्न कक्षा को देखना चाहते हैं वे अभी भी जानते हैं कि कार्य आभासी हैं। – Omnifarious

+0

आपका मतलब है 'वर्ग सीआरएक्सएंगल: सार्वजनिक CPolygon { सार्वजनिक: आभासी int क्षेत्र() {वापसी (चौड़ाई * ऊंचाई); } }; '? – qazwsx

+0

हां। और 'आभासी ~ CRectangle() {} 'साथ ही। जैसा कि मैंने कहा, यह बताते हुए कि ये फ़ंक्शंस वर्चुअल हैं, यह एक अच्छा तरीका है, किसी भी तरह से भाषा द्वारा इसकी आवश्यकता नहीं है। – Omnifarious

उत्तर

64

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

इस प्रश्न का उत्तर गहराई से here में भी दिया गया है। निम्नलिखित पूर्ण उदाहरण कार्यक्रम है कि प्रभाव को दर्शाता है है:

#include <iostream> 

class FooBase { 
public: 
    ~FooBase() { std::cout << "Destructor of FooBase" << std::endl; } 
}; 

class Foo : public FooBase { 
public: 
    ~Foo() { std::cout << "Destructor of Foo" << std::endl; } 
}; 

class BarBase { 
public: 
    virtual ~BarBase() { std::cout << "Destructor of BarBase" << std::endl; } 
}; 

class Bar : public BarBase { 
public: 
    ~Bar() { std::cout << "Destructor of Bar" << std::endl; } 
}; 

int main() { 
    FooBase * foo = new Foo; 
    delete foo; // deletes only FooBase-part of Foo-object; 

    BarBase * bar = new Bar; 
    delete bar; // deletes complete object 
} 

आउटपुट:

Destructor of FooBase 
Destructor of Bar 
Destructor of BarBase 

ध्यान दें कि delete bar; कारणों दोनों विनाशकर्ता, ~Bar और ~BarBase,, के नाम से जाना है, जबकि delete foo; केवल ~FooBase कहता है। उत्तरार्द्ध भी undefined behavior है, इसलिए प्रभाव की गारंटी नहीं है।

+0

यह उत्तर स्लाइसिंग के दुष्प्रभावों का प्रदर्शन करने वाले उदाहरण के साथ काफी सुधार हुआ होगा। जब यह हो तो यह मेरे ऊपर से एक उथल-पुथल प्राप्त करेगा। – Omnifarious

+1

@ ओमनिफरीस: मैंने एक उदाहरण जोड़ा। –

+0

बस स्पष्ट होने के लिए: 'foo हटाएं' अपरिभाषित व्यवहार का आह्वान करता है, यह केवल '~ FooBase 'चलाने की गारंटी नहीं है। – Mankarse

12

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

struct Foo { 
    virtual ~Foo() {} 
    virtual void bar() = 0; 
}; 

यह छोड़कर बंद , अपरिभाषित व्यवहार हो सकता है आम तौर पर valgrind जैसे उपकरणों में एक स्मृति रिसाव के रूप में दिखाई देता है।

+0

यदि आप बेस-क्लास संदर्भ या पॉइंटर के माध्यम से उप-वर्ग ऑब्जेक्ट हटाते हैं तो यह केवल यूबी है। –

+3

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

+0

जो मैंने सुना है, उससे मुख्य रूप से समस्याएं होती हैं यदि आप (या कोई) उस संरचना को समाप्त करने का निर्णय लेता है। –

1

यह केवल इसका मतलब है कि

CPolygon* p = new CRectangle; 
delete p; 

... या जो कुछ भी जो कुछ स्मार्ट सूचक में रैपिंग की तरह एक कोड, अनिवार्य रूप से ठीक से व्यवहार नहीं करेगा क्योंकि CPolygon विलोपन पर बहुरूपी नहीं है, और CRectange हिस्सा नहीं होंगे ठीक से नष्ट कर दिया।

यदि आप CRectangle और CPolygon polymorphicaly को हटाने नहीं जा रहे हैं, तो यह चेतावनी सार्थक नहीं है।

+2

लेकिन यदि आप CRectangle और CPolygon polymorphicaly को हटाने नहीं जा रहे हैं, तो बेस क्लास विनाशक को संकलन समय पर इसे लागू करने के लिए संरक्षित किया जाना चाहिए। –

+0

@ मार्कब: जरूरी नहीं: यदि सीपीओलिगॉन सार नहीं है (मुझे ओपी अमूर्तता कितनी गहरी नहीं है), सीआरईटी और सीपीओलिगॉन दोनों स्टैक के सही कानूनी नागरिक हो सकते हैं, संदर्भों के माध्यम से सीपीओलिगॉन एल्गोरिदम में भाग लेते हैं। क्षेत्र को बहुरूप रूप से गणना करने की आवश्यकता है, लेकिन कोई बहुलक विनाश की आवश्यकता नहीं है। और CPolygon खुद को विनाशकारी (कोई संरक्षित विनाशक) होने की आवश्यकता है। एक ऐसी कक्षा को प्राप्त करना जिसमें वर्चुअल विनाशक नहीं है वह कक्षा को प्राप्त करने से अलग नहीं है जिसमें सभी विधियां वर्चुअल नहीं हैं। बस वर्चुअल रूप से व्यवहार करने के लिए आभासी नहीं है उम्मीद नहीं है। –

+0

@EmilioGaravaglia: आमतौर पर एक ठोस वर्ग से प्राप्त होने से बचने के लिए अनुशंसा की जाती है। विभिन्न प्रकार के विभिन्न गानों में अनजाने टुकड़े करने के लिए यह बहुत खुला है। –

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