2012-05-07 16 views
18

से प्रवेश करना मैं std::map से प्राप्त करना चाहता हूं, लेकिन जहां तक ​​मुझे पता है कि std::map कोई वर्चुअल विनाशक नहीं है।सी ++: std :: नक्शा

क्या उचित वस्तु विनाश सुनिश्चित करने के लिए मेरे विनाशक में स्पष्ट रूप से std::map के विनाशक को कॉल करना संभव है?

उत्तर

28

विनाशक को बुलाया जाता है, भले ही यह आभासी न हो, लेकिन यह मुद्दा नहीं है।

यदि आप std::map पर पॉइंटर के माध्यम से अपने प्रकार की ऑब्जेक्ट को हटाने का प्रयास करते हैं तो आपको अपरिभाषित व्यवहार मिलता है।

विरासत की बजाय संरचना का उपयोग करें, std कंटेनर विरासत में नहीं हैं, और आपको नहीं करना चाहिए।

मैं तुम्हें std::map की कार्यक्षमता का विस्तार करना चाहते संभालने कर रहा हूँ (कहते हैं कि तुम न्यूनतम मूल्य को खोजने के लिए चाहते हैं), जिस स्थिति में आप दो कहीं बेहतर है, और कानूनी, विकल्प हैं:

1) के रूप में सुझाव रचना, आप उपयोग कर सकते हैं बजाय:

template<class K, class V> 
class MyMap 
{ 
    std::map<K,V> m; 
    //wrapper methods 
    V getMin(); 
}; 

2) नि: शुल्क कार्य:

namespace MapFunctionality 
{ 
    template<class K, class V> 
    V getMin(const std::map<K,V> m); 
} 
+9

+1 हमेशा विरासत की बजाय संरचना का पक्ष लें। अभी भी इच्छा है कि लपेटने के लिए आवश्यक सभी बॉयलरप्लेट कोड को कम करने का कोई तरीका था। – daramarak

+0

@ दशमराक: तो क्या मैं, अगर कुछ विशेषता 'asribute.insert' का उपयोग कर सकता हूं, तो काम कर सकता है! दूसरी तरफ, यह बहुत दुर्लभ है कि आपको वास्तव में सभी विधियों की आवश्यकता है, और रैपिंग अर्थपूर्ण नाम देने और उच्च स्तरीय प्रकार लेने का मौका देता है :) –

+3

@ दशमराक: * फिर भी इच्छा है कि सभी बॉयलरप्लेट कोड को कम करने का कोई तरीका था लपेटने के लिए आवश्यक *: हाँ, वहाँ है: विरासत। लेकिन प्रोग्रामर स्वयं को आश्वस्त हैं कि उन्हें इसका उपयोग नहीं करना चाहिए ... क्योंकि वे हमेशा इसे "एक" के रूप में समझते हैं। लेकिन यह एक आवश्यकता नहीं है, सिर्फ एक सार्वजनिक विश्वास है। –

13

मैं std::map से प्राप्त करना चाहते [...]

क्यों?

वहाँ दो पारंपरिक कारणों के वारिस हैं:

  • इसके इंटरफेस पुन: उपयोग करने (और इस प्रकार, इसके खिलाफ कोडित विधि)
  • अपने व्यवहार पुन: उपयोग करने

पूर्व कोई मतलब नहीं यहाँ बनाता है map में कोई virtual विधि नहीं है, इसलिए आप विरासत में अपने व्यवहार को संशोधित नहीं कर सकते; और उत्तरार्द्ध विरासत के उपयोग का एक विकृति है जो केवल अंत में रखरखाव को जटिल बनाता है।


अपने इच्छित उपयोग (अपने प्रश्न में संदर्भ की कमी) की स्पष्ट जानकारी के बिना

, मुझे लगता है करेगा कि क्या आप वास्तव में चाहते हैं कुछ बोनस के संचालन के साथ एक नक्शा की तरह कंटेनर प्रदान करने के लिए, है।

  • रचना:: वहाँ इस लक्ष्य को हासिल करने के लिए दो तरीके हैं आप एक नया वस्तु है, जो एक std::map शामिल बनाते हैं, और पर्याप्त इंटरफ़ेस
  • विस्तार प्रदान करते हैं: आप नए मुक्त कार्यों कि std::map
  • पर काम बनाने

उत्तरार्द्ध सरल है, हालांकि यह भी अधिक खुला है: std::map का मूल इंटरफ़ेस अभी भी चौड़ा है; इसलिए यह संचालन को प्रतिबंधित करने के लिए अनुपयुक्त है।

पूर्व में अधिक हेवीवेट है, निस्संदेह, लेकिन अधिक संभावनाएं प्रदान करता है।

यह तय करने के लिए आप दोनों दृष्टिकोणों में से कौन सा दृष्टिकोण अधिक उपयुक्त है।

12

एक गलत धारणा है: शुद्ध OOP की अवधारणा -outside विरासत, कि सी ++ नहीं है - "एक क्षय की क्षमता के साथ एक अनाम सदस्य के साथ रचना," एक से ज्यादा कुछ नहीं है।

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

Destructors स्पष्ट रूप से एक दूसरे से कहा जाने के बाद से उनके कॉल हमेशा विनिर्देश द्वारा श्रृंखलित है की जरूरत नहीं है।

#include <iostream> 
unsing namespace std; 

class A 
{ 
public: 
    A() { cout << "A::A()" << endl; } 
    ~A() { cout << "A::~A()" << endl; } 
    void hello() { cout << "A::hello()" << endl; } 
}; 

class B: public A 
{ 
public: 
    B() { cout << "B::B()" << endl; } 
    ~B() { cout << "B::~B()" << endl; } 
    void hello() { cout << "B::hello()" << endl; } 
}; 

int main() 
{ 
    B b; 
    b.hello(); 
    return 0; 
} 

इच्छा उत्पादन

A::A() 
B::B() 
B::hello() 
B::~B() 
A::~A() 

बनाना एक

class B 
{ 
public: 
    A a; 
    B() { cout << "B::B()" << endl; } 
    ~B() { cout << "B::~B()" << endl; } 
    void hello() { cout << "B::hello()" << endl; } 
}; 

के साथ बी में एम्बेड कि उत्पादन होगा बिल्कुल उसी।

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

बेशक

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

मेरे नमूने में ए :: ~ ए() वर्चुअल रूप से ए :: हैलो नहीं है। इसका क्या मतलब है?

सरल: इसी कारण A::hello बुला B::hello बुला का कारण नहीं बनेगा, A::~A() फोन करने के लिए (हटाने) के द्वारा B::~B() का कारण नहीं बनेगा। -इन आप शैली के पहले अभिकथन प्रोग्रामिंग आप स्वीकार कर सकते हैं, तो कोई कारण नहीं कि आप दूसरे स्वीकार नहीं कर सकते हैं। मेरी नमूने में कोई A* p = new B कि delete p एक के बाद प्राप्त होगा :: ~ एक आभासी नहीं है और मुझे पता है इसका क्या मतलब है।

वास्तव में है कि एक ही कारण है कि नहीं होगा, बी, A* p = &((new B)->a); एक delete p; साथ के लिए दूसरे उदाहरण का उपयोग करते हुए, हालांकि यह दूसरा मामला, पहले एक साथ पूरी तरह से दोहरे, कोई स्पष्ट कारणों के लिए दिलचस्प नहीं किसी को भी लग रहा है।

एकमात्र समस्या "रखरखाव" है, इस अर्थ में कि योपुर कोड एक ओओपी प्रोग्रामर द्वारा देखा जाता है- इसे अस्वीकार कर देगा, क्योंकि यह स्वयं में गलत नहीं है, लेकिन क्योंकि उसे ऐसा करने के लिए कहा गया है।

वास्तव में, "अगर नाशक आभासी नहीं है प्राप्त नहीं है" है, क्योंकि प्रोग्रामर के सबसे beleave भी कई प्रोग्रामर है कि पता नहीं है कि वे एक आधार के लिए एक सूचक पर हटाने के कॉल नहीं कर सकते देखते हैं कि। (माफ करना अगर यह विनम्र नहीं है, लेकिन प्रोग्रामिंग अनुभव के 30 साल के बाद मैं किसी अन्य कारण से नहीं देख सकते हैं!)

लेकिन आपके सवाल का अलग है: कॉलिंग बी

:: ~ बी() (हटाने या द्वारा स्कोप एंडिंग द्वारा) हमेशा ए :: ~ ए() के बाद ए (चाहे वह एम्बेडेड हो या विरासत में हो) परिणामस्वरूप किसी भी मामले में बी का हिस्सा होगा।


Luchian टिप्पणियों के बाद: अपरिभाषित व्यवहार ऊपर अपनी टिप्पणी में एक एक नहीं आभासी नाशक के साथ सूचक करने वाली एक-object's आधार पर एक हटाए जाने से संबंधित है alluded।

ओओपी स्कूल के अनुसार, यह परिणाम "नियम नहीं है यदि कोई आभासी विनाशक मौजूद नहीं है"।

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

मेरी स्थिति, बस, यह स्वीकार करती है कि सी ++ सिर्फ ओओपी नहीं है, और सभी सी ++ ऑब्जेक्ट्स डिफ़ॉल्ट रूप से ओओपी उन्मुख नहीं हैं, और, ओओपी स्वीकार करना हमेशा एक आवश्यक आवश्यकता नहीं है, यह भी स्वीकार करता है कि सी ++ विरासत हमेशा नहीं है जरूरी रूप से ओओपी प्रतिस्थापन के लिए सर्विसिंग।

std :: नक्शा polymorphic नहीं है इसलिए यह बदलने योग्य नहीं है। माईमैप वही है: पॉलिमॉर्फिक नहीं और प्रतिस्थापन योग्य नहीं।

इसे बस std :: map का पुन: उपयोग करना होगा और उसी std :: मानचित्र इंटरफ़ेस का पर्दाफाश करना होगा। और विरासत सिर्फ पुनर्लेखित कार्यों के लंबे बॉयलरप्लेट से बचने का तरीका है जो केवल पुन: उपयोग किए गए लोगों को कॉल करता है।

MyMap में वर्चुअल dtor नहीं होगा क्योंकि std :: मानचित्र में कोई नहीं है। और यह मेरे लिए- एक सी ++ प्रोग्रामर को बताने के लिए पर्याप्त है कि ये बहुलक वस्तुएं नहीं हैं और इसका उपयोग दूसरे के स्थान पर नहीं किया जाना चाहिए।

मुझे यह मानना ​​है कि आज इस स्थिति को अधिकांश सी ++ विशेषज्ञों द्वारा साझा नहीं किया गया है। लेकिन मुझे लगता है (मेरी एकमात्र व्यक्तिगत राय) यह केवल उनके इतिहास की वजह से है, जो ओओपी से सेवा करने के लिए एक सिद्धांत के रूप में संबंधित है, सी ++ की आवश्यकता के कारण नहीं। मेरे लिए सी ++ एक शुद्ध ओओपी भाषा नहीं है और अनिवार्य रूप से हमेशा ओओपी प्रतिमान का पालन नहीं करना चाहिए, एक संदर्भ में जहां ओओपी का पालन नहीं किया जाता है या आवश्यक है।

+3

आप वहां कुछ खतरनाक वक्तव्य कर रहे हैं। अप्रचलित के रूप में आभासी विनाशक की आवश्यकता पर ध्यान न दें। मानक ** स्पष्ट रूप से बताता है कि मैंने जो परिस्थिति में उल्लेख किया है, उसमें अनिर्धारित व्यवहार उत्पन्न होता है। एब्स्ट्रक्शन ओओपी का एक बड़ा हिस्सा है। इसका मतलब है कि आप न केवल पुन: उपयोग करने के लिए प्राप्त करते हैं, बल्कि वास्तविक प्रकार को छिपाने के लिए भी प्राप्त करते हैं। मतलब, एक अच्छे डिजाइन में, यदि आप विरासत का उपयोग करते हैं, तो आप 'std :: map *' के साथ समाप्त हो जाएंगे जो वास्तव में 'MyMap' को इंगित करता है। और यदि आप इसे हटा देते हैं, तो कुछ भी हो सकता है, जिसमें क्रैश भी शामिल है। –

+4

@ लचियनग्रिगोर: * मानक स्पष्ट रूप से बताता है कि मैंने जो परिस्थिति में उल्लेख किया है, वह अनिश्चित व्यवहार उत्पन्न होता है। *। सच है, लेकिन यह मेरी स्थिति का उल्लेख नहीं है, न कि ओपी में है। * मतलब, एक अच्छी डिजाइन में, यदि आप विरासत का उपयोग करते हैं, तो आप std :: map * के साथ समाप्त हो जाएंगे जो वास्तव में इंगित करता है MyMap *: यह सामान्य गलत है, और केवल शुद्ध सूचक आधारित ओओपी के साथ सच है। यह सटीक है कि मेरे नमूने क्या नहीं हैं। आप मेरे नमूनों के अस्तित्व की व्याख्या कैसे करते हैं, जो बहुरूपता और पॉइंटर्स का उपयोग नहीं करते हैं? –

+2

@LuchianGrigore: वैसे भी, मुझे लगता है कि आप * सही * हैं: जो मैं जोर दे रहा हूं वह खतरनाक है, लेकिन कार्यक्रम की शुद्धता के लिए नहीं, बल्कि ओओपी प्रोग्रामिंग आधारित संस्कृति के लिए! लेकिन चिंता न करें: आपकी प्रतिक्रिया की उम्मीद थी! –

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