आप एसटीडी :: वेक्टर से विरासत में नहीं मिलेंगे


189

ठीक है, यह कबूल करना वास्तव में मुश्किल है, लेकिन मुझे इस समय से विरासत में एक मजबूत प्रलोभन है std::vector

मुझे वेक्टर के लिए लगभग 10 अनुकूलित एल्गोरिदम चाहिए और मैं चाहता हूं कि वे सीधे वेक्टर के सदस्य हों। लेकिन स्वाभाविक रूप से मैं भी बाकी std::vectorइंटरफ़ेस चाहता हूं । ठीक है, मेरा पहला विचार, कानून का पालन करने वाले नागरिक के रूप में, कक्षा std::vectorमें एक सदस्य होना था MyVector। लेकिन तब मुझे मैन्युअल रूप से सभी std :: वेक्टर के इंटरफ़ेस को फिर से बनाना होगा। टाइप करने के लिए बहुत ज्यादा। इसके बाद, मैंने निजी विरासत के बारे में सोचा, ताकि मैं रीप्रोविंग के तरीकों के बजाय using std::vector::memberसार्वजनिक खंड में एक गुच्छा लिखूं । यह वास्तव में थकाऊ है।

और यहाँ मैं कर रहा हूँ, मुझे वास्तव में लगता है कि मैं केवल सार्वजनिक रूप से विरासत में प्राप्त कर सकता हूं std::vector, लेकिन प्रलेखन में एक चेतावनी प्रदान करता हूं कि इस वर्ग का उपयोग पॉलीमॉर्फिक रूप से नहीं किया जाना चाहिए। मुझे लगता है कि अधिकांश डेवलपर्स यह समझने के लिए पर्याप्त रूप से सक्षम हैं कि इसका उपयोग किसी भी तरह से बहुरूपिक रूप से नहीं किया जाना चाहिए।

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

इसके अलावा, इस तथ्य के अलावा कि कुछ बेवकूफ कुछ लिख सकते हैं

std::vector<int>* p  = new MyVector

वहाँ MyVector का उपयोग करने में कोई अन्य यथार्थवादी संकट है? यथार्थवादी कहकर मैं एक समारोह की कल्पना करता हूं जो वेक्टर के लिए एक पॉइंटर लेता है ...

खैर, मैंने अपना मामला बताया है। मैंने पाप किया है। अब यह आप पर है कि मुझे माफ करें या नहीं :)


9
तो, आप मूल रूप से पूछ रहे हैं कि क्या इस तथ्य के आधार पर एक सामान्य नियम का उल्लंघन करना ठीक है कि आप कंटेनर के इंटरफ़ेस को फिर से लागू करने के लिए बहुत आलसी हैं? तब नहीं, यह नहीं है। देखें, आपके पास दोनों दुनिया के सर्वश्रेष्ठ हो सकते हैं यदि आप उस कड़वी गोली को निगलते हैं और ठीक से करते हैं। उस आदमी मत बनो। मजबूत कोड लिखें।
जिम ब्रिसॉम

7
आप गैर-सदस्य फ़ंक्शंस के साथ अपनी कार्यक्षमता को जोड़ना / जोड़ना क्यों नहीं चाह सकते? मेरे लिए, इस परिदृश्य में ऐसा करना सबसे सुरक्षित काम होगा।
सिमोन

11
@Jim: std::vectorका इंटरफ़ेस काफी विशाल है, और जब C ++ 1x साथ आता है, तो यह बहुत विस्तार करेगा। टाइप करने के लिए बहुत कुछ है और कुछ वर्षों में विस्तार करना है। मुझे लगता है कि यह एक अच्छा कारण है कि हम नियमन के बजाय विरासत पर विचार करें - यदि कोई इस आधार का पालन करता है कि उन कार्यों को सदस्य होना चाहिए (जो मुझे संदेह है)। एसटीएल कंटेनरों से नहीं निकलने का नियम यह है कि वे बहुरूपिए नहीं हैं। यदि आप उन्हें इस तरह से उपयोग नहीं कर रहे हैं, तो यह लागू नहीं होता है।
sbi

9
प्रश्न का वास्तविक मांस एक वाक्य में है: "मैं चाहता हूं कि वे सीधे वेक्टर के सदस्य बनें"। सवाल में और कुछ नहीं वास्तव में मायने रखता है। आप इसे "क्यों" चाहते हैं? गैर-सदस्यों के रूप में यह कार्यक्षमता प्रदान करने में क्या समस्या है?
:

8
@ जोश: "तू तू" हमेशा "तू" की तुलना में अधिक सामान्य रहा है, और यह किंग जेम्स बाइबिल में पाया गया संस्करण भी है (जो आमतौर पर लोग लिखते हैं कि जब आप "तू नहीं होगा []] ")। पृथ्वी पर क्या आप इसे "गलत वर्तनी" कहने के लिए नेतृत्व करेंगे?
रक्ख

जवाबों:


155

वास्तव में, सार्वजनिक विरासत के साथ कुछ भी गलत नहीं है std::vector। यदि आपको इसकी आवश्यकता है, तो बस यही करें।

मैं सुझाव दूंगा कि यदि यह वास्तव में आवश्यक है तो ही करें । केवल तभी यदि आप वह नहीं कर सकते जो आप नि: शुल्क कार्यों के साथ चाहते हैं (जैसे कि कुछ राज्य रखना चाहिए)।

समस्या यह है कि MyVectorएक नई इकाई है। इसका मतलब है कि एक नया C ++ डेवलपर को पता होना चाहिए कि इसका उपयोग करने से पहले यह क्या है। बीच क्या अंतर है std::vectorऔर MyVector? यहां और वहां उपयोग करने के लिए कौन सा बेहतर है? अगर मुझे स्थानांतरित std::vectorकरने की आवश्यकता है तो क्या होगा MyVector? क्या मैं अभी उपयोग swap()करूं या नहीं?

केवल बेहतर दिखने के लिए कुछ बनाने के लिए नई संस्थाओं का उत्पादन न करें। ये संस्थाएँ (विशेष रूप से, ऐसी आम) निर्वात में नहीं रहने वाली हैं। वे लगातार बढ़े हुए एंट्रोपी के साथ मिश्रित वातावरण में रहेंगे।


7
मेरा एकमात्र प्रतिवाद यह है कि व्यक्ति को वास्तव में पता होना चाहिए कि वह ऐसा करने के लिए क्या कर रहा है। उदाहरण के लिए, नहीं है में अतिरिक्त डेटा के सदस्यों का परिचय MyVectorऔर फिर कार्यों कि स्वीकार करने के लिए इसे पारित करने की कोशिश std::vector&या std::vector*। अगर std :: वेक्टर * या std :: वेक्टर & का उपयोग करके किसी भी प्रकार का कॉपी असाइनमेंट शामिल है, तो हमारे पास ऐसे स्लाइसिंग मुद्दे हैं जहां नए डेटा सदस्यों की MyVectorप्रतिलिपि नहीं बनाई जाएगी। आधार सूचक / संदर्भ के माध्यम से स्वैप कॉल करने के बारे में भी यही सच होगा। मैं किसी भी तरह की विरासत पदानुक्रम के बारे में सोचता हूं कि ऑब्जेक्ट स्लाइसिंग एक बुरा है।
stinky472

13
std::vectorविध्वंसक नहीं है virtual, इसलिए आपको उससे कभी विरासत में नहीं लेना चाहिए
आंद्रे फ्रेटेली

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

3
@GrahamAsher यदि आप किसी ऑब्जेक्ट को पॉइंटर से बेस तक डिलीट करते हैं, और डिस्ट्रक्टर वर्चुअल नहीं है, तो आपका प्रोग्राम अपरिभाषित व्यवहार प्रदर्शित कर रहा है। अपरिभाषित व्यवहार का एक संभावित परिणाम है "यह मेरे परीक्षणों में ठीक काम करता है"। एक और बात यह है कि यह आपकी दादी माँ को आपके वेब ब्राउज़िंग इतिहास को ईमेल करता है। दोनों सी ++ मानक के अनुरूप हैं। यह संकलक, ओएस या चंद्रमा के चरण के बिंदु विमोचन के साथ एक से दूसरे में बदल रहा है।
यक्क - एडम नेवरामॉन्ट

2
@GrahamAsher नहीं, जब भी आप किसी ऑब्जेक्ट को बिना किसी वर्चुअल डिस्ट्रक्टर के आधार पर पॉइंटर के माध्यम से डिलीट करते हैं, तो यह मानक के तहत अपरिभाषित व्यवहार है। मैं समझता हूं कि आप क्या सोचते हैं; आप सिर्फ गलत होते हैं। "बेस क्लास डिस्ट्रक्टर को कहा जाता है, और यह काम करता है" इस अपरिभाषित व्यवहार का एक संभावित लक्षण (और सबसे आम) है, क्योंकि यह भोली मशीन कोड है जो आमतौर पर कंपाइलर उत्पन्न करता है। यह इसे सुरक्षित नहीं करता है और न ही एक महान विचार है।
यक्क - एडम नेवरामॉन्ट

92

पूरे एसटीएल को इस तरह से डिजाइन किया गया था कि एल्गोरिदम और कंटेनर अलग-अलग हैं

इसने विभिन्न प्रकार के पुनरावृत्तियों की एक अवधारणा को जन्म दिया: कांस्ट इटएटर, रैंडम एक्सेस इटरेटर्स, आदि।

इसलिए मैं आपको इस सम्मेलन को स्वीकार करने और अपने एल्गोरिदम को इस तरह से डिजाइन करने की सलाह देता हूं कि वे इस बात की परवाह नहीं करेंगे कि वे कौन से कंटेनर पर काम कर रहे हैं - और उन्हें केवल एक विशेष प्रकार के इटेरेटर की आवश्यकता होगी, जिसे उन्हें प्रदर्शन करने की आवश्यकता होगी संचालन।

इसके अलावा, मुझे आपको जेफ एटवुड की कुछ अच्छी टिप्पणियों पर पुनर्निर्देशित करना चाहिए ।


63

std::vectorसार्वजनिक रूप से विरासत में नहीं मिलने का मुख्य कारण एक आभासी विध्वंसक की अनुपस्थिति है जो आपको वंशजों के बहुरूपिक उपयोग से प्रभावी रूप से रोकता है। विशेष रूप से, आप कर रहे हैं अनुमति नहीं करने के लिए deleteएक std::vector<T>*एक व्युत्पन्न वस्तु (भले ही व्युत्पन्न वर्ग में कोई सदस्य नहीं कहते हैं) में है कि वास्तव में अंक, फिर भी संकलक आम तौर पर इसके बारे में आपको चेतावनी नहीं कर सकते।

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

class AdVector: private std::vector<double>
{
    typedef double T;
    typedef std::vector<double> vector;
public:
    using vector::push_back;
    using vector::operator[];
    using vector::begin;
    using vector::end;
    AdVector operator*(const AdVector & ) const;
    AdVector operator+(const AdVector & ) const;
    AdVector();
    virtual ~AdVector();
};

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


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

2
vectorvectorविध्वंसक भंडारण मुद्दा नहीं है - आखिरकार, विध्वंसक को एक पॉइंटर के माध्यम से सभी अधिकार कहा जाएगा vector। यह सिर्फ इतना है कि मानक मना करता है deleteing मुक्त दुकान वस्तुओं एक आधार वर्ग अभिव्यक्ति के माध्यम से। यह निश्चित रूप से है कि (डी) आवंटन तंत्र मेमोरी चंक के आकार को अनुमान से मुक्त करने की कोशिश कर सकता है delete, उदाहरण के लिए, जब कुछ आकारों की वस्तुओं के लिए कई आवंटन एरेनास होते हैं। यह प्रतिबंध स्थैतिक या स्वचालित भंडारण अवधि के साथ वस्तुओं के सामान्य विनाश पर लागू नहीं होता है।
पीटर -

@DavisHerring मुझे लगता है कि हम वहां सहमत हैं :-)।
पीटर -

@DavisHerring आह, मैं देखता हूं, आप मेरी पहली टिप्पणी देखें - उस टिप्पणी में एक IIUC था, और यह एक प्रश्न में समाप्त हो गया; मैंने बाद में देखा कि वास्तव में यह हमेशा निषिद्ध है। (बेसिलव्स ने एक सामान्य बयान दिया था, "प्रभावी रूप से रोकता है", और मुझे आश्चर्य हुआ कि जिस विशिष्ट तरीके से यह रोकता है उसके बारे में।) तो हाँ, हम सहमत हैं: यूबी।
पीटर -

@ बासीलेव अनजाने में रहा होगा। फिक्स्ड।
थॉमसमैकलॉड

36

यदि आप इस पर विचार कर रहे हैं, तो आप स्पष्ट रूप से अपने कार्यालय में पहले से ही भाषा के बच्चों को मार सकते हैं। उनके साथ रास्ते से हटकर, सिर्फ क्यों नहीं

struct MyVector
{
   std::vector<Thingy> v;  // public!
   void func1( ... ) ; // and so on
}

यह सभी संभावित भूलों को रोक देगा, जो गलती से आपके MyVector वर्ग को बढ़ा सकते हैं, और आप अभी भी थोड़ा जोड़कर सभी वेक्टर ऑप्स का उपयोग कर सकते हैं .v


और कंटेनरों और एल्गोरिदम को उजागर करना? ऊपर कोस का उत्तर देखें।
ब्रूनो नेरी


19

क्या आप पूरा करने की उम्मीद कर रहे हैं? बस कुछ कार्यक्षमता प्रदान करना?

यह करने के लिए C ++ मुहावरेदार तरीका सिर्फ कुछ मुफ्त कार्यों को लिखना है जो कार्यक्षमता को लागू करते हैं। संभावना है कि आपको वास्तव में एक std :: वेक्टर की आवश्यकता नहीं है, विशेष रूप से आपके द्वारा कार्यान्वित की जाने वाली कार्यक्षमता के लिए, जिसका अर्थ है कि आप वास्तव में std से प्राप्त करने का प्रयास करके पुन: प्रयोज्यता खो रहे हैं :: वेक्टर।

मैं आपको दृढ़ता से मानक पुस्तकालय और हेडर देखने की सलाह दूंगा, और ध्यान दें कि वे कैसे काम करते हैं।


5
मैं आश्वस्त नहीं हूं। क्या आप यह समझाने के लिए प्रस्तावित कोड में से कुछ के साथ अपडेट कर सकते हैं?
कार्ल कंचल

6
@Armen: सौंदर्यशास्त्र के अलावा, क्या कोई अच्छे कारण हैं?
स्नोमार्क

12
@Armen: बेहतर सौंदर्यशास्त्र, और अधिक से अधिक उदारता, मुफ्त frontऔर backकार्य भी प्रदान करना होगा । :) (मुफ्त के उदाहरण पर भी विचार करेंbegin और endC ++ 0x और बढ़ावा देने ।)
अंकल ने

3
मैं अभी भी मुक्त कार्यों के साथ क्या गलत नहीं है। यदि आप एसटीएल के "सौंदर्यशास्त्र" को पसंद नहीं करते हैं, तो शायद सी ++ आपके लिए सौंदर्यवादी रूप से गलत जगह है। और कुछ सदस्य कार्यों को जोड़ने से यह ठीक नहीं होगा, क्योंकि अन्य एल्गोरिदम अभी भी मुफ्त कार्य कर रहे हैं।
फ्रैंक ऑस्टरफेल्ड

17
बाहरी एल्गोरिथ्म में भारी ऑपरेशन के परिणामस्वरूप कैश करना मुश्किल है। मान लीजिए कि आपको वेक्टर में सभी तत्वों की राशि की गणना करनी है या गुणांक के रूप में वेक्टर तत्वों के साथ एक बहुपद समीकरण को हल करना है। वे ऑपरेशन भारी हैं और आलस्य उनके लिए उपयोगी होगा। लेकिन आप इसे कंटेनर से लपेटे या विरासत में लिए बिना प्रस्तुत नहीं कर सकते।
बसिलेव्स

14

मुझे लगता है कि बहुत कम नियमों का 100% समय के बाद आँख बंद करके पालन किया जाना चाहिए। ऐसा लगता है कि आपने इसे बहुत सोचा है, और आश्वस्त हैं कि यह रास्ता तय करना है। इसलिए - जब तक कोई व्यक्ति ऐसा नहीं करने के लिए अच्छे विशिष्ट कारणों के साथ आता है - मुझे लगता है कि आपको अपनी योजना के साथ आगे बढ़ना चाहिए।


9
आपका पहला वाक्य 100% सत्य है। :)
स्टीव फालोअर्स

5
दुर्भाग्य से, दूसरा वाक्य नहीं है। उसने बहुत सोचा नहीं है। अधिकांश प्रश्न अप्रासंगिक है। इसका एकमात्र हिस्सा जो उनकी प्रेरणा दर्शाता है, "मैं चाहता हूं कि वे सीधे वेक्टर के सदस्य हों"। मुझे चाहिए। यह वांछनीय क्यों है इसका कोई कारण नहीं । ऐसा लगता है जैसे उसने यह बिल्कुल नहीं सोचा है
जालफ

7

std::vectorजब तक कोई एक वर्ग बनाने की इच्छा नहीं करता है , तब से विरासत में लेने का कोई कारण नहीं है std::vector, क्योंकि यह अपने तरीके से अपने तरीके से छिपी हुई जानकारी को संभालता है std::vector, या जब तक कि किसी के पास इस तरह के वर्ग की वस्तुओं का उपयोग करने के लिए वैचारिक कारण नहीं हैं। std::vectorवाले हैं। हालांकि, C ++ पर मानक के निर्माता std::vectorकिसी भी इंटरफ़ेस (संरक्षित सदस्यों के रूप में) के साथ प्रदान नहीं करते थे कि इस तरह के विरासत वाले वर्ग एक विशिष्ट तरीके से वेक्टर को बेहतर बनाने के लिए लाभ ले सकते हैं। दरअसल, उनके पास कोई सोचने का कोई तरीका नहीं था विशिष्ट पहलू के जिसे विस्तार या फाइन-ट्यून अतिरिक्त कार्यान्वयन की आवश्यकता हो सकती है, इसलिए उन्हें किसी भी उद्देश्य के लिए ऐसा कोई इंटरफ़ेस प्रदान करने के बारे में सोचने की आवश्यकता नहीं थी।

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


बहुत बढ़िया जवाब। एसओ में आपका स्वागत है!
अर्मेन त्सिरुन्यान

4

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

BUT थ्योरी में: [expr.delete] से C ++ 0x FCD में: पहले विकल्प में (डिलीट ऑब्जेक्ट), यदि डिलीट की जाने वाली ऑब्जेक्ट का स्टैटिक टाइप उसके डायनेमिक टाइप से अलग है, तो स्टैटिक टाइप ए होगा ऑब्जेक्ट के डायनेमिक प्रकार का बेस क्लास डिलीट किया जाएगा और स्टैटिक टाइप में वर्चुअल डिस्ट्रक्टर होगा या व्यवहार अपरिभाषित है।

लेकिन आप निजी तौर पर std :: वेक्टर बिना किसी समस्या के प्राप्त कर सकते हैं। मैंने निम्नलिखित पैटर्न का उपयोग किया है:

class PointVector : private std::vector<PointType>
{
    typedef std::vector<PointType> Vector;
    ...
    using Vector::at;
    using Vector::clear;
    using Vector::iterator;
    using Vector::const_iterator;
    using Vector::begin;
    using Vector::end;
    using Vector::cbegin;
    using Vector::cend;
    using Vector::crbegin;
    using Vector::crend;
    using Vector::empty;
    using Vector::size;
    using Vector::reserve;
    using Vector::operator[];
    using Vector::assign;
    using Vector::insert;
    using Vector::erase;
    using Vector::front;
    using Vector::back;
    using Vector::push_back;
    using Vector::pop_back;
    using Vector::resize;
    ...

3
"आपको केवल एक वर्चुअल विध्वंसक की आवश्यकता है यदि बेस क्लास और व्युत्पन्न वर्ग के आकार अलग-अलग नाड / या आपके पास वर्चुअल फ़ंक्शंस (जिसका अर्थ वी-टेबल) है।" यह दावा व्यावहारिक रूप से सही है, लेकिन सैद्धांतिक रूप से नहीं
अरमान सुनाइयन

2
हां, सिद्धांत रूप में यह अभी भी अपरिभाषित व्यवहार है।
जलफ 16

यदि आप दावा करते हैं कि यह अपरिभाषित व्यवहार है तो मैं एक प्रमाण (मानक से उद्धरण) देखना चाहूंगा।
ह्मुएलनर

8
@ विमलर: दुर्भाग्य से, इस पर अरमान और जलेफ़ सही हैं। से [expr.delete]C ++ 0x FCD में: <उद्धरण> पहला विकल्प (हटाना वस्तु) में, यदि वस्तु के स्थिर प्रकार हटाए जाने के लिए अपनी गतिशील प्रकार से अलग है, स्थिर प्रकार गतिशील प्रकार का एक आधार वर्ग होगा हटाई जाने वाली वस्तु और स्थैतिक प्रकार का एक आभासी विध्वंसक होगा या व्यवहार अपरिभाषित है। </ उद्धरण>
बेन Voigt

1
जो हास्यास्पद है, क्योंकि मुझे वास्तव में लगा कि व्यवहार एक गैर-तुच्छ विध्वंसक की उपस्थिति पर निर्भर था (विशेष रूप से, कि पॉड कक्षाओं को एक पॉइंटर-टू-बेस के माध्यम से नष्ट किया जा सकता है)।
बेन वोइगट

3

यदि आप अच्छी C ++ शैली का पालन करते हैं, तो वर्चुअल फ़ंक्शन की अनुपस्थिति समस्या नहीं है, लेकिन स्लाइसिंग (देखें https://stackoverflow.com/a/14461532/877329 )

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

void foo(SomeType* obj)
    {
    if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
        {
        obj->doStuff();
        }
    delete obj;
    }

class SpecialSomeType:public SomeType
    {
    // whatever 
    };

int main()
    {
    SpecialSomeType obj;
    doStuff(&obj); //Will crash here. But caller does not know that
//  ...
    }

इसके विपरीत, यह हमेशा (वर्चुअल विध्वंसक के साथ या बिना) काम करेगा:

void foo(SomeType* obj)
    {
    if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
        {
        obj->doStuff();
        }
    }

class SpecialSomeType:public SomeType
    {
    // whatever 
    };

int main()
    {
    SpecialSomeType obj;
    doStuff(&obj);
//  The correct destructor *will* be called here.
    }

यदि किसी फैक्ट्री द्वारा वस्तु का निर्माण किया जाता है, तो फैक्ट्री को एक काम करने वाले को एक पॉइंटर भी लौटा देना चाहिए, जिसका उपयोग इसके बजाय किया जाना चाहिए delete, क्योंकि फैक्ट्री अपने स्वयं के ढेर का उपयोग कर सकती है। कॉल करने वाला इसे फॉर्म का share_ptrया प्राप्त कर सकता है unique_ptr। संक्षेप में, नहीं है deleteकुछ भी आप नहीं मिला सीधे से new


2

हाँ यह तब तक सुरक्षित है जब तक आप सावधान रहें कि आप उन चीजों को न करें जो सुरक्षित नहीं हैं ... मुझे नहीं लगता कि मैंने कभी किसी को नए के साथ वेक्टर का उपयोग करते देखा है इसलिए व्यवहार में आप ठीक होंगे। हालाँकि, यह c ++ में आम मुहावरा नहीं है ...।

क्या आप एल्गोरिदम क्या हैं इसके बारे में अधिक जानकारी देने में सक्षम हैं?

कभी-कभी आप एक डिज़ाइन के साथ एक सड़क को समाप्त करते हैं और फिर आपके द्वारा लिए गए अन्य रास्तों को नहीं देख सकते हैं - यह तथ्य कि आप मेरे लिए 10 नए एल्गोरिदम के छल्ले अलार्म घंटियाँ के साथ वेक्टर की आवश्यकता का दावा करते हैं - क्या वास्तव में 10 सामान्य उद्देश्य हैं वे एल्गोरिथम जो एक वेक्टर कार्यान्वित कर सकते हैं, या आप एक वस्तु बनाने की कोशिश कर रहे हैं जो एक सामान्य उद्देश्य वेक्टर है और जिसमें एप्लिकेशन विशिष्ट फ़ंक्शन शामिल हैं?

मैं निश्चित रूप से यह नहीं कह रहा हूं कि आपको ऐसा नहीं करना चाहिए, यह सिर्फ इतना है कि आपके द्वारा दी गई जानकारी की घंटी बज रही है, जिससे मुझे लगता है कि शायद आपके गर्भपात के साथ कुछ गलत है और आपको प्राप्त करने का एक बेहतर तरीका है चाहते हैं।


2

मुझे std::vectorहाल ही में विरासत में मिला , और यह बहुत उपयोगी पाया गया और अब तक मुझे इसके साथ कोई समस्या नहीं हुई है।

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

समस्या सिर्फ मैं दृष्टिकोण के साथ देख सकते हैं इतना गैर आभासी नाशक के साथ नहीं है, बल्कि इस तरह के रूप में कुछ अन्य तरीकों, जो मैं अधिभार चाहते हैं, push_back(), resize(), insert()आदि निजी विरासत वास्तव में एक अच्छा विकल्प हो सकता है।

धन्यवाद!


10
मेरे अनुभव में, सबसे खराब दीर्घकालिक क्षति अक्सर उन लोगों के कारण होती है जो कुछ बीमार-सलाह की कोशिश करते हैं, और " अभी तक इसका अनुभव नहीं किया है ( इसके साथ कोई समस्या नहीं देखी गई है")।
मोहभंग हुआ

0

यहाँ, मुझे दो और तरीके पेश करने चाहिए जो आप चाहते हैं। एक लपेटने का एक और तरीका है std::vector, एक और तरीका है जो उपयोगकर्ताओं को कुछ भी तोड़ने का मौका दिए बिना विरासत में मिलता है:

  1. मुझे std::vectorफंक्शन रैपर्स के बिना बहुत सारे रैपिंग का एक और तरीका जोड़ना चाहिए ।

#include <utility> // For std:: forward
struct Derived: protected std::vector<T> {
    // Anything...
    using underlying_t = std::vector<T>;

    auto* get_underlying() noexcept
    {
        return static_cast<underlying_t*>(this);
    }
    auto* get_underlying() const noexcept
    {
        return static_cast<underlying_t*>(this);
    }

    template <class Ret, class ...Args>
    auto apply_to_underlying_class(Ret (*underlying_t::member_f)(Args...), Args &&...args)
    {
        return (get_underlying()->*member_f)(std::forward<Args>(args)...);
    }
};
  1. से इनहेरिट std :: अवधि के बजाय std::vectorऔर dtor समस्या से बचने के।

0

यह सवाल सांस की मोती-क्लचिंग का उत्पादन करने की गारंटी है, लेकिन वास्तव में एक मानक कंटेनर से बचने के लिए बचने के लिए, या "अनावश्यक रूप से गुणा करने वाली संस्थाओं" से बचाव का कोई कारण नहीं है। सबसे सरल, कम से कम संभव अभिव्यक्ति स्पष्ट, और सबसे अच्छी है।

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

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

एक जगह जहां यह तकनीक C ++ 20 में तुरंत उपयोगी होगी, आरक्षण है। जहाँ हमने लिखा होगा

  std::vector<T> names; names.reserve(1000);

हम कह सकते हैं

  template<typename C> 
  struct reserve_in : C { 
    reserve_in(std::size_t n) { this->reserve(n); }
  };

और फिर, वर्ग के सदस्यों के रूप में भी,

  . . .
  reserve_in<std::vector<T>> taken_names{1000};  // 1
  std::vector<T> given_names{reserve_in<std::vector<T>>{1000}}; // 2
  . . .

(वरीयता के अनुसार) और उन पर केवल रिजर्व () कॉल करने के लिए कंस्ट्रक्टर लिखने की आवश्यकता नहीं है।

(कारण यह है कि reserve_in , तकनीकी रूप से, C ++ 20 के लिए प्रतीक्षा करने की आवश्यकता है कि पूर्व मानकों को चालों में संरक्षित करने के लिए खाली वेक्टर की क्षमता की आवश्यकता नहीं है। इसे एक निरीक्षण के रूप में स्वीकार किया जाता है, और यथोचित रूप से तय होने की उम्मीद की जा सकती है। '20 के लिए समय में एक दोष के रूप में। हम यह भी उम्मीद कर सकते हैं कि पिछले मानकों के अनुसार प्रभावी ढंग से, ठीक होने की उम्मीद है, क्योंकि सभी मौजूदा कार्यान्वयन वास्तव में चालों में क्षमता का संरक्षण करते हैं; मानकों ने इसकी आवश्यकता नहीं है। उत्सुकता सुरक्षित रूप से कूद सकती है। बंदूक - भंडार लगभग हमेशा एक अनुकूलन वैसे भी है।)

कुछ का तर्क है कि reserve_inनि: शुल्क फ़ंक्शन टेम्पलेट द्वारा बेहतर तरीके से सेवा की जाती है:

  template<typename C> 
  auto reserve_in(std::size_t n) { C c; c.reserve(n); return c; }

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

  auto given_names{reserve_in<std::vector<T>>(1000)}; // 2
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.