2009-03-24 15 views
12

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

यह ठीक काम करता है, लेकिन मैंने एसटीएल से विरासत में नहीं होने के बारे में कई पोस्ट पढ़ी हैं। क्या कोई एक ठोस सलाह प्रदान कर सकता है कि मैं नीचे दिए गए कोड को बेहतर तरीके से कैसे लिख सकता हूं?

class Item 
{ 
    int a; 
    int b; 
    int c; 

    int SpecialB() 
    { 
    return a * b + c; 
    } 
}; 

class ItemList : public std::vector<Item> 
{ 
    int MaxA() 
    { 
    if(this->empty()) 
     throw; 

    int maxA = (*this)[0].a; 

    for(int idx = 1; idx < this->size(); idx++) 
    { 
     if((*this)[idx].a > maxA) 
     { 
     maxA = (*this)[idx].a; 
     } 
    } 
    return maxA; 
    } 

    int SpecialB() 
    { 
    if(this->empty()) 
     throw; 

    int specialB = (*this)[0].SpecialB(); 

    for(int idx = 1; idx < this->size(); idx++) 
    { 
     if((*this)[idx].SpecialB() < specialB) 
     { 
     specialB -= (*this)[idx].c; 
     } 
    } 
    return specialB; 
    } 

    int AvgC() 
    { 
    if(this->empty()) 
     throw; 

    int cSum = 0; 
    for(int idx = 0; idx < this->size(); idx++) 
    { 
     cSum += (*this)[idx].c; 
    } 

    return cSum/this->size(); // average 
    } 
}; 

संपादित: विचारशील जवाब का एक समूह के लिए धन्यवाद। मैं इसके बजाय सहायक कार्यों का निर्माण करूंगा और अब से एसटीएल कंटेनर से कभी भी उत्तराधिकारी नहीं होगा।

उत्तर

12

आपको इस तरह से वेक्टर का विस्तार करने की आवश्यकता क्यों है?

अपने मज़दूरों के साथ मानक <algorithm> का उपयोग करें।
उदा।

int accumulate_func(int start_from, int result, const Item& item) 
{ 
    if (item.SpecialB() < start_from) 
    { 
     result -= item.SpecialB(); 
    } 
    return result; 
} 

if (v.empty()) 
{ 
    throw sometghing("empty vector"); 
} 
const int result = std::accumulate(v.begin(), v.end(), v.front(), boost::bind(&accumulate_func, v.front(), _1, _2)); 

Btw:: -

std::min_element, std::max_element

int max_a = std::max_element 
     ( 
      v.begin(), 
      v.end(), 
      boost::bind( 
       std::less<int>(), 
       bind(&Item::a, _1), 
       bind(&Item::a, _2) 
      ) 
     )->a; 

std::accumulate calculate avarage के लिए

const double avg_c = std::accumulate(v.begin(), v.end(), double(0), boost::bind(Item::c, _1))/v.size(); // ofcourse check size before divide 

अपने ItemList :: SpecialB() के रूप में rewrited जा सकता है अगर आप डॉन सदस्यों तक पहुंच की आवश्यकता नहीं है, आपको विरासत की आवश्यकता नहीं है।

+0

लेकिन एक समय में किसी आइटम पर काम नहीं करता है? मेरे विशेष मामले को डेटा सदस्यों पर काम करने की जरूरत है। i.e. :: ए, :: बी, या :: सी ...? – sivabudh

+0

@ShChris, यही कारण है कि आप एक मज़ेदार का उपयोग करते हैं जो आपके लिए भारी भारोत्तोलन करता है ... –

+0

@ ShaChris23: अधिकांश एल्गोरिदम मज़दूरों को स्वीकार कर सकते हैं जो आपके सदस्यों को – bayda

1

मैं नहीं देख सकता कि आपको उन विधियों के साथ वेक्टर का विस्तार करने की आवश्यकता क्यों है। तुम बस, जैसे स्टैंडअलोन कार्यों के रूप में उन्हें लिख सकते हैं:

int MaxA(const std::vector<Item>& vec) { 
    if(vec.empty()) { 
     throw; 
    } 

    int maxA = vec[0].a; 
    for(std::vector<Item>::const_iterator i = vec.begin(); i != vec.end(); ++i) { 
     if(i->a > maxA) { 
      maxA = i->a; 
     } 
    } 
    return maxA; 
} 

या वहाँ std :: max_element जो ज्यादा एक ही करना होगा ... शून्य से निश्चित रूप से फेंक।

+0

मैं आप जो कह रहा हूं उससे सहमत हूं। हालांकि, आपको नहीं लगता कि यह अच्छा होगा अगर मैक्सए() फ़ंक्शन आपके कंटेनर क्लास का हिस्सा है? मेरा मतलब है कि मैक्सए को बाद में वेक्टर कंटेनर के साथ काम करने की उम्मीद है। – sivabudh

+0

यह अच्छा होगा, हाँ, लेकिन यह एक बुरा विचार है बी/सी एसटीएल विरासत के लिए डिज़ाइन नहीं किया गया है। मेरा जवाब देखें – tgamblin

11

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

हेक, यदि आप इसे अच्छी तरह से योजना बनाते हैं, तो इसे इंडेक्स के बजाय इटेटरेटर के साथ काम करें और यह std::vector से अधिक के साथ काम करेगा (<algorithm> कुछ अच्छे उदाहरणों के लिए देखें)।

उदाहरण के लिए, आप एक functor Maxa के लिए इस तरह इस्तेमाल कर सकते हैं:

struct CmpA { 
    bool operator()(const Item &item1, const Item &item2) { return item1.a < item2.a; } 
} 

const int result = std::max_element(v.begin(), v.end(), CmpA()).a; 

अपने specialB बस के रूप में एक functor और std::accumulate

संपादित साथ सरल किया जा सकता: या ग के लिए ++ 11 और बाद में, यह के रूप में आसान हो सकता है के रूप में:

const int result = std::max_element(v.begin(), v.end(), [](const Item &item1, const Item &item2) { 
    return item1.a < item2.a; 
}).a; 

संपादित करें: आपके द्वारा पूछे गए कारण है कि यह बेहतर है यह इस तरह से करने के लिए:

अगर आप एल्गोरिदम, टेम्पलेट्स और iterators उपयोग करें, यह भले ही आप एक std::list<Item> या जो कुछ भी में आइटम रखने का फैसला काम करेंगे।यह बस अधिक बहुमुखी है और कोड पुन: उपयोग करने में मदद करता है।

प्लस <algorithm> में फ़ंक्शंस आपके लिए बहुत कुछ करते हैं ताकि आप केवल 3 लाइन एडाप्टर फ़ैक्टर का उपयोग कर सकें।

संपादित करें: इस के अलावा, tgamblin कुछ बहुत ही बाध्यकारी कारण सूचीबद्ध std::vector से विरासत में नहीं करने के लिए (और अधिकांश अन्य एसटीडी कंटेनर, std::string सहित)।

+0

दरअसल: इटरेटर्स के लिए लेखन कार्य सदस्य कार्यों के एक बैग को विरासत में लाने से _way_ अधिक शक्तिशाली अवधारणा है। आपको +1 – xtofl

1

यदि मानक एल्गोरिदम में आपकी आवश्यकता नहीं है, तो बस नि: शुल्क फ़ंक्शंस लिखें, अधिमानतः टेम्पलेट।

2

एसटीएल कंटेनर से विरासत में नहीं होने के बारे में चेतावनी प्रकट होती है क्योंकि एसटीएल कंटेनर के तरीके आभासी नहीं हैं। तो यदि आप विधियों को ओवरराइड नहीं करते हैं और पॉलीमोर्फिक व्यवहार की आवश्यकता नहीं है, लेकिन केवल कक्षा का विस्तार करें - एसटीएल कंटेनर का उत्तराधिकारी होना ठीक है।

+0

यकीन है, यह "ठीक है"। लेकिन कोई बात नहीं है क्योंकि आप केवल std :: वेक्टर के सार्वजनिक इंटरफ़ेस पर भरोसा कर सकते हैं। तो आप विरासत की परेशानी के बिना उस पर भी काम कर सकते हैं। –

20

यह खराब विचार है।

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

इस पर अधिक जानकारी के लिए, पर std::string से प्राप्त करने के लिए यह उत्तर देखें। एक ही अंक में से कई लागू होते हैं:

Constructor doesn’t work for class inherited from std::string

  • कोई आभासी नाशक
  • कोई संरक्षित कार्यों
  • बहुरूपता काम नहीं करेगा (ताकि आप इनहेरिट द्वारा कुछ भी नहीं प्राप्त करता है), और आप वस्तु मिल जाएगा टुकड़ा करने की क्रिया। std::vector असाइन करने योग्य है, लेकिन यदि आप अपने स्वयं के फ़ील्ड जोड़ते हैं तो वे असाइनमेंट पर कॉपी नहीं होंगे यदि आप वेक्टर पॉइंटर या वेक्टर संदर्भ से असाइन करते हैं। ऐसा इसलिए है क्योंकि vector का operator= आपके फ़ील्ड के बारे में नहीं जानता है।

इन सभी कारणों से, आप एसटीएल की बात करते समय विस्तार से कार्य करने के लिए बेहतर हैं।

+0

आपने विनाशक का उल्लेख किया; अगर मेरे पास std :: सूची के अंदर ऑब्जेक्ट्स का गुच्छा है, तो क्या std :: सूची नष्ट होने के बाद वे नष्ट हो जाएंगे? या मुझे सफाई से पहले मैन्युअल रूप से सूची को खाली करना होगा। – sivabudh

5

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

1

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

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

class ItemList { 
private: 
    std::vector<Item> mItems; 
public: 
    typedef std::vector<Item>::size_type size_type; 
    int MaxA(); 
    int SpecialB(); 
    Item &operator[](size_type offset) { return mItems[offset]; } 
    size_type size() const { return mItems.size(); } 
}; 

...इत्यादि। यह एक कठिन काम है, लेकिन यह आपको वह प्रभाव देगा जो आपने मांगा था।

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