GObject: एनकैप्सुलेशन, इंस्टेंटेशन, आत्मनिरीक्षण

... साथ ही साथ अन्य डरावने शब्द! (सी)

इससे पहले कि हम GLib प्रकार के ऑब्जेक्ट सिस्टम के कुछ उन्नत विशेषताओं से परिचित हों, हमें कई बिंदुओं के बारे में बात करने की आवश्यकता है, जिन्हें हमने पिछले दो लेखों में नहीं छुआ था। इस बार हम बुनियादी प्रकार के GObject से परिचित होंगे, इस तथ्य के बारे में बात करेंगे कि बुनियादी GObject का कोई वंशज अलग संरचना की वस्तुओं की दोहरी एकता (और अक्सर त्रिगुण) है, जिसमें हेडर फ़ाइलों और फ़ाइलों की शुरुआत में स्रोत कोड के साथ रहस्यमय मैक्रोज़ खोले जाते हैं। कठोर स्थानीय RTTI के लिए कौन से उपकरण काम करते हैं, GObject और उसके वंशज के पास दो विध्वंसक (और तीन निर्माणकर्ता) क्यों हैं, साथ ही साथ कई अन्य दिलचस्प छोटी चीजें भी हैं।

छवि

GObject के बारे में पूरा चक्र:


Gobject: मूल बातें
GObject: विरासत और इंटरफेस
GObject: एनकैप्सुलेशन, इंस्टेंटेशन, आत्मनिरीक्षण

संरचना। संरचनाओं के बहुत सारे।


जैसा कि हम जानते हैं, GObject के वंशजों को विरासत में लिया जा सकता है - व्युत्पन्न और गैर-विरासत में मिला - अंतिम। सामान्य तौर पर, एक व्युत्पन्न GObject में तीन ऑब्जेक्ट्स का एक संयोजन होता है: एक क्लास स्ट्रक्चर, एक इंस्टेंस स्ट्रक्चर और प्राइवेट डेटा के साथ एक स्ट्रक्चर।

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

/* animalcat.h */ /*   ,      :) */ typedef struct _AnimalCat AnimalCat; typedef struct _AnimalCatClass AnimalCatClass; typedef struct _AnimalCatPrivate AnimalCatPrivate; struct _AnimalCatClass { GObjectClass parent_class; /*    */ void (*say_meow) (AnimalCat*); /*   */ gpointer padding[10]; /*  ; gpointer -  void* */ }; 

अंतिम प्रकारों के लिए, एक वर्ग संरचना को परिभाषित करने की आवश्यकता नहीं है।

व्युत्पन्न वस्तुओं के लिए निजी डेटा के साथ एक संरचना की आवश्यकता होती है। इसे स्रोत कोड फ़ाइल में परिभाषित किया गया है, और फॉर्म का उपयोग स्वचालित रूप से उत्पन्न फ़ंक्शन के माध्यम से प्राप्त किया जा सकता है जैसे कि animal_cat_get_instance_private ()। इस स्थिति में, .s-file की शुरुआत में मैक्रो G_DEFINE_TYPE_WITH_PRIVATE (NamespaceObject, namespace_object, PARENT_TYPE) की तरह दिखना चाहिए। आप मैक्रो G_DEFINE_TYPE_WITH_CODE (मैक्रो G_ADD_PRIVATE शामिल) का उपयोग कर सकते हैं।

 /* animalcat.c */ #include "animalcat.h" G_DEFINE_TYPE_WITH_PRIVATE(AnimalCat, animal_cat, G_TYPE_OBJECT) /* G_DEFINE_TYPE_WITH_CODE(AnimalCat, animal_cat, G_TYPE_OBJECT, G_ADD_PRIVATE (AnimalCat)) */ struct _AnimalCatPrivate { char* name; double weight; int age; }; static void animal_cat_init(AnimalCat* self) { AnimalCatPrivate* priv = animal_cat_get_instance_private(self); priv->age = 0; priv->name = "Barsik"; /*    */ } 

सभी डेटा को एनकैप्सुलेटेड माना जाता है। उन्हें एक्सेस करने के लिए, आप सामान्य रैपर - गेटर्स और सेटर का उपयोग कर सकते हैं, लेकिन, जैसा कि हम बाद में देखेंगे, GObject इसके लिए बहुत अधिक शक्तिशाली उपकरण प्रदान करता है।

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

 /* animaltiger.c */ struct _AnimalTiger { AnimalCat parent; /*         */ int speed; /*   */ }; 

इंटरफेस के रूप में, उनके कार्यान्वयन के लिए केवल इंटरफ़ेस संरचना को परिभाषित करना आवश्यक है, बहुत सामान्य वर्ग के समान। _AnimalPredator व्यू ऑब्जेक्ट की संरचना स्वयं ही उत्पन्न हो जाएगी।

 /* animalpredator.h */ typedef struct _AnimalPredatorInterface AnimalPredatorInterface; struct _AnimalPredatorInterface { GTypeInterface parent; /*     GTypeInterface */ void (*hunt) (AnimalPredator* self); /*   */ }; 


दृश्य पालना तालिका:
छवि

व्यवहार में गतिशील प्रकार का पता लगाना


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

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

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

ANIMAL_CAT_CLASS (klass): वर्ग संरचनाओं के लिए एक समान मैक्रो। सम्मेलन C ++ कंपाइलर के साथ संगतता के लिए शब्द वर्ग का उपयोग नहीं करने के लिए निर्धारित करता है।

ANIMAL_IS_CAT (obj): जैसा कि नाम से ही स्पष्ट है, यह स्थूल निर्धारित करता है कि क्या obj इस प्रकार का सूचक है (और क्या यह NULL सूचक है)। इस तरह के चेक के साथ ऑब्जेक्ट के तरीकों को शुरू करना अच्छा है।

 void animal_cat_run (AnimalCat *self) { assert(ANIMAL_IS_CAT (self)); g_return_if_fail (ANIMAL_IS_CAT (self)); /*   GLib */ /*   */ } 

ANIMAL_IS_CAT_CLASS (klass): वर्ग संरचनाओं के लिए समान है।

ANIMAL_CAT_GET_CLASS (obj): संबंधित वर्ग संरचना के लिए एक संकेतक लौटाता है।

इंटरफेस के लिए मैक्रो परिभाषाओं का एक समान सेट भी उत्पन्न होता है।

ANIMAL_PREDATOR (obj): इंटरफ़ेस प्रकार के लिए कास्ट।
ANIMAL_IS_PREDATOR (obj): प्रकार की जाँच।
ANIMAL_PREDATOR_GET_IFACE (obj): इंटरफ़ेस संरचना प्राप्त करना।

ऑब्जेक्ट नाम मैक्रो G_OBJECT_TYPE_NAME (obj) का उपयोग करके प्राप्त किया जा सकता है, जो टाइप नाम के साथ एक सी-स्ट्रिंग लौटाता है।

स्रोत फ़ाइल G_DEFINE_TYPE और उसके विस्तारित संस्करणों की शुरुआत में मैक्रोज़ फॉर्म एनिमल_कैट_परेंट_क्लास का एक पॉइंटर उत्पन्न करते हैं, जो पॉइंटर को पैरेंट ऑब्जेक्ट के क्लास स्ट्रक्चर में लौटाता है, साथ ही फॉर्म एनिमल_कैट_गेट_इंस्टीन_पैरेंट () के फंक्शन का उपयोग करता है, अगर हम संबंधित मैक्रो का उपयोग करते हैं।

विध्वंसक और अन्य आभासी कार्य


जैसा कि हम याद करते हैं, किसी भी गोबिज वंशज को बनाते समय, फार्म Animal_cat_init () के कार्य लॉन्च किए जाते हैं। वे सी ++ और जावा कंस्ट्रक्टर के रूप में एक ही भूमिका करते हैं। विनाशकारियों के साथ, स्थिति अधिक जटिल है।

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

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

फ़ंक्शंस निपटाना () और अंतिम रूप देना (), साथ ही कई अन्य, जिनके बारे में हम बाद में बात करेंगे, आभासी हैं और GObjectClass में परिभाषित हैं।

 static void animal_cat_finalize(GObject* obj) { g_print("Buy!\n"); /*  printf()  GLib */ /*    . . */ G_OBJECT_CLASS (animal_cat_parent_class)->finalize(obj); /*         */ } static void animal_cat_class_init(AnimalCatClass* klass) { GObjectClass* obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = animal_cat_finalize; /*   */ } 

Animal_cat_finalize () फ़ंक्शन की अंतिम पंक्ति को और स्पष्टीकरण की आवश्यकता हो सकती है। जब मैक्रो G_DEFINE_TYPE और उसके विस्तारित संस्करणों का विस्तार किया जाता है, तो पैरेंट क्लास को पशु_कैट_परेंट_क्लास पॉइंटर बनाया जाता है। हम मूल वर्ग से संबंधित फ़ंक्शन को कॉल करते हैं, जो इस मामले में सीधे एक GObjectClass संरचना है, और यह, बदले में, श्रृंखला में पिछले वर्ग के अंतिम () को कॉल करता है। यह चिंता करने की कोई आवश्यकता नहीं है कि मूल वर्ग में अंतिम रूप () ओवरराइड नहीं हो सकता है; GObject इसका ध्यान रखेगा।

यह केवल यह याद रखना है कि विध्वंसक को केवल तभी कहा जाता है जब संदर्भ काउंटर को शून्य किया जाता है:

 int main(int argc, char** argv) { AnimalCat* cat = animal_cat_new(); g_object_unref(cat); /*      */ } 

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

इसके अलावा, GObjectClass में वर्चुअल फ़ंक्शंस get_property () और set_property () शामिल हैं, जिन्हें GObject बेस प्रकार की ऐसी शक्तिशाली विशेषताओं और इसके वंशजों को अपनी वस्तुओं में गुणों के रूप में उपयोग करने के लिए पुनर्परिभाषित किया जाना चाहिए। हम इस बारे में अगले लेख में बात करेंगे।

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


All Articles