في عملية استخدام DELPHI لتطوير البرمجيات، نحن نشبه مجموعة من الأبقار والأغنام السعيدة في الأراضي العشبية، نستمتع بلا مبالاة بأشعة الشمس التي جلبتها لنا لغة Object Pascal ومحطات المياه الغنية التي توفرها عناصر تحكم VCL المختلفة. بالنظر إلى السماء الزرقاء التي لا حدود لها، والنظر إلى العشب الأخضر المورق على الأرض، من سيفكر في مدى ضخامة الكون وما هي الأشياء الأصغر من الجزيئات والذرات؟ وهذا أمر يخص الفلاسفة. في هذا الوقت كان الفيلسوف جالسا على قمة جبل عال، ينظر إلى الأعلى وينظر إلى التغيرات التي تحدث في سديم الكون، يحدق في زحف الحشرات على الأرض، ثم يعود فجأة إلى الوراء، ويومئ برأسه ويبتسم لمجموعتنا من الرعي الماشية والأغنام. التقط قطعة من العشب، وأمسكها بلطف في فمه، وأغمض عينيه وتذوقها بعناية، وأتساءل ما هو طعم قطعة العشب هذه في فم الفيلسوف؟ ومع ذلك، كان لديه دائما ابتسامة راضية على وجهه.
إن معرفة وفهم العالم الذري المجهري لـ DELPHI يمكن أن يمكّننا من فهم هيكل التطبيق العياني لـ DELPHI بشكل كامل، وبالتالي تطوير برنامجنا في مساحة أيديولوجية أوسع. يبدو الأمر كما لو أن نيوتن اكتشف حركة الأجسام العيانية، لكنه كان منزعجًا لأنه لم يتمكن من معرفة سبب تحرك الأجسام بهذه الطريقة. على العكس من ذلك، عاش أينشتاين الحياة النسبية السعيدة بين قوانين الجسيمات الأساسية وحركة الأجسام العيانية !
القسم 1 إلىذرة الكائن
ما هو كائن؟
إنها النواة الأساسية لبنية لغة Object Pascal وأصل عناصر تحكم VCL المختلفة. يمكننا أن نعتبر TObject إحدى الذرات التي تشكل تطبيق DELPHI، وهي بالطبع مكونة من جزيئات أكثر دقة مثل عناصر تركيب باسكال الأساسية.
يقال أن TObject هو ذرة برنامج DELPHI لأن TObject مدعوم داخليًا بواسطة مترجم DELPHI. جميع فئات الكائنات مشتقة من TObject، حتى إذا لم تحدد TObject كفئة أصل. يتم تعريف TObject في وحدة النظام، التي تعد جزءًا من النظام. في بداية وحدة System.pas، يوجد نص التعليق هذا:
{ثوابت وأنواع وإجراءات محددة مسبقًا}
{ والوظائف (مثل True أو Integer أو }
{Writeln) لا تحتوي على إعلانات فعلية.}
{بدلاً من ذلك، تم دمجها في المترجم}
{ويعاملون كأنهم معلنون}
{في بداية وحدة النظام.}
هذا يعني أن هذه الوحدة تحتوي على ثوابت وأنواع وإجراءات ووظائف محددة مسبقًا (مثل: True أو Integer أو Writeln) لم يتم الإعلان عنها فعليًا، ولكنها مدمجة بواسطة المترجم ويتم استخدامها في بداية التجميع يكون تعريفا مبينا. يمكنك إضافة ملفات برنامج مصدر أخرى مثل Classes.pas أو Windows.pas إلى ملف المشروع الخاص بك لتجميع وتصحيح التعليمات البرمجية المصدر، ولكن لا يمكنك مطلقًا إضافة ملف البرنامج المصدر System.pas إلى ملف مشروعك للتجميع! ستقوم DELPHI بالإبلاغ عن أخطاء التجميع للتعريفات المكررة للنظام!
لذلك، TObject هو تعريف مقدم داخليًا بواسطة المترجم. بالنسبة لأولئك منا الذين يستخدمون DELPHI لتطوير البرامج، TObject هو شيء ذري.
تعريف TObject في وحدة النظام هو كما يلي:
TObject = class
إنشاء المنشئ؛
الإجراء مجاني؛
وظيفة الفئة InitInstance(Instance: Pointer): TObject;
الإجراء CleanupInstance؛
نوع الوظيفة: TClass؛
وظيفة الفئة ClassName: ShortString؛
وظيفة الفئة ClassNameIs(const Name: string): Boolean;
وظيفة الفصل ClassParent: TClass؛
وظيفة الفئة ClassInfo: المؤشر؛
وظيفة الفئة InstanceSize: Longint؛
وظيفة الفئة InheritsFrom(AClass: TClass): Boolean;
وظيفة الفصل MethodAddress(const Name: ShortString): المؤشر;
دالة فئة اسم الطريقة (العنوان: المؤشر): ShortString؛
وظيفة FieldAddress (اسم ثابت: ShortString): المؤشر؛
وظيفة GetInterface(const IID: TGUID; out Obj): Boolean;
وظيفة الفئة GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
وظيفة الفئة GetInterfaceTable: PInterfaceTable؛
وظيفة SafeCallException(ExceptObject: TObject;
باستثناء Addr: المؤشر): HResult الظاهري؛
إجراء ما بعد البناء الظاهري؛
الإجراء قبل التدمير الظاهري؛
إرسال الإجراء (رسالة فار افتراضية) ؛
الإجراء DefaultHandler(var message);
وظيفة الفئة NewInstance: TObject;
الإجراء FreeInstance الظاهري؛
تدمير الظاهري.
نهاية؛
بعد ذلك، سوف نطرق تدريجيًا باب ذرات الجسم لنرى البنية الموجودة بداخله.
نحن نعلم أن TObject هو الفئة الأساسية لجميع الكائنات، فما هو الكائن بالضبط؟
أي كائن في DELPHI هو مؤشر يشير إلى المساحة التي يشغلها الكائن في الذاكرة! على الرغم من أن الكائن عبارة عن مؤشر، إلا أننا عندما نشير إلى أعضاء الكائن، لا نحتاج إلى كتابة الكود MyObject^.GetName، ولكن يمكننا فقط كتابة MyObject.GetName. هذا بناء جملة موسع للغة Object Pascal وهو بدعم من المترجم. الأصدقاء الذين يستخدمون C++ Builder واضحون جدًا فيما يتعلق بالعلاقة بين الكائنات والمؤشرات، لأنه يجب تعريف الكائنات في C++ Builder كمؤشرات. المكان الذي يشير إليه مؤشر الكائن هو مساحة الكائن حيث يقوم الكائن بتخزين البيانات. دعنا نحلل بنية البيانات لمساحة الذاكرة التي يشير إليها مؤشر الكائن.
تشير أول 4 بايتات من مساحة الكائن إلى جدول عناوين الطريقة الافتراضية (VMT?C Virtual Method Table) لفئة الكائن. المساحة التالية هي مساحة لتخزين بيانات الأعضاء الخاصة بالكائن نفسه، ويتم تخزينها بالترتيب الإجمالي من أعضاء البيانات في فئة السلف الأكثر بدائية للكائن إلى أعضاء البيانات في فئة الكائن، وبالترتيب الذي يتم به يتم تعريف أعضاء البيانات في كل مستوى من مستويات الفصل.
يحتوي جدول الطريقة الافتراضية (VMT) الخاص بالفئة على عناوين الإجراءات الخاصة بالطرق الافتراضية لجميع الفئات المشتقة من الفئة الأصلية للفئة. الطريقة الافتراضية للفئة هي طريقة تم الإعلان عنها بالكلمة المحجوزة Virtual الطريقة هي الآلية الأساسية لتحقيق تعدد أشكال الكائن. على الرغم من أن الأساليب الديناميكية المعلنة باستخدام الكلمة المحجوزة Dynamic يمكنها أيضًا تحقيق تعدد أشكال الكائنات، إلا أن هذه الأساليب لا يتم تخزينها في جدول عناوين الطريقة الافتراضية (VMT)، إنها مجرد طريقة أخرى يوفرها Object Pascal يمكنها توفير مساحة تخزين الفئة. ولكن على حساب سرعة الاتصال.
حتى لو لم نحدد أي طريقة افتراضية للفئة بأنفسنا، فإن كائن الفئة لا يزال لديه مؤشر إلى جدول عناوين الطريقة الافتراضية، ولكن طول إدخال العنوان هو صفر. ومع ذلك، أين يتم تخزين الأساليب الافتراضية المحددة في TObject، مثل Destroy وFreeInstance وما إلى ذلك؟ اتضح أن عناوين الطرق الخاصة بهم يتم تخزينها في مسافة إزاحة في الاتجاه السلبي بالنسبة لمؤشر VMT. في الواقع، مساحة البيانات التي يقابلها 76 بايت في الاتجاه السلبي لجدول VMT هي بنية بيانات النظام لفئة الكائن، وهي مرتبطة بالمترجم ويمكن تغييرها في إصدارات DELPHI المستقبلية.
لذلك، يمكنك الاعتقاد بأن VMT عبارة عن بنية بيانات تبدأ من مساحة عنوان الإزاحة السالبة، ومنطقة بيانات الإزاحة السالبة هي منطقة بيانات النظام لـ VMT، وبيانات الإزاحة الإيجابية لـ VMT هي منطقة بيانات المستخدم (طريقة افتراضية مخصصة). جدول العناوين). ترتبط الوظائف والإجراءات المتعلقة بمعلومات الفئة أو معلومات وقت تشغيل الكائن المحددة في TObject بشكل عام ببيانات نظام VMT.
تمثل بيانات VMT فئة في الواقع، VMT هي فئة! في Object Pascal، نستخدم معرفات مثل TObject وTComponent وما إلى ذلك لتمثيل الفئات، والتي يتم تنفيذها كبيانات VMT الخاصة بها داخليًا في DELPHI. إن نوع الفئة المحددة بفئة الكلمة المحجوزة هو في الواقع مؤشر لبيانات VMT ذات الصلة.
بالنسبة لتطبيقنا، تعد بيانات VMT بيانات ثابتة. بعد أن يقوم المترجم بتجميع تطبيقنا، يتم تحديد معلومات البيانات هذه وتهيئتها. يمكن لبيانات البرنامج التي نكتبها الوصول إلى المعلومات المتعلقة بـ VMT، أو الحصول على معلومات مثل حجم الكائن أو اسم الفئة أو بيانات سمة وقت التشغيل، أو استدعاء الأساليب الافتراضية أو قراءة اسم وعنوان الطريقة، وما إلى ذلك.
عندما يتم إنشاء كائن، سيقوم النظام بتخصيص مساحة ذاكرة للكائن وربط الكائن بالفئة ذات الصلة، لذلك تصبح البايتات الأربعة الأولى في مساحة البيانات المخصصة للكائن مؤشرات لمؤشر فئة VMT.
دعونا نلقي نظرة على كيفية ولادة الأشياء وموتها. عندما أشاهد ابني البالغ من العمر ثلاث سنوات وهو يقفز على العشب، فبالتحديد لأنني شهدت عملية ولادة الحياة أستطيع أن أفهم حقًا معنى الحياة وعظمتها. فقط أولئك الذين جربوا الموت سوف يفهمون الحياة ويعتزون بها أكثر. لذلك، دعونا نفهم عملية خلق وتدمير الأشياء!
نعلم جميعًا أنه يمكن إنشاء أبسط كائن باستخدام العبارة التالية:
AnObject := TObject.Create;
يقوم المترجم بتنفيذ التجميع الخاص به على النحو التالي:
استنادًا إلى VMT المطابق لـ TObject، قم باستدعاء مُنشئ إنشاء TObject. يستدعي مُنشئ الإنشاء عملية ClassCreate الخاصة بالنظام، وتستدعي عملية ClassCreate الخاصة بالنظام الأسلوب الظاهري NewInstance من خلال فئة VMT المخزنة فيه. الغرض من استدعاء أسلوب NewInstance هو إنشاء مساحة مثيل للكائن، نظرًا لأننا لم نقم بتحميل هذه الطريقة بشكل زائد، فهي تمثل NewInstance لفئة TObject. سوف يقوم أسلوب NewInstance لفئة TObjec باستدعاء إجراء GetMem لتخصيص الذاكرة للكائن بناءً على حجم مثيل الكائن (InstanceSize) الذي تمت تهيئته بواسطة المترجم في جدول VMT، ثم استدعاء أسلوب InitInstance لتهيئة المساحة المخصصة. تقوم طريقة InitInstance أولاً بتهيئة أول 4 بايتات من مساحة الكائن كمؤشر إلى VMT المطابق لفئة الكائن، ثم تقوم بمسح المساحة المتبقية. بعد إنشاء مثيل الكائن، يتم أيضًا استدعاء الطريقة الافتراضية AfterConstruction. أخيرًا، احفظ مؤشر عنوان بيانات مثيل الكائن في متغير AnObject، وبهذه الطريقة يتم إنشاء كائن AnObject.
وبالمثل، يمكن تدمير كائن باستخدام العبارة التالية:
AnObject.Destroy;
تم الإعلان عن أداة تدمير TObject، Destroy، كطريقة افتراضية، وهي أيضًا إحدى الطرق الافتراضية المتأصلة في النظام. تستدعي الطريقة Destory أولاً الطريقة الافتراضية BeforeDestruction، ثم تستدعي عملية ClassDestroy الخاصة بالنظام. تستدعي عملية ClassDestory أسلوب FreeInstance الظاهري من خلال فئة VMT، وتستدعي طريقة FreeInstance عملية FreeMem لتحرير مساحة ذاكرة الكائن. وبهذه الطريقة، يختفي الكائن من النظام.
إن عملية تدمير الأشياء أبسط من عملية بناء الأشياء، تمامًا كما أن ولادة الحياة هي عملية حمل طويلة، لكن الموت قصير العمر نسبيًا، ويبدو أن هذه قاعدة حتمية.
أثناء عملية إنشاء الكائن وتدميره، يتم استدعاء وظيفتين ظاهريتين، NewInstance وFreeInstance، لإنشاء مساحة الذاكرة الخاصة بمثيل الكائن وتحريرها. السبب وراء إعلان هاتين الوظيفتين كوظائف افتراضية هو السماح للمستخدمين بالحصول على مساحة للتوسع عند كتابة فئات كائنات خاصة تتطلب من المستخدمين إدارة ذاكرتهم الخاصة (كما هو الحال في بعض برامج التحكم الصناعية الخاصة).
إن الإعلان عن AfterConstruction و BeforeDestruction كوظائف افتراضية هو أيضًا إعطاء الفئة المشتقة في المستقبل الفرصة للسماح للكائن المولود حديثًا بتنفس أول نفس من الهواء النقي بعد إنشاء الكائن، والسماح للكائن بإكمال التأثير قبل أن يموت الكائن هذا كل شيء منطقي. في الواقع، يتم تشغيل حدث OnCreate وحدث OnDestroy لكائن TForm وكائن TDataModule على التوالي في عمليتي الوظيفة الافتراضية المتمثلة في التحميل الزائد لـ TForm وTDataModule.
بالإضافة إلى ذلك، يوفر TObjec أيضًا طريقة مجانية، وهي ليست طريقة افتراضية، ويتم توفيرها خصيصًا لتحرير الكائن بأمان عندما يكون من غير الواضح ما إذا كان الكائن فارغًا (لا شيء). في الواقع، إذا لم تتمكن من معرفة ما إذا كان الكائن فارغًا، فهناك مشكلة عدم وضوح منطق البرنامج. ومع ذلك، لا يوجد أحد مثالي ويمكن أن يرتكب الأخطاء، ومن الجيد أيضًا استخدام التطبيق المجاني لتجنب الأخطاء العرضية. لكن كتابة البرامج الصحيحة لا يمكن أن تعتمد فقط على مثل هذه الحلول، فالهدف الأول من البرمجة يجب أن يكون التأكد من الصحة المنطقية للبرنامج!
يمكن للأصدقاء المهتمين قراءة الكود الأصلي لوحدة النظام، حيث تتم كتابة كمية كبيرة من الكود بلغة التجميع. يمكن للأصدقاء الحذرين أن يجدوا أن مُنشئ TObject و Destructor Destory لم يكتبوا أي تعليمات برمجية في الواقع، من خلال نافذة Debug CPU في حالة التصحيح، يمكن أن ينعكس رمز التجميع الخاص بالإنشاء والتدمير بوضوح. لأن الأساتذة الذين أنشأوا DELPHI لم يرغبوا في تزويد المستخدمين بالكثير من الأشياء المعقدة، لقد أرادوا من المستخدمين كتابة التطبيقات بناءً على مفاهيم بسيطة وإخفاء العمل المعقد داخل النظام ليقوموا به. لذلك، عند نشر وحدة System.pas، تتم إزالة رموز هاتين الوظيفتين خصيصًا لجعل المستخدمين يعتقدون أن TObject هو مصدر كل الأشياء، وتبدأ الفئات المشتقة من المستخدم تمامًا من العدم، وهذا ليس خطأ في حد ذاته. على الرغم من أن قراءة هذه الأكواد الأساسية لـ DELPHI تتطلب قدرًا صغيرًا من المعرفة بلغة التجميع، إلا أن قراءة مثل هذه الأكواد يمكن أن تمنحنا فهمًا أعمق لأصل وتطور عالم DELPHI. حتى لو كنت لا تفهم الكثير، فإن قدرتك على فهم بعض الأشياء الأساسية على الأقل ستساعدنا كثيرًا في كتابة برامج DELPHI.
القسم 2 TClass ذرة
في وحدة System.pas، يتم تعريف TClass على النحو التالي:
TClass = فئة TObject؛
وهذا يعني أن TClass هي فئة TObject. نظرًا لأن TObject نفسه عبارة عن فئة، فإن TClass هو ما يسمى بفئة الفئات.
من الناحية النظرية، TClass هو نوع من الفئة، أي فئة. ومع ذلك، نحن نعلم أن فئة DELPHI تمثل جزءًا من بيانات VMT. لذلك، يمكن اعتبار الفئة النوع المحدد لعنصر بيانات VMT، وهو في الواقع نوع مؤشر يشير إلى بيانات VMT!
في لغة C++ التقليدية السابقة، لم يكن من الممكن تحديد نوع الفصل. بمجرد تجميع الكائن، يتم إصلاحه، ويتم تحويل المعلومات الهيكلية للفئة إلى رمز الجهاز المطلق، ولن تكون معلومات الفئة الكاملة موجودة في الذاكرة. يمكن لبعض اللغات الموجهة للكائنات ذات المستوى الأعلى أن تدعم الوصول الديناميكي واستدعاء معلومات الفئة، ولكنها غالبًا ما تتطلب آلية تفسير داخلية معقدة والمزيد من موارد النظام. تمتص لغة Object Pascal من DELPHI بعض الميزات الممتازة للغات عالية المستوى الموجهة للكائنات، مع الاحتفاظ بالميزة التقليدية المتمثلة في تجميع البرامج مباشرة في كود الآلة، مما يحل بشكل مثالي مشاكل الوظائف المتقدمة وكفاءة البرنامج.
يرجع ذلك على وجه التحديد إلى أن DELPHI تحتفظ بمعلومات الفئة الكاملة في التطبيق، حيث يمكنها توفير وظائف متقدمة موجهة للكائنات مثل تحويل الفئات وتحديدها في وقت التشغيل، حيث تلعب بيانات VMT الخاصة بالفئة دورًا أساسيًا رئيسيًا. يمكن للأصدقاء المهتمين قراءة عمليتي تجميع AsClass وIsClass في وحدة النظام، وهما رموز التنفيذ الخاصة بـ as وis لتعميق فهمهم للفئات وبيانات VMT.
...
يتضمن المحتوى التالي أيضًا فهم المنشئين الوهميين وآلية تنفيذ الواجهة وآلية تنفيذ معالجة الاستثناءات وما إلى ذلك. المبادئ الأساسية لـ DLPHI. آمل أن أتمكن من الانتهاء منه بعد عيد العمال وأساهم به للجميع.