c++ - क्यों std:: जोड़ी एक्सपोजर मेम्बर वेरिएबल्स?




stl encapsulation (5)

इसका कारण यह है कि डेटा संरचना पर किसी वास्तविक आक्रमणकारी को लगाने की आवश्यकता नहीं है, क्योंकि std::pair मॉडल दो तत्वों के लिए एक सामान्य-उद्देश्य वाला कंटेनर है। दूसरे शब्दों में, प्रकार std::pair<T, U> की एक वस्तु को क्रमशः T और U किसी भी first और second तत्व के लिए मान्य माना जाता है। इसी तरह, इसके तत्वों के मूल्य में बाद के म्यूटेशन वास्तव में std::pair प्रति std::pair की वैधता को प्रभावित नहीं कर सकते हैं।

एलेक्स स्टेपानोव (एसटीएल के लेखक) ने अपने पाठ्यक्रम के दौरान इस सामान्य डिजाइन सिद्धांत को स्पष्ट रूप से प्रस्तुत किया है , घटक के साथ कुशल प्रोग्रामिंग , जब singleton कंटेनर (यानी, एक तत्व का एक कंटेनर) पर टिप्पणी करते हैं।

इस प्रकार, अपने आप में सिद्धांत बहस का एक स्रोत हो सकता है, यह std::pair के आकार के पीछे कारण है।

http://www.cplusplus.com/reference/utility/pair/ , हम जानते हैं कि std::pair के दो सदस्य चर हैं, first और second

एसटीएल डिजाइनरों ने getFirst() और getFirst() की पेशकश करने के बजाय दो सदस्य चर, first और second को उजागर करने का फैसला क्यों किया?


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

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

गेटर्स और सेटर्स के लिए एक उदाहरण एक वर्ग होगा जो एक गणितीय वेक्टर का प्रतिनिधित्व करता है जिसमें आप वेक्टर के बारे में कुछ जानकारी कैश करना चाहते हैं। कहते हैं कि आप लंबाई स्टोर करना चाहते हैं। इस मामले में, बदलते vec.x शायद लंबाई / परिमाण को बदल देंगे। तो, न केवल आपको एक्स को एक गेटर में लपेटने की आवश्यकता है, आपको एक्स के लिए एक सेटर प्रदान करना होगा, जो वेक्टर की कैश्ड लंबाई को अपडेट करना जानता है। (बेशक, अधिकांश वास्तविक गणित पुस्तकालय इन मूल्यों को कैश नहीं करते हैं, और इस प्रकार चर को उजागर करते हैं।)

तो आपको उनका उपयोग करने के संदर्भ में खुद से पूछने का सवाल है: क्या यह वर्ग कभी इस अवधारणा को नियंत्रित करने की आवश्यकता है या इस चर में परिवर्तन के लिए सतर्क हो सकता है?

Std :: pair जैसी किसी चीज़ में उत्तर एक फ्लैट "नहीं" है। किसी वर्ग में सदस्यों तक पहुंच को नियंत्रित करने का कोई मामला नहीं है जिसका एकमात्र उद्देश्य उन सदस्यों को शामिल करना है। निश्चित रूप से यह जानने के लिए जोड़ी की कोई आवश्यकता नहीं है कि क्या उन चर को छुआ गया है, यह देखते हुए कि वे केवल इसके दो सदस्य हैं, और इस प्रकार अद्यतन करने की कोई स्थिति नहीं है। जोड़ी इस बात से अनभिज्ञ है कि इसमें वास्तव में क्या है और इसका अर्थ क्या है, इसलिए इसमें जो भी है उसे ट्रैक करना प्रयास के लायक नहीं है।

संकलक के आधार पर और यह कैसे कॉन्फ़िगर किया गया है, गेटर्स और सेटर ओवरहेड को पेश कर सकते हैं। यह शायद ज्यादातर मामलों में महत्वपूर्ण नहीं है, लेकिन अगर आप उन्हें std::pair जैसी मौलिक चीज़ों पर रखना चाहते हैं, तो यह एक गैर-तुच्छ चिंता होगी। जैसे, उनके जोड़ को उचित ठहराया जाना चाहिए - जैसा कि मैंने अभी समझाया, यह नहीं हो सकता।


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

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

"भविष्य में परिवर्तन" के लिए विहित ( हालांकि खराब ) उदाहरण Point - क्या आपको x और y या getY() और getY() पर्दाफाश करना चाहिए? सामान्य उत्तर गेटर्स / सेटर का उपयोग करना है क्योंकि भविष्य में कुछ समय पर आप कार्टेसियन से आंतरिक प्रतिनिधित्व को ध्रुवीय में बदलना चाह सकते हैं और आप नहीं चाहते कि आपके उपयोगकर्ता प्रभावित हों (या उन्हें उस डिज़ाइन निर्णय पर निर्भर होना चाहिए) ।

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


मुझे उन टिप्पणियों की संख्या से याद किया गया जो ऑब्जेक्ट-ओरिएंटेड डिज़ाइन की कोई बुनियादी समझ नहीं दिखाती हैं (क्या यह साबित करता है कि c ++ एक OO- भाषा नहीं है?)। हाँ, एसटीडी का डिज़ाइन :: जोड़ी में कुछ ऐतिहासिक लक्षण होते हैं, लेकिन यह खराब डिज़ाइन को अच्छा नहीं बनाता है; न ही इसे तथ्य को नकारने के बहाने के रूप में इस्तेमाल किया जाना चाहिए। इससे पहले कि मैं इस पर टिप्पणी करूं, मुझे टिप्पणियों में कुछ सवालों के जवाब देने दें:

आपको नहीं लगता कि इंट में सेटर और गेटटर भी होना चाहिए

हां, डिजाइन के दृष्टिकोण से हमें एक्सेसरों का उपयोग करना चाहिए क्योंकि ऐसा करने से हम अतिरिक्त लचीलेपन के अलावा कुछ नहीं खोते हैं। कुछ नए एल्गोरिदम अतिरिक्त बिट्स को कुंजी / मानों में पैक करना चाहते हैं, और आप उन्हें एक्सेसर्स के बिना एन्कोड / डिकोड नहीं कर सकते।

अगर गटर में कोई तर्क नहीं है तो एक गेटर में कुछ क्यों लपेटें?

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

आम धारणा के विपरीत, ऐसी वस्तुएं जो कुछ भी नहीं करतीं लेकिन सदस्य चर को गेटर्स और सेटर के साथ स्टोर करते हैं, "जिस तरह से काम किया जाना चाहिए" नहीं है।

एक और जंगली अनुमान।

ठीक है, मैं यहां रुकूंगा।

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

मैंने देखा कि वे std :: unordered_map के कार्यान्वयन के लिए ओपन हैशिंग (यानी, क्लोज्ड चेनिंग) के उपयोग को भी अनिवार्य करते हैं। यह न केवल एक डिज़ाइन के दृष्टिकोण से अजीब है (आप चीजों को कैसे लागू करना चाहते हैं, इसे प्रतिबंधित करना चाहते हैं ???), लेकिन कार्यान्वयन के मामले में भी बहुत गूंगा - लिंक की गई सूची का उपयोग करके जंजीर हैशटेब शायद सभी श्रेणियों में सबसे धीमा है। वेब पर जाएं, हम आसानी से उस std को पा सकते हैं: unordered_map अक्सर हैशटेबल बेंचमार्क का डोरमैट होता है। यहां तक ​​कि यह जावा के हाशप (मैं नहीं जानता कि वे इस मामले में कैसे पिछड़ गए, यह भी पता नहीं है, क्योंकि हाशप एक जंजीर हैशटेबल भी है)। एक पुराना बहाना यह है कि chained हैशटेबल बेहतर प्रदर्शन करते हैं जब load_factor 1 के पास पहुंचता है, जो पूरी तरह से अमान्य है क्योंकि 1) इस समस्या से निपटने के लिए खुले पते वाले परिवार में बहुत सारी तकनीकें हैं - कभी हूप्सकोटिंग और रॉबिन-हूड हैशिंग, और के बारे में सुना। उत्तरार्द्ध वास्तव में 30 सनकी वर्षों के लिए वहाँ गया है; 2) प्रत्येक प्रवेश के लिए एक जंजीर हैशटेबल एक पॉइंटर (64 बिट मशीनों पर एक अच्छा 8 बाइट्स) को जोड़ता है, इसलिए जब हम कहते हैं कि अनऑर्डेड_मैप अप्रोच 1 का लोड_फैक्टर, यह 100% उपयोग का उपयोग नहीं है! हमें इसे ध्यान में रखना चाहिए और समान स्मृति उपयोग के साथ विकल्पों के साथ unordered_map के प्रदर्शन की तुलना करना चाहिए। और यह पता चला है कि Google Dense HashMap जैसे विकल्प std :: unordered_map से 3-4 गुना तेज हैं।

ये प्रासंगिक क्यों हैं? क्योंकि दिलचस्प बात यह है कि ओपन हैशिंग को अनिवार्य करने से std की जोड़ी बनती है :: जोड़ी कम खराब दिखती है, अब हमें वैकल्पिक भंडारण संरचना के लचीलेपन की आवश्यकता नहीं है। इसके अलावा, एसटीडी की उपस्थिति :: जोड़ी को नए / बेहतर एल्गोरिदम को अपनाने के लिए लगभग असंभव बना देता है ताकि एसटीडी की एक ड्रॉप-इन प्रतिस्थापन में लिखा जा सके: unordered_map। कभी-कभी आपको आश्चर्य होता है कि क्या उन्होंने जानबूझकर ऐसा किया है ताकि एसटीडी की खराब डिजाइन :: जोड़ी और एसटीडी के पैदल यात्री कार्यान्वयन :: unordered_map साथ में लंबे समय तक जीवित रह सकें। बेशक, मैं मजाक कर रहा हूं, इसलिए जिसने भी लिखा है, वह नाराज मत हो। वास्तव में जावा या पायथन का उपयोग करने वाले लोग (ठीक है, मैं पायथन के खिंचाव को स्वीकार करता हूं) उन्हें "सी ++ के रूप में तेज़" होने के बारे में अच्छा महसूस करने के लिए धन्यवाद देना चाहता हूं।


यह तर्क दिया जा सकता है कि std::pair अपने सदस्यों तक पहुँचने के लिए एक्सेसर कार्यों को करना बेहतर होगा! विशेष रूप से std::pair पतित मामलों के लिए std::pair एक फायदा हो सकता है। उदाहरण के लिए, जब कम से कम एक प्रकार का एक खाली, गैर-अंतिम वर्ग होता है, तो वस्तुएं छोटी हो सकती हैं (खाली आधार को एक आधार बनाया जा सकता है जिसे अपना पता प्राप्त करने की आवश्यकता नहीं होगी)।

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

दूसरी ओर, यह इसे ऑप्टिमाइज़र पर [थोड़ा] कठिन बनाता है यह देखने के लिए कि जब एक्सेसर फ़ंक्शन का उपयोग किया जाता है तो क्या हो रहा है क्योंकि अतिरिक्त अनुक्रम बिंदुओं को पेश किया जाता है। मैं कल्पना कर सकता था कि मेंग ली और अलेक्जेंडर स्टेपानोव ने भी मापा था कि क्या अंतर है (न ही मैंने)। यहां तक ​​कि अगर वे नहीं करते हैं, तो सदस्यों को सीधे पहुंच प्रदान करना निश्चित रूप से एक सहायक समारोह से गुजरने की तुलना में धीमा नहीं है, जबकि रिवर्स जरूरी नहीं है।

मैं निर्णय का हिस्सा नहीं था और C ++ मानक में तर्क नहीं है, लेकिन मुझे लगता है कि यह सदस्यों को सार्वजनिक डेटा सदस्य बनाने के लिए एक जानबूझकर निर्णय था।







encapsulation