في Object Pascal، يتم إنشاء جميع الكائنات في مساحة كومة الذاكرة المؤقتة بدلاً من المكدس، لذلك لن يتم استدعاء المُنشئ تلقائيًا بواسطة المترجم مثل C++. إن بناء الأشياء وتدمير الأشياء هي مسؤولية المبرمج.
لإنشاء كائن، يجب عليك أولاً تخصيص ذاكرة للكائن. هذه الخطوة مدعومة من قبل المترجم في Object Pascal - ما يسمى "Compiler Magic". Magic)"، لا يحتاج المبرمج إلى المشاركة في هذه العملية؛ فيجب تهيئة أعضاء بيانات الكائن، وسيكون المترجم مسؤولاً عن "مسحها"، ولكن إذا كانت هناك مهمة خاصة، فيمكن إكمالها في المنشئ، يجب تحرير الموارد المطلوبة (وليس الذاكرة التي يشغلها الكائن نفسه) عند تدمير الكائن، وتقع على عاتق المدمر أيضًا عملية إعادة تدوير الذاكرة التي يشغلها الكائن نفسه "سحر المترجم".
تخصيص وإعادة تدوير ذاكرة الكائن
عندما يقوم المترجم بتخصيص ذاكرة لكائن ما، فإن الدعم الذي يقدمه هو إدراج هذه الأسطر من كود التجميع قبل استدعاء المنشئ:
اختبار دل، دل
جي زي + 08 دولار
أضف esp، - 10 دولارات
اتصل بـClassCreate // انتبه إلى هذا السطر من التعليمات البرمجية
يستدعي السطر الأخير من الكود أعلاه الدالة _ClassCreate في السطر 8949 من ملف system.pas (الخاضع لـ Delphi 6). تقوم هذه الدالة بتخصيص الذاكرة المناسبة لكل كائن على وجه التحديد. بعد اكتمال تخصيص الذاكرة، يتم استدعاء منشئ الفئة لتهيئة أعضاء البيانات. بعد ذلك، سيقوم المترجم بإدراج الأسطر التالية من كود التجميع:
اختبار دل، دل
جيز +$0f
اتصل بـAfterConstruction
البوب dWord ptr fs:[00000000 دولار]
أضف esp، $0c
المهمة الرئيسية هي استدعاء AfterConstruction لكل مثيل كائن. هذا الاستدعاء ليس له أي فائدة في دلفي. وجوده محجوز لـ C++ Builder.
وبالمثل، عند تدمير كائن ما، يجب أولاً استدعاء مدمر الفئة لتحرير الموارد التي يطلبها الكائن. بعد ذلك، يتم إعادة تدوير مساحة الذاكرة التي يشغلها الكائن نفسه. يكتمل هذا العمل عن طريق قيام المترجم بإدخال رمز التجميع التالي بعد استدعاء المدمر:
اتصل بـBeforeDestruction
اختبار دل، دل
سعر +05 دولار
اتصل بـClassDestroy
يتوافق العمل الذي يتم تنفيذه بواسطة هذه الرموز مع ما يتم تنفيذه عند إنشاء الكائن وتخصيص الذاكرة، وبشكل أساسي استدعاء وظيفة _ClassDestroy على السطر 8997 في system.pas.
البناء والمدمر
لتعريف مُنشئ، استخدم الكلمة الأساسية للمنشئ وفقًا للاتفاقية، اسم المُنشئ هو إنشاء (بالطبع يمكن استخدام أسماء أخرى، لكن هذا ليس تصميمًا جيدًا بأي حال من الأحوال!). يحب:
يكتب
TMyFamily = class // الفصل المحدد لعائلتك
خاص
FMyFatherName: سلسلة // اسم والدك
FMyMotherName: سلسلة // اسم والدتك
… // أفراد عائلتك الآخرين
عام
إنشاء المُنشئ (strFatherName، strMotherName : String)؛
…… // طرق أخرى
نهاية؛
قد تتساءل، إذا لم أقدم مُنشئًا لفصلي، فهل يمكن إنشاء كائناته؟ الجواب هو: نعم. لقد تم ذكر السبب من قبل، حيث يتم إكمال تخصيص الذاكرة التي يشغلها الكائن نفسه بواسطة المترجم. وبما أن جميع الفئات في Object Pascal (باستثناء فئة TObject نفسها) مشتقة من فئة TObject، فسيقوم المترجم باستدعاء مُنشئ TObject.Create()، لكن هذه الوظيفة هي وظيفة فارغة، ولن تؤثر على فئة TMyFamily عند تهيئة أعضاء البيانات (FMyFatherName، FMyMotherName)، سيتم مسحهم تلقائيًا إلى سلاسل فارغة (أي '')، لأن TObject.Create() لا يعرف والدك أو والدتك على الإطلاق!
عند إنشاء كائن، يتم استدعاء المنشئ مباشرة، بالشكل التالي:
MyFamilyObject := TMyFamily.Create('Zhang', 'Li');
استخدم الكلمة الأساسية Destructor لتعريف أداة التدمير حسب الاصطلاح، يُطلق على أداة التدمير اسم Destroy. يحب:
يكتب
TMyClass = class
عام
تجاوز المدمر () ؛
نهاية؛
السبب وراء إضافة بيان التجاوز في نهاية إعلان المدمر هو التأكد من إمكانية تدمير الكائن بشكل صحيح في حالة تعدد الأشكال (ستتم مناقشة تعدد الأشكال بالتفصيل في القسم 2.4). إذا لم تقم بإضافة الكلمة الأساسية للتجاوز، فسيقدم المترجم تحذيرًا مشابهًا لـ "الطريقة 'Destroy' تخفي الطريقة الافتراضية للنوع الأساسي 'TObject'". يعني التحذير أن التدمير الذي حددته يخفي الطريقة الافتراضية TObject.Destroy() للفئة الأساسية. في هذه الحالة، لا يمكن إتلاف الكائن بشكل صحيح في المواقف متعددة الأشكال.
ملاحظة: يجب الإعلان عن المدمرات ببيان تجاوز.
وبالمثل، إذا لم تكن هناك موارد خاصة تحتاج إلى إصدارها في فصلك الدراسي، فلن تحتاج إلى تحديد أداة إتلاف. ومع ذلك، عند تدمير كائن، يجب عليك استدعاء الأسلوب Free() الخاص بالكائن بدلاً من استدعاء Destroy() مباشرة.
MyFamilyObject.Free();
وذلك لأن الأسلوب Free() سيحدد ما إذا كان الكائن نفسه صفراً، وإذا لم يكن صفراً، فسيتم استدعاء Destroy() للكائن لزيادة الأمان. والآن بعد أن أصبحت هناك طرق أكثر أمانًا للقيام بذلك، فمن المؤكد أنه لا يوجد سبب لعدم القيام بذلك.
ملاحظة: لا تستدعي Destroy() مباشرة على كائن ما، بل تستدعي Free() بدلًا من ذلك.
يمكن أن نستنتج من هذا أنه في Object Pascal ما عليك سوى الاهتمام بتخصيص وإطلاق الموارد التي يطبقها الكائن، ولا يتعين عليك الاهتمام بالمساحة التي يشغلها الكائن نفسه!