C ++ में प्रथम श्रेणी के नागरिकों की तरह तरीके

दूसरे दिन, बग ट्रैकर के साथ चलते समय, एक दिलचस्प बग भर गया, यह एक ही बार में कई C ++ 11 सुविधाओं का उपयोग करता है:


इस बग का विश्लेषण करते हुए, मैंने सोचा कि अब आप प्रथम श्रेणी के नागरिकों के रूप में तरीकों को आसानी से लागू कर सकते हैं

वास्तव में, विकिपीडिया हमें बताता है कि प्रथम श्रेणी के नागरिक क्या हैं - एक इकाई जो एक कार्यक्रम की प्रक्रिया में बनाई जा सकती है, एक पैरामीटर के रूप में पारित की जाती है, जिसे एक चर को सौंपा जाता है, और फ़ंक्शन का परिणाम हो सकता है।

ट्रेनिंग


एक कंपाइलर चुनना

चूंकि मेरे पास ताजा gcc या msvc नहीं है, इसलिए मैंने ताजा क्लेंग-3.1 बनाने का फैसला किया:
mkdir llvm cd llvm svn co http://llvm.org/svn/llvm-project/llvm/tags/RELEASE_31/final ./ cd tools svn co http://llvm.org/svn/llvm-project/cfe/tags/RELEASE_31/final clang cd ../../ mkdir build cd build cmake ../llvm -DCMAKE_INSTALL_PREFIX=/home/pixel/fakeroot -DCMAKE_BUILD_TYPE=Release make -j4 make check-all make install 


Libcxx लाइब्रेरी का चयन करें

मैंने नए संकलक की सभी विशेषताओं का उपयोग करने के लिए libcxx लाइब्रेरी बनाने का भी फैसला किया है:
 mkdir libcxx cd libcxx svn co http://llvm.org/svn/llvm-project/libcxx/trunk ./ cd ../ mkdir build_libcxx cd build_libcxx CC=clang CXX=clang++ cmake ../libcxx -DCMAKE_INSTALL_PREFIX=/home/pixel/fakeroot -DCMAKE_BUILD_TYPE=Release make -j4 make install 


Libcxx असेंबली के बारे में कुछ शब्द: मैंने ट्रंक से नवीनतम संस्करण लेने का फैसला किया, क्योंकि आखिरी रिलीज मुझसे नहीं बनना चाहता था (मैं इसे समझना नहीं चाहता था, इसलिए मैंने ट्रंक लिया)। इसके साथ ही क्लेंक का उपयोग करते हुए libcxx का निर्माण किया जाना चाहिए, इसके लिए मैंने वातावरण चर CC और CXX को क्लैंग के साथ बदलने के लिए सेट किया है। इसके अलावा, किसी कारण से, मैं परीक्षण चलाना नहीं चाहता ( चेक-लिबक्स बना )

हौसले से उठाए गए क्लैंग और libcxx का उपयोग करने के लिए CMakeLists.txt उदाहरण

 cmake_minimum_required(VERSION 2.8) project (clang_haxxs) add_definitions(-std=c++11 -nostdinc++) include_directories(/home/pixel/fakeroot/lib/clang/3.1/include) include_directories(/home/pixel/fakeroot/include/c++/v1) link_directories(/home/pixel/fakeroot/lib) add_executable(clang_haxxs main.cpp) set_target_properties(clang_haxxs PROPERTIES LINK_FLAGS -stdlib=libc++) 


तदनुसार, cmake के लिए हम पर्यावरण चर CC और CXX को उसी तरह पुनर्परिभाषित करते हैं जैसे कि libcxx का निर्माण करते समय।

व्याख्यात्मक उदाहरण


इसलिए, तैयारी की प्रक्रिया पूरी हो गई है, हम उदाहरण के लिए आगे बढ़ते हैं:
 #include <iostream> #include <functional> using namespace std; struct FirstClass { FirstClass(): x(0) { } int get_x() const { return x; } function<int ()> f1 = [this]() -> int { cout << "called member function f1..." << endl; ++x; f1 = f2; return 5; }; private: function<int ()> f2 = [this]() -> int { cout << "called member function f2..." << endl; return x; }; int x; }; int main() { FirstClass m; m.f1(); m.f1(); function<int ()> f3 = []() -> int { cout << "called free function f3..." << endl; return 100500; }; m.f1 = f3; m.f1(); return 0; } 

कार्यक्रम का आउटपुट:
called member function f1...
called member function f2...
called free function f3...


वास्तव में, इसी तरह की कार्यक्षमता को c ++ 11 के बिना लागू किया जा सकता है, लेकिन यह कम पठनीय लगेगा। कोड पठनीयता में मुख्य योगदान गैर-स्थैतिक सदस्य आरंभीकरण द्वारा किया जाता है - हमें C ++ - 03 में सामान्य विधियों के समान विधि की घोषणा और कार्यान्वयन प्राप्त होता है। अन्य विशेषताएं कम या ज्यादा C ++ - 03 और तृतीय-पक्ष पुस्तकालयों द्वारा उत्सर्जित होती हैं: बढ़ावा :: कार्य, बढ़ावा :: लंबोदर।

विसर्जन


आइए अधिक विस्तार से विचार करें कि हम ऐसी वस्तुओं के साथ क्या कर सकते हैं:
स्थिर और गैर-स्थिर तरीकों का अनुकरण

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

यहां एक ख़ासियत है: वास्तव में, स्थैतिक कार्यों की अवधारणा का उपयोग यहां सही ढंग से नहीं किया गया है, क्योंकि शुरू में सी ++ में उन्हें ऐसे कार्यों के रूप में परिभाषित किया गया है जिन्हें एक निर्मित वस्तु के बिना कहा जा सकता है, यहां हम अभी भी फ़ंक्शन तक पहुंचने के लिए एक ऑब्जेक्ट बनाने के लिए मजबूर हैं।

कक्षा के बाहर तरीकों की स्थापना

हमें पता चला कि एक गैर-स्थैतिक फ़ंक्शन को कैसे परिभाषित किया जाए, अब यह समझना है कि कक्षा के बाहर यह कैसे करना है, यह बहुत सरल है - आपको उस वस्तु को स्थानांतरित करने की आवश्यकता है जिसके लिए यह फ़ंक्शन कैप्चर सूची से जुड़ा हुआ है:
  function<int ()> f4 = [&m]() ->int { cout << "called free function f4 with capture list..." << endl; return m.get_x() + 1; }; m.f1 = f4; m.f1(); 


कैप्चर सूची में किसी वस्तु के संदर्भ को पास करते समय, यहां हमें सावधान रहना चाहिए, क्योंकि किसी फ़ंक्शन को परिभाषित करने और उसे ऑब्जेक्ट पर बाँधने की कार्रवाई समयबद्ध है, हम निम्नलिखित गलती कर सकते हैं:
"कैप्चर सूची में निर्दिष्ट गलत वस्तु से बांधें।"

इसके अलावा, एक और सीमा जो यहां मौजूद है, अगर हम वर्ग घोषणा के बाहर एक फ़ंक्शन संलग्न करते हैं, तो हम निजी वर्ग चर तक पहुंच खो देते हैं:
  function<int ()> err = [&m]() ->int { cout << "called free function err with capture list..." << endl; return mx + 1; }; 

उसी समय, संकलक शपथ लेता है: /usr/src/projects/clang/usage/main.cpp:64:12: error: 'x' is a private member of 'FirstClass'

विधि का निषेध ओवरराइड करता है

यहां सब कुछ सरल है, क्योंकि विधि वर्ग का एक सामान्य सदस्य है, इसके विवरण में कोई बाधा नहीं है, हमें बस वही मिलता है, जिसकी हमें आवश्यकता है:
  struct FirstClassConst { const function <int()> f1 = []() -> int { return 1; }; }; FirstClassConst mc; mc.f1 = f3; 

संकलक हमें डांटता है: /usr/src/projects/clang/usage/main.cpp:70:8: error: no viable overloaded '='
mc.f1 = f3;
~~~~~ ^ ~~
/usr/src/projects/clang/usage/main.cpp:70:8: error: no viable overloaded '='
mc.f1 = f3;
~~~~~ ^ ~~


कांस्ट विधि का अभाव

ईमानदार सी ++ विधियों में यह निर्धारित करने की क्षमता है कि विधि वर्ग के सदस्यों को नहीं बदलती है, और इसे एक स्थिर ऑब्जेक्ट पर लागू किया जा सकता है, इस तरह के तरीकों को कांस्टेबल क्वालीफायर के साथ चिह्नित किया जाता है। उदाहरण में, यह get_x विधि है।
यदि हम वस्तुओं के रूप में तरीकों को लागू करते हैं, तो यह संभावना गायब हो जाती है, इसके बजाय हम एक स्थिर वस्तु के सदस्यों को बदल सकते हैं:
  struct MutableFirstClass { int x; MutableFirstClass(): x(0){} int nonConstMethod() { ++x; return x; } function <int()> f1 = [this]() -> int { this->x = 100500; return x; }; }; const MutableFirstClass mm; mm.f1(); //mm.nonConstMethod(); 

यदि आप अंतिम कॉल को /usr/src/projects/clang/usage/main.cpp:93:2: error: member function 'nonConstMethod' not viable: 'this' argument has type 'const MutableFirstClass', but function is not marked const
mm.nonConstMethod();
^~
करते हैं, तो संकलक निम्नानुसार शपथ लेता है: /usr/src/projects/clang/usage/main.cpp:93:2: error: member function 'nonConstMethod' not viable: 'this' argument has type 'const MutableFirstClass', but function is not marked const
mm.nonConstMethod();
^~
/usr/src/projects/clang/usage/main.cpp:93:2: error: member function 'nonConstMethod' not viable: 'this' argument has type 'const MutableFirstClass', but function is not marked const
mm.nonConstMethod();
^~

अधिकांश कार्यों के निम्नलिखित अनुक्रम होने की संभावना है:
गैर स्थैतिक सदस्य आरंभीकरण सिंटैक्टिक शुगर से अधिक कुछ नहीं है, और इसलिए कैप्चर सूची में यह कैप्चर कंस्ट्रक्टर में होता है, और कंस्ट्रक्टर में यह प्रकार MutableFirstClass * कास्ट का है , और इसलिए हम वैरिएबल के मूल्यों को बदल सकते हैं।

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

आगे क्या है


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

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

इसके अलावा, मेमोरी की खपत बढ़ जाती है, libcxx के कार्यान्वयन में, प्रत्येक ऐसी विधि 16 बाइट्स लेती है, इसलिए विधियों की संख्या में वृद्धि के साथ, हम अधिक से अधिक बोल्ड ऑब्जेक्ट प्राप्त करेंगे।

आपको समय माप भी लेना चाहिए और देशी C ++ विधियों की तुलना में ऐसे तरीकों की कॉल गति की तुलना करें (आप आभासी तरीकों से गति की तुलना कर सकते हैं)।

Source: https://habr.com/ru/post/In145673/


All Articles