वेक्टर :: एट बनाम वेक्टर :: ऑपरेटर []


95

मुझे पता है कि इसकी सीमा जाँच की वजह at()से यह धीमी है [], जो C ++ वेक्टर एट / [] ऑपरेटर गति या :: std :: वेक्टर :: at () बनाम ऑपरेटर [] << आश्चर्यजनक परिणामों जैसे समान प्रश्नों पर भी चर्चा की गई है !! 5 से 10 गुना धीमा / तेज! । मुझे अभी समझ में नहीं आया कि यह at()विधि किस लिए अच्छी है।

यदि मेरे पास इस तरह का एक सरल वेक्टर है: std::vector<int> v(10);और मैं स्थिति के at()बजाय इसके तत्वों का उपयोग करने का निर्णय लेता हूं []जब मेरे पास एक सूचकांक होता है iऔर मुझे यकीन नहीं है कि अगर वैक्टर में इसकी सीमा होती है, तो यह मुझे इसे पकड़ने की कोशिश करने के लिए मजबूर करता है ब्लॉक करें :

try
{
    v.at(i) = 2;
}
catch (std::out_of_range& oor)
{
    ...
}

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

if (i < v.size())
    v[i] = 2;

तो मेरा सवाल है: वेक्टर
का उपयोग करने के क्या फायदे हैं : वेक्टर पर अधिक :: ऑपरेटर [] ?
मुझे वेक्टर :: आकार + वेक्टर :: ऑपरेटर [] के बजाय वेक्टर का उपयोग कब करना चाहिए ?


11
+1 बहुत अच्छा सवाल !! लेकिन मुझे नहीं लगता () है कि आमतौर पर इस्तेमाल किया।
रोहित विपिन मैथ्यूज

10
ध्यान दें कि आपके उदाहरण कोड में, if (i < v.size()) v[i] = 2;एक संभावित कोड पथ है जो 2किसी भी तत्व को निर्दिष्ट नहीं करता है v। अगर यह सही व्यवहार है, महान। लेकिन अक्सर समझदार कुछ भी नहीं है कि यह फ़ंक्शन कब कर सकता है i >= v.size()। इसलिए कोई विशेष कारण नहीं है कि अप्रत्याशित स्थिति को इंगित करने के लिए अपवाद का उपयोग नहीं करना चाहिए । कई फ़ंक्शन केवल operator[]आकार, दस्तावेज़ के खिलाफ जांच के बिना उपयोग करते हैं, जो iसीमा में होने चाहिए, और कॉलर पर परिणामी यूबी को दोष देते हैं।
स्टीव जेसोप

जवाबों:


74

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


1
+1 मुझे गलत उपयोगकर्ता के इनपुट को अलग करने के तरीके के बारे में स्पष्टीकरण पसंद है (इनपुट सत्यापन, अवैध इनपुट की उम्मीद की जा सकती है इसलिए इसे कुछ असाधारण नहीं माना जाता है) ... और कोड में कीड़े (सीमा से बाहर चलने वाले अनुमार्गण) असाधारण है बात)
Bojan Komazec

तो आप कहते हैं कि मुझे size()+ का उपयोग करना चाहिए []जब सूचकांक उपयोगकर्ताओं के इनपुट पर निर्भर करता है, उन assertस्थितियों में उपयोग करें जहां सूचकांक को भविष्य में आसान बग फिक्सिंग के लिए सीमा से बाहर नहीं होना चाहिए और .at()अन्य सभी स्थितियों में (बस मामले में, कुछ गलत हो सकता है)। ।)
10

8
@ लियो: यदि आपका कार्यान्वयन vectorतब डिबगिंग कार्यान्वयन प्रदान करता है, तो संभवतः इसका उपयोग करना बेहतर होगा क्योंकि " जस्ट इन केस" विकल्प के बजाय at()हर जगह। इस तरह आप रिलीज़ मोड में थोड़े और प्रदर्शन की उम्मीद कर सकते हैं, बस अगर आपको कभी ज़रूरत पड़े तो।
स्टीव जेसोप

3
हाँ, इन दिनों अधिकांश एसटीएल कार्यान्वयन एक डिबग मोड का समर्थन करते हैं, जो सीमा-जाँच भी करता है operator[], जैसे gcc.gnu.org/onbuildocs/libstdc++/manual/… इसलिए यदि आपका प्लेटफ़ॉर्म इस बात का समर्थन करता है, तो आप शायद इसके साथ सबसे बेहतर हैं!
pmdj

1
@pmdj शानदार बिंदु, जिसके बारे में मुझे नहीं पता था ... लेकिन अनाथ लिंक। : P करंट एक है: gcc.gnu.org/oniltocs/libstdc++/manual/debug_mode.html
underscore_d

16

यह मुझे कोशिश-पकड़ने वाले ब्लॉक के साथ इसे लपेटने के लिए मजबूर करता है

नहीं, यह नहीं (प्रयास / पकड़ ब्लॉक ऊपर की ओर हो सकता है)। यह तब उपयोगी होता है जब आप अपरिभाषित व्यवहार के दायरे में प्रवेश करने के लिए अपने कार्यक्रम के बजाय एक अपवाद को फेंकना चाहते हैं।

मैं मानता हूं कि वैक्टरों की सीमा तक पहुंच से बाहर एक प्रोग्रामर की गलती है (जिस स्थिति में आपको assertउन गलतियों का अधिक आसानी से पता लगाने के लिए उपयोग करना चाहिए , मानक पुस्तकालयों के अधिकांश डिबग संस्करण आपके लिए यह स्वचालित रूप से करते हैं)। आप अपवादों का उपयोग नहीं करना चाहते हैं जो प्रोग्रामर गलतियों की रिपोर्ट करने के लिए ऊपर की तरफ निगल सकते हैं: आप बग को ठीक करने में सक्षम होना चाहते हैं ।

चूंकि यह संभावना नहीं है कि वेक्टर के लिए सीमा से बाहर का उपयोग सामान्य कार्यक्रम प्रवाह का हिस्सा है (इस मामले में, आप सही हैं: sizeअपवाद को छोड़ देने के बजाय पहले से जांचें ), मैं आपके निदान से सहमत हूं: atअनिवार्य रूप से बेकार है।


अगर मैं out_of_rangeअपवाद नहीं पकड़ता , तो abort()कहा जाता है।
LihO

@ लिहो: जरूरी नहीं..जो try..catchइस विधि को बुला रहा है उस विधि में मौजूद हो सकता है।
नवीन

12
यदि और कुछ नहीं, atतो इस हद तक उपयोगी है कि आप स्वयं को कुछ ऐसा लिख ​​पाएंगे if (i < v.size()) { v[i] = 2; } else { throw what_are_you_doing_you_muppet(); }। लोग अक्सर "शाप, मुझे अपवाद को संभालना पड़ता है" के संदर्भ में अपवाद-फेंकने वाले कार्यों के बारे में सोचते हैं, लेकिन जब तक आप सावधानीपूर्वक दस्तावेज बनाते हैं कि आपके प्रत्येक कार्य क्या फेंक सकते हैं, उनका उपयोग "महान, मैं नहीं" के रूप में किया जा सकता है। एक शर्त की जाँच करें और एक अपवाद फेंकें "।
स्टीव जेसोप

@SteveJessop: मुझे प्रोग्राम बग के लिए अपवादों को फेंकना पसंद नहीं है, क्योंकि उन्हें अन्य प्रोग्रामर द्वारा अपस्ट्रीम पकड़ा जा सकता है। दावे यहाँ बहुत अधिक उपयोगी हैं।
अलेक्जेंड्रे सी।

6
@AlexandreC। ठीक है, उस के लिए आधिकारिक प्रतिक्रिया है कि से out_of_rangeव्युत्पन्न है logic_error, और अन्य प्रोग्रामर "चाहिए" को पता है कि logic_errorएस अपस्ट्रीम को पकड़ने और उन्हें अनदेखा करने से बेहतर है । assertअगर आपके सहकर्मी उनकी गलतियों के बारे में नहीं जानना चाहते हैं, तो भी इसे नजरअंदाज किया जा सकता है, यह सिर्फ इसलिए कठिन है क्योंकि उन्हें आपके कोड को संकलित करना होगा NDEBUG;-) प्रत्येक तंत्र की अपनी खूबियां और खामियां हैं।
स्टीव जेसप

11

सदिश का उपयोग करने के क्या फायदे हैं: सदिश से अधिक :: ऑपरेटर []? मुझे वेक्टर :: आकार + वेक्टर :: ऑपरेटर [] के बजाय वेक्टर का उपयोग कब करना चाहिए?

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

यह भी उल्लेखनीय है कि कुछ प्रकार के कोड में, एक इंडेक्स को जटिल तरीकों से बढ़ाया जा रहा है, और लगातार एक सरणी को देखने के लिए उपयोग किया जाता है। ऐसे मामलों में, सही चेक का उपयोग करना सुनिश्चित करना बहुत आसान है at()

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

if (token.at(i) == Token::Keyword_Enum)
{
    ASSERT_EQ(tokens.at(++i), Token::Idn);
    if (tokens.at(++i) == Left_Brace)
        ...
    or whatever

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


10

at यदि आप वेक्टर के लिए एक पॉइंटर हैं तो यह स्पष्ट हो सकता है:

return pVector->at(n);
return (*pVector)[n];
return pVector->operator[](n);

एक तरफ प्रदर्शन, इनमें से पहला सरल और स्पष्ट कोड है।


... खासकर जब आप के लिए एक सूचक की जरूरत n एक वेक्टर के मई के तत्व।
डॉल्फिन

4

सबसे पहले, चाहे at()या operator[]धीमी है निर्दिष्ट नहीं है। जब कोई त्रुटि नहीं होती है, तो मैं उनसे कम से कम डिबगिंग बिल्ड में समान गति के बारे में अपेक्षा करूंगा। अंतर यह है कि at()वास्तव में क्या होता है यह निर्दिष्ट करता है कि एक सीमा त्रुटि है (एक अपवाद), जहां के मामले में operator[], यह अपरिभाषित व्यवहार है - मेरे द्वारा उपयोग की जाने वाली सभी प्रणालियों में एक दुर्घटना (जी ++ और वीसी ++), कम से कम जब सामान्य डिबगिंग झंडे का उपयोग किया जाता है। (एक और अंतर यह है कि एक बार जब मैं अपने कोड के बारे में सुनिश्चित हो जाता हूं, तो मैं operator[] डिबगिंग को बंद करके पर्याप्त गति वृद्धि प्राप्त कर सकता हूं । यदि प्रदर्शन की आवश्यकता है - मैं इसे तब तक नहीं करूंगा जब तक कि यह आवश्यक न हो।)

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


4
आप उनसे उसी गति के बारे में अपेक्षा क्यों करेंगे, जब operator[]उन्हें सीमा-जांच के लिए मजबूर नहीं किया जाता है, जबकि at()है? क्या आप उस कैशिंग, अटकलें और ब्रांचिंग बफर मुद्दों को लागू कर रहे हैं?
सेबेस्टियन मच

@phresnel operator[]को सीमा जाँच करने की आवश्यकता नहीं है, लेकिन सभी अच्छे कार्यान्वयन करते हैं। कम से कम डिबगिंग मोड में। अंतर केवल इतना है कि वे क्या करते हैं यदि सूचकांक सीमा से बाहर है: operator[]त्रुटि संदेश के साथ गर्भपात, at()एक अपवाद फेंकता है।
जेम्स कंज

2
क्षमा करें, आपका "डिबगिंग मोड में" छूट गया है। हालाँकि, मैं डिबग मोड में इसकी गुणवत्ता पर कोड नहीं मापूंगा। रिलीज़ मोड में, जाँच केवल आवश्यक है at()
सेबेस्टियन मच

1
@phresnel मेरे द्वारा वितरित अधिकांश कोड "डीबग" मोड में है। आप केवल जाँच बंद कर देते हैं जब प्रदर्शन के मुद्दों को वास्तव में इसकी आवश्यकता होती है। (Microsoft पूर्व -2010 यहां एक समस्या std::stringथी , क्योंकि हमेशा काम नहीं करता था यदि जाँच विकल्प रनटाइम के अनुरूप नहीं थे: -MDऔर आप बेहतर तरीके से जाँच बंद -MDdकर देंगे, और आपके पास बेहतर होगा। इस पर।)
जेम्स कांजे

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

1

अपवादों का उपयोग करने का पूरा बिंदु यह है कि आपका त्रुटि हैंडलिंग कोड आगे दूर हो सकता है।

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

इसके अलावा, अन्य कंटेनरों, जैसे std::map, की std::map::atतुलना में थोड़ा अलग शब्दार्थ भी हैं std::map::operator[]: कास्ट मैप पर इस्तेमाल किया जा सकता है, जबकि operator[]नहीं कर सकते। अब अगर आप कंटेनर अज्ञेयवादी कोड लिखना चाहते हैं, तो ऐसा कुछ जो const std::vector<T>&या तो आपके साथ सौदा कर सकता है const std::map<std::size_t, T>&, या ContainerType::atआपकी पसंद का हथियार होगा।

हालांकि, ये सभी मामले आमतौर पर किसी तरह के अनलिमिटेड डेटा-इनपुट को हैंडल करते समय दिखाई देते हैं। यदि आप अपनी मान्य सीमा के बारे में सुनिश्चित हैं, जैसा कि आप आमतौर पर होना चाहिए, तो आप आमतौर पर उपयोग कर सकते हैं operator[], लेकिन बेहतर अभी तक, के साथ पुनरावृत्तियों begin()और end()


1

इस लेख के अनुसार , प्रदर्शन एक तरफ, यह उपयोग करने के लिए कोई फर्क नहीं पड़ता है atया operator[], केवल अगर पहुंच वेक्टर के आकार के भीतर होने की गारंटी है। अन्यथा, यदि पहुंच केवल वेक्टर की क्षमता पर आधारित है तो इसका उपयोग करना अधिक सुरक्षित है at


1
बाहर ड्रेगन हो। यदि हम उस लिंक पर क्लिक करते हैं तो क्या होगा? (संकेत: मुझे यह पहले से ही पता है, लेकिन StackOverflow पर हम ऐसी टिप्पणियों को प्राथमिकता देते हैं जो लिंक सड़ने से ग्रस्त नहीं होते हैं, यानी जो आप कहना चाहते हैं उसके बारे में एक संक्षिप्त सारांश प्रदान करें)
सेबस्टियन मच

पारितोषिक के लिए धन्यवाद। यह अब तय हो गया है।
आहज

0

नोट: ऐसा प्रतीत होता है कि कुछ नए लोग इस उत्तर को गलत बता रहे हैं, जो गलत है। नीचे उत्तर सही है और यहां सत्यापित किया जा सकता है ।

वहाँ वास्तव में केवल एक ही अंतर है: atसीमा जाँच operator[]नहीं करता है, जबकि नहीं। यह डिबग बिल्ड के साथ-साथ रिलीज़ बिल्ड पर भी लागू होता है और यह मानकों द्वारा बहुत अच्छी तरह से निर्दिष्ट है। यह इत्ना आसान है।

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


1
C ++ में अपवाद एक त्रुटि हैंडलिंग तंत्र के रूप में हैं, डिबगिंग के लिए एक उपकरण नहीं है। हर्ब सटर बताते हैं कि फेंकना std::out_of_rangeया किसी भी रूप में std::logic_error, वास्तव में, अपने आप में और यहां एक तर्क त्रुटि है
बिग टेम्प

@BigTemp - मुझे यकीन नहीं है कि आपकी टिप्पणी इस सवाल और जवाब से संबंधित कैसे है। हाँ, अपवाद अत्यधिक विषय पर बहस कर रहे हैं लेकिन सवाल यहाँ के बीच अंतर है atऔर []और मेरे जवाब बस अंतर में कहा गया है। मैं व्यक्तिगत रूप से "सुरक्षित" विधि का उपयोग करता हूं जब पूर्ण कोई मुद्दा नहीं है। जैसा कि नुथ कहते हैं कि समय से पहले अनुकूलन नहीं करते हैं। इसके अलावा, दार्शनिक मतभेदों की परवाह किए बिना उत्पादन की तुलना में कीड़े को जल्दी पकड़ना अच्छा है।
शीतल शाह
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.