في عملية استخدام 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 كمؤشرات. المكان الذي يشير إليه مؤشر الكائن هو مساحة الكائن حيث يقوم الكائن بتخزين البيانات. دعنا نحلل بنية البيانات لمساحة الذاكرة التي يشير إليها مؤشر الكائن.
تشير البايتات الأربع الأولى من مساحة الكائن إلى جدول عناوين الطريقة الافتراضية (VMT - جدول الطريقة الافتراضية) لفئة الكائن. المساحة التالية هي مساحة لتخزين بيانات الأعضاء الخاصة بالكائن نفسه، ويتم تخزينها بالترتيب الإجمالي من أعضاء البيانات في فئة السلف الأكثر بدائية للكائن إلى أعضاء البيانات في فئة الكائن، وبالترتيب الذي يتم به يتم تعريف أعضاء البيانات في كل مستوى من مستويات الفصل.
يحتوي جدول الطريقة الافتراضية (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.
عالم دلفي الذري (2)
الكلمات المفتاحية: ضوابط دلفي المتنوعة
القسم 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.
مع نوع الفئة، يمكنك استخدام الفئة كمتغير. يمكن فهم متغير الفئة على أنه كائن خاص، ويمكنك الوصول إلى توابع متغير الفئة تمامًا مثل الكائن. على سبيل المثال: دعونا نلقي نظرة على جزء البرنامج التالي:
يكتب
TsampleClass = فئة TsampleObject؛
TsampleObject = الفئة (TObject)
عام
إنشاء المنشئ؛
تدمير المدمرة؛
وظيفة الفئة GetSampleObjectCount:Integer;
الإجراء GetObjectIndex:Integer؛
نهاية؛
فار
aSampleClass : TSampleClass;
كلاس: TClass؛
في هذا الكود، نحدد فئة TSampleObject ونوع الفئة المرتبطة بها TsampleClass، بالإضافة إلى متغيري الفئة aSampleClass وaClass. بالإضافة إلى ذلك، قمنا أيضًا بتعريف مُنشئ ومدمر وطريقة فئة GetSampleObjectCount وطريقة كائن GetObjectIndex لفئة TsampleObject.
أولاً، دعونا نفهم معنى متغيرات الفئة aSampleClass وaClass.
من الواضح أنه يمكنك التعامل مع TsampleObject و TObject كقيم ثابتة وتعيينهما لمتغيرات aClass، تمامًا مثل تعيين 123 قيمة ثابتة للمتغير الصحيح i. ولذلك فإن العلاقة بين أنواع الفئات والفئات ومتغيرات الفئة هي العلاقة بين الأنواع والثوابت والمتغيرات، ولكن على مستوى الفئة وليس على مستوى الكائن. بالطبع، ليس من القانوني تعيين TObject مباشرة إلى aSampleClass، لأن aSampleClass هو متغير فئة من فئة TSampleObject المشتقة من TObject، ولا يحتوي TObject على كافة التعريفات المتوافقة مع نوع TSampleClass. على العكس من ذلك، من القانوني تعيين TsampleObject لمتغير aClass، لأن TsampleObject هي فئة مشتقة من TObject ومتوافقة مع نوع TClass. وهذا مشابه تمامًا لعلاقة مطابقة التعيين والنوع لمتغيرات الكائن.
ثم دعونا نلقي نظرة على ما هي الأساليب الصفية.
تشير طريقة الفئة المزعومة إلى الطريقة التي يتم استدعاؤها على مستوى الفئة، مثل طريقة GetSampleObjectCount المحددة أعلاه، وهي طريقة تم الإعلان عنها بفئة الكلمات المحجوزة. تختلف أساليب الفئة عن أساليب الكائنات التي يتم استدعاؤها على مستوى الكائن. في تعريف TObject، يمكننا العثور على عدد كبير من أساليب الفئة، مثل ClassName، ClassInfo، NewInstance، وما إلى ذلك. من بينها، يتم تعريف NewInstance أيضًا على أنه افتراضي، أي طريقة فئة افتراضية. هذا يعني أنه يمكنك إعادة كتابة طريقة تنفيذ NewInstance في فئة فرعية مشتقة لإنشاء مثيلات كائن لتلك الفئة بطريقة خاصة.
يمكنك أيضًا استخدام المعرف self في أساليب الفصل، لكن معناه يختلف عن self في أساليب الكائنات. تمثل الذات في أسلوب الفصل فئتها الخاصة، أي المؤشر إلى VMT، بينما تمثل الذات في أسلوب الكائن الكائن نفسه، أي المؤشر إلى مساحة بيانات الكائن. على الرغم من أنه لا يمكن استخدام أساليب الفئة إلا على مستوى الفئة، إلا أنه لا يزال بإمكانك استدعاء أساليب الفئة من خلال كائن. على سبيل المثال، يمكن استدعاء أسلوب الفئة ClassName للكائن TObject من خلال العبارة aObject.ClassName، لأن البايتات الأربعة الأولى في مساحة بيانات الكائن التي يشير إليها مؤشر الكائن هي مؤشرات للفئة VMT. على العكس من ذلك، لا يمكنك استدعاء أساليب الكائن على مستوى الفئة، ويجب أن تكون عبارات مثل TObject.Free غير قانونية.
تجدر الإشارة إلى أن المنشئ هو أسلوب فئة، والمدمر هو أسلوب كائن!
ماذا؟ البناة هي أساليب فئة والمدمرات هي أساليب كائن! هل كان هناك أي خطأ؟
كما ترى، عندما تقوم بإنشاء كائن، فمن الواضح أنك تستخدم عبارة مشابهة لما يلي:
aObject := TObject.Create;
من الواضح أنه يستدعي طريقة إنشاء فئة TObject. عند حذف كائن، استخدم العبارة التالية:
aObject.Destroy;
حتى إذا كنت تستخدم الأسلوب المجاني لتحرير الكائن، يتم استدعاء الأسلوب تدمير الكائن بشكل غير مباشر.
السبب بسيط للغاية. قبل إنشاء الكائن، لم يكن الكائن موجودًا بعد، فقط الفئة موجودة. على العكس من ذلك، فإن حذف كائن يجب أن يؤدي إلى حذف الكائن الموجود، وليس الفئة.
أخيرًا، دعونا نناقش مسألة المنشئين الوهميين.
في لغة C++ التقليدية، يمكن تنفيذ أدوات التدمير الافتراضية، لكن تنفيذ المنشئات الافتراضية يمثل مشكلة صعبة. لأنه في لغة C++ التقليدية، لا توجد أنواع من الفئات. توجد مثيلات للكائنات العامة في مساحة البيانات العالمية في وقت الترجمة، كما أن كائنات الوظائف المحلية هي أيضًا مثيلات تم تعيينها في مساحة المكدس في وقت الترجمة، حتى أن الكائنات التي تم إنشاؤها ديناميكيًا يتم وضعها في بنية الفئة الثابتة باستخدام المثيل المخصص في مساحة الكومة، والمنشئ هو مجرد أسلوب كائن يقوم بتهيئة مثيل الكائن الذي تم إنشاؤه. لا توجد أساليب فئة حقيقية في لغة C ++ التقليدية. حتى لو كان من الممكن تعريف ما يسمى بالطرق القائمة على الفئات الثابتة، فسيتم تنفيذها في النهاية كوظيفة عالمية خاصة، ناهيك عن أساليب الطبقة الافتراضية التي يمكنها استهداف كائن معين فقط الحالات. لذلك، تعتقد لغة C++ التقليدية أنه قبل إنشاء مثيل كائن معين، من المستحيل إنشاء الكائن نفسه بناءً على الكائن المراد إنشاؤه. وهو أمر مستحيل بالفعل، لأن هذا من شأنه أن يخلق مفارقة متناقضة في المنطق!
ومع ذلك، فإنه على وجه التحديد بسبب المفاهيم الأساسية لمعلومات نوع الفصل الديناميكي، وأساليب الفصل الافتراضية الحقيقية، والمنشئات التي يتم تنفيذها بناءً على الفئات في DELPHI، يمكن تنفيذ المنشئات الافتراضية. يتم إنتاج الأشياء عن طريق الفصول الدراسية. الكائن يشبه طفلًا ينمو، والطبقة هي أمه. ولا يعرف الطفل نفسه نوع الشخص الذي سيصبح عليه في المستقبل، لكن الأمهات يستخدمن أساليبهن التعليمية الخاصة لتربية أطفال مختلفات. أيها الناس، المبادئ واحدة.
في تعريف فئة TComponent، يتم تعريف المُنشئ على أنه افتراضي بحيث يمكن لأنواع مختلفة من عناصر التحكم تنفيذ طرق البناء الخاصة بها. هذه هي عظمة المفاهيم مثل الفئات التي أنشأتها TClass، وكذلك عظمة DELPHI.
.................................................. ..
الفصل 3 عرض الزمان والمكان في WIN32
نظر والدي العجوز إلى حفيده الصغير وهو يلعب بالألعاب على الأرض، ثم قال لي: "هذا الطفل يشبهك تمامًا عندما كنت طفلاً. يحب تفكيك الأشياء ولا يتوقف إلا بعد رؤيتها حتى النهاية. " عندما أفكر في طفولتي، غالبًا ما كنت أقوم بتفكيك سيارات الألعاب، وساعات المنبه الصغيرة، وصناديق الموسيقى، وما إلى ذلك، وكثيرًا ما كانت أمي توبخني.
في المرة الأولى التي فهمت فيها المبادئ الأساسية لأجهزة الكمبيوتر، كان الأمر يتعلق بصندوق الموسيقى الذي قمت بتفكيكه. كان ذلك في كتاب هزلي عندما كنت في المدرسة الثانوية. كان رجل عجوز ذو لحية بيضاء يشرح نظرية الآلات الذكية، وكان عمه ذو الشارب يتحدث عن أجهزة الكمبيوتر وصناديق الموسيقى. وقالوا إن وحدة المعالجة المركزية للكمبيوتر هي صف من قصب الموسيقى المستخدمة للنطق في صندوق الموسيقى، وبرنامج الكمبيوتر عبارة عن نتوءات مكتظة بكثافة على الأسطوانة الصغيرة في صندوق الموسيقى، ويعادل دوران الأسطوانة الصغيرة إلى دوران وحدة المعالجة المركزية، وهي الحركة الطبيعية لمؤشر التعليمات، بينما تتحكم النتوءات التي تمثل الموسيقى على الأسطوانة الصغيرة في اهتزاز القصبة الموسيقية لتنتج تعليمات تعادل تنفيذ البرنامج بواسطة المعالج المركزي. يصدر صندوق الموسيقى لحنًا جميلاً، يتم تشغيله وفقًا للنوتة الموسيقية المحفورة على الأسطوانة الصغيرة بواسطة الحرفي. يكمل الكمبيوتر معالجة معقدة بناءً على البرنامج المبرمج مسبقًا بواسطة المبرمج. بعد أن ذهبت إلى الكلية، علمت أن الرجل العجوز ذو اللحية البيضاء هو العملاق العلمي تورينج، وقد عززت نظريته عن الآلات المحدودة تطور ثورة المعلومات بأكملها، وكان العم ذو الشارب هو أبو أجهزة الكمبيوتر، فون نيومان. لا تزال هندسة الكمبيوتر هي الهيكل المعماري الرئيسي لأجهزة الكمبيوتر. لم يتم تفكيك صندوق الموسيقى عبثا، يمكن للأم أن تطمئن.
فقط من خلال الفهم البسيط والعميق يمكننا إنشاء إبداعات عميقة وموجزة.
سنناقش في هذا الفصل المفاهيم الأساسية المتعلقة ببرمجتنا في نظام التشغيل Windows 32 بت وإنشاء العرض الصحيح للزمان والمكان في WIN32. آمل أنه بعد قراءة هذا الفصل، يمكننا الحصول على فهم أعمق للبرامج والعمليات والخيوط، وفهم مبادئ الملفات القابلة للتنفيذ ومكتبات الارتباط الديناميكي وحزم وقت التشغيل، ورؤية الحقيقة بوضوح حول البيانات العالمية والبيانات المحلية والمعلمات في الذاكرة .
القسم 1 فهم العملية
لأسباب تاريخية، نشأ نظام Windows من DOS. في عصر DOS، كان لدينا دائمًا مفهوم البرنامج فقط، وليس مفهوم العملية. في ذلك الوقت، كانت أنظمة التشغيل العادية فقط، مثل UNIX وVMS، هي التي كانت تمتلك مفهوم العمليات، وكانت العمليات المتعددة تعني أجهزة كمبيوتر صغيرة ومحطات طرفية ومستخدمين متعددين، وهو ما يعني أيضًا المال. في معظم الأوقات، لم أتمكن من استخدام سوى أجهزة الكمبيوتر الصغيرة وأنظمة DOS الرخيصة نسبيًا، ولم أبدأ في التعامل مع العمليات وأجهزة الكمبيوتر الصغيرة إلا عندما كنت أدرس أنظمة التشغيل.
ولم يحدث ذلك إلا بعد Windows 3. في الماضي، في نظام DOS، كان من الممكن تنفيذ برنامج واحد فقط في نفس الوقت، ولكن في نظام Windows، كان من الممكن تنفيذ برامج متعددة في نفس الوقت. أثناء تشغيل برنامج في نظام DOS، لا يمكن تنفيذ نفس البرنامج في نفس الوقت، ولكن في نظام Windows، يمكن تشغيل أكثر من نسختين من نفس البرنامج في نفس الوقت، وكل نسخة قيد التشغيل من البرنامج عبارة عن عملية. لنكون أكثر دقة، كل تشغيل لأي برنامج يولد مهمة، وكل مهمة هي عملية.
عندما يتم فهم البرامج والعمليات معًا، يمكن اعتبار كلمة برنامج تشير إلى أشياء ثابتة. البرنامج النموذجي عبارة عن تعليمات برمجية ثابتة وبيانات مكونة من ملف EXE أو ملف EXE بالإضافة إلى عدة ملفات DLL. العملية هي تشغيل برنامج، وهو عبارة عن تعليمات برمجية وبيانات متغيرة ديناميكيًا تعمل ديناميكيًا في الذاكرة. عندما يكون هناك حاجة إلى برنامج ثابت للتشغيل، سيوفر نظام التشغيل مساحة معينة من الذاكرة لهذه العملية، وينقل رمز البرنامج الثابت والبيانات إلى مساحات الذاكرة هذه، ويعيد وضع رمز البرنامج والبيانات وتعيينها في هذه المساحة تنفيذها في الداخل، وبالتالي خلق عملية ديناميكية.
تشغيل نسختين من نفس البرنامج في نفس الوقت يعني وجود مساحتين للعمليات في ذاكرة النظام، لكن وظائف البرنامج الخاصة بهما هي نفسها، لكنهما في حالات مختلفة تتغير ديناميكيًا.
من حيث وقت تشغيل العملية، يتم تنفيذ كل عملية في نفس الوقت، ويطلق على المصطلح المهني التنفيذ المتوازي أو التنفيذ المتزامن. ولكن هذا هو في الأساس الشعور السطحي الذي يمنحنا إياه نظام التشغيل. في الواقع، يتم تنفيذ كل عملية بطريقة مشاركة الوقت، أي أن كل عملية تتناوب وتشغل وقت وحدة المعالجة المركزية لتنفيذ تعليمات البرنامج الخاصة بالعملية. بالنسبة لوحدة المعالجة المركزية، يتم تنفيذ تعليمات عملية واحدة فقط في نفس الوقت. نظام التشغيل هو المتلاعب وراء تشغيل العملية المجدولة، فهو يقوم باستمرار بحفظ وتبديل الحالة الحالية لكل عملية يتم تنفيذها في وحدة المعالجة المركزية، بحيث تعتقد كل عملية مجدولة أنها تعمل بشكل كامل ومستمر. نظرًا لأن جدولة العمليات المشتركة بالوقت سريعة جدًا، فهذا يعطينا الانطباع بأن العمليات كلها تعمل في نفس الوقت. في الواقع، التشغيل المتزامن الحقيقي يكون ممكنًا فقط في بيئة أجهزة متعددة وحدات المعالجة المركزية (CPU). عندما نتحدث عن الخيوط لاحقًا، سنجد أن الخيوط هي التي تحرك العملية حقًا، والأهم من ذلك أنها توفر مساحة للعملية.
من حيث المساحة التي تشغلها العملية، فإن كل مساحة عملية مستقلة نسبيًا، وكل عملية تعمل في مساحة مستقلة خاصة بها. يتضمن البرنامج كلاً من مساحة التعليمات البرمجية ومساحة البيانات. يشغل كل من التعليمات البرمجية والبيانات مساحة العملية. يخصص Windows الذاكرة الفعلية لمساحة البيانات التي تتطلبها كل عملية، ويستخدم بشكل عام أساليب المشاركة لمساحة التعليمات البرمجية، مما يؤدي إلى تعيين رمز واحد من البرنامج إلى عمليات متعددة للبرنامج. وهذا يعني أنه إذا كان البرنامج يحتوي على 100 كيلو من التعليمات البرمجية ويتطلب 100 كيلو من مساحة البيانات، مما يعني أنه مطلوب إجمالي 200 كيلو من مساحة العملية، فسيخصص نظام التشغيل 200 كيلو من مساحة العملية في المرة الأولى التي يتم فيها تشغيل البرنامج، و200 كيلو من مساحة العملية سيتم تخصيص المساحة في المرة الثانية التي يتم فيها تشغيل البرنامج. عند بدء العملية، يخصص نظام التشغيل 100 كيلو بايت فقط من مساحة البيانات، بينما تشارك مساحة التعليمات البرمجية مساحة العملية السابقة.
ما ورد أعلاه هو عرض الوقت والمكان الأساسي للعملية في نظام التشغيل Windows. في الواقع، هناك اختلاف كبير في عرض الوقت والمكان للعملية بين نظامي التشغيل Windows 16 بت و32 بت.
من حيث الوقت، فإن إدارة العمليات لأنظمة تشغيل Windows 16 بت، مثل Windows 3.x، بسيطة للغاية، وهي في الواقع مجرد نظام تشغيل لإدارة المهام المتعددة. علاوة على ذلك، تكون جدولة مهام نظام التشغيل سلبية. إذا لم تتوقف المهمة عن معالجة الرسالة، فيجب على نظام التشغيل الانتظار. نظرًا للعيوب الموجودة في إدارة العمليات لنظام Windows 16 بت، فعند تشغيل العملية، فإنها تشغل موارد وحدة المعالجة المركزية بالكامل. في تلك الأيام، لكي تتاح لـ Windows 16 بت فرصة لجدولة مهام أخرى، أشادت Microsoft بمطوري تطبيقات Windows لكونهم مبرمجين واسعي الأفق، لذلك كانوا على استعداد لكتابة بضعة أسطر إضافية من التعليمات البرمجية كهدية نظام التشغيل. على العكس من ذلك، تتمتع أنظمة التشغيل WIN32، مثل Windows 95 وNT، بقدرات نظام تشغيل متعددة العمليات ومتعددة المهام. تتم جدولة العملية في WIN32 بالكامل بواسطة نظام التشغيل بمجرد انتهاء الشريحة الزمنية للعملية قيد التشغيل، سيتحول نظام التشغيل بشكل فعال إلى العملية التالية بغض النظر عما إذا كانت العملية لا تزال تقوم بمعالجة البيانات. بالمعنى الدقيق للكلمة ، لا يمكن اعتبار نظام تشغيل Windows 16 بت بمثابة نظام تشغيل كامل ، ولكن نظام التشغيل Win32 32 بت هو نظام التشغيل الحقيقي. بالطبع ، لن تقول Microsoft أن Win32 تعوض عن أوجه القصور في النوافذ 16 بت ، ولكنها تدعي أن Win32 تنفذ تقنية متقدمة تسمى "تعدد المهام الاستباقية" ، وهي طريقة تجارية.
من منظور الفضاء ، على الرغم من أن مساحة العملية في نظام تشغيل Windows 16 بت مستقلة نسبيًا ، يمكن للعمليات الوصول بسهولة إلى مساحة بيانات بعضها البعض. نظرًا لأن هذه العمليات هي في الواقع قطاعات بيانات مختلفة في نفس المساحة المادية ، ويمكن أن تتسبب عمليات العناوين غير السليمة بسهولة في قراءة وكتابة مساحة غير صحيحة ، وتعطل نظام التشغيل. ومع ذلك ، في نظام التشغيل Win32 ، كل مساحة عملية مستقلة تمامًا. يوفر Win32 كل عملية مساحة عنوان افتراضية ومستمرة تصل إلى 4G. تعني مساحة العناوين المستمرة المزعومة أن كل عملية لها مساحة عنوان من 00000000 دولار إلى $ fffffff ، بدلاً من المساحة المجزأة من النوافذ 16 بت. في Win32 ، لا داعي للقلق بشأن القراءة والكتابة العمليات التي تؤثر عن غير قصد على البيانات في مساحات العمليات الأخرى ، ولا داعي للقلق بشأن العمليات الأخرى التي تأتي لمضايقة عملك. في الوقت نفسه ، فإن المساحة الافتراضية 4G المستمرة التي توفرها WIN32 هي الذاكرة الفعلية التي تم تعيينها لك من قبل نظام التشغيل بدعم من الأجهزة. الذاكرة المادية.
القسم 2 مساحة العملية
عندما نستخدم Delphi لكتابة تطبيقات Win32 ، نادراً ما نهتم بالعالم الداخلي للعملية عند تشغيله. نظرًا لأن WIN32 يوفر 4G من مساحة العملية الظاهرية المستمرة لعملية لدينا ، فربما يستخدم تطبيق أكبر في العالم حاليًا جزءًا منه فقط. يبدو أن مساحة العملية غير محدودة ، لكن مساحة عملية 4G افتراضية ، وقد تكون الذاكرة الفعلية لآلةك بعيدة عن ذلك. على الرغم من أن العملية لديها مساحة شاسعة ، إلا أن بعض برامج الخوارزمية المعقدة ستظل قادرة على التشغيل بسبب تجاوز الفائض ، وخاصة البرامج التي تحتوي على عدد كبير من الخوارزميات العودية.
لذلك ، فإن الفهم المتعمق لهيكل مساحة عملية 4G ، وعلاقتها بالذاكرة المادية ، وما إلى ذلك ، ستساعدنا نظرة العالم والمنهجية لحل مختلف المشاكل الصعبة.
بعد ذلك ، سوف نستخدم تجربة بسيطة لفهم العالم الداخلي لعملية WIN32. قد يتطلب هذا بعض المعرفة بسجلات الكأس ولغة التجميع ، لكنني حاولت شرحها بلغة بسيطة.
عند بدء تشغيل Delphi ، سيتم إنشاء مشروع Project1 تلقائيًا ، وسنبدأ به. قم بتعيين نقطة توقف في أي مكان في البرنامج الأصلي لـ Project1.dpr ، على سبيل المثال ، قم بتعيين نقطة توقف في الجملة Begin. ثم قم بتشغيل البرنامج وسيتوقف تلقائيًا عندما يصل إلى نقطة التوقف. في هذا الوقت ، يمكننا فتح نافذة وحدة المعالجة المركزية في أداة تصحيح الأخطاء لمراقبة الهيكل الداخلي لمساحة العملية.
يتم إيقاف سجل مؤشر التعليمات الحالي عند $ 0043E4B8. مساحة العملية ، التي تشغل 00000000 دولار إلى مساحة عنوان صغيرة جميلة مقابل $ fffffff.
في مربع الأوامر في نافذة وحدة المعالجة المركزية ، يمكنك البحث عن محتويات مساحة العملية. عند عرض محتوى المساحة أقل من 00400000 دولار ، ستجد سلسلة من علامات الاستفهام "؟؟؟؟" إذا نظرت إلى القيمة السداسية للمتغير العالمي في هذا الوقت ، فستجد أنه أيضًا 00400000 دولار. على الرغم من أن Hinstance يعكس مقبض مثيل العملية ، في الواقع ، فإن قيمة عنوان البداية عندما يتم تحميل البرنامج في الذاكرة ، وأيضًا في نوافذ 16 بت. لذلك ، يمكننا أن نعتقد أن برنامج العملية يتم تحميله بدءًا من 00400000 دولار ، أي أن المساحة التي تبدأ من 4 أمتار في المساحة الافتراضية 4G هي المساحة التي يتم فيها تحميل البرنامج.
من 00400000 دولار فصاعدًا وقبل 0044D000 دولار ، فهي أساسًا مساحة عنوان رمز البرنامج والبيانات العالمية. في مربع المكدس في نافذة وحدة المعالجة المركزية ، يمكنك عرض عنوان المكدس الحالي. وبالمثل ، ستجد أن مساحة عنوان المكدس الحالية تتراوح من 0067b000 دولار إلى 00680000 دولار ، بطول 5000 دولار. في الواقع ، فإن الحد الأدنى لحجم مساحة المكدس للعملية هو 5000 دولار ، والذي يتم الحصول عليه بناءً على قيمة حجم المكدس في صفحة الرابط من ProjectOptions عند تجميع برنامج Delphi ، بالإضافة إلى 1000 دولار. ينمو المكدس من العنوان الراقي إلى الأسفل. مساحة العملية. عند تجميع برنامج Delphi ، يمكنك التحكم في مساحة المكدس القصوى التي يمكن زيادتها عن طريق ضبط قيمة حجم المكدس الأقصى في صفحة الارتباط في ProjectOptions. لا سيما في البرامج التي تحتوي على علاقات استدعاء روتين فرعي عميق أو تستخدم خوارزميات عودية ، يجب ضبط قيمة حجم المكدس القصوى بشكل معقول. نظرًا لأن استدعاء روتين فرعي يتطلب مساحة مكدس ، وبعد أن يتم استنفاد المكدس ، سيرمي النظام خطأ "فائض المكدس".
يبدو أن مساحة العملية بعد مساحة المكدس يجب أن تكون مساحة خالية. في الواقع ، هذا ليس هو الحال. يبدو أن العملية يمكن أن تمتلك مساحة 2G فقط. في الواقع ، فإن المساحة التي يمكن أن تملكها العملية حقًا ليست حتى 2G ، لأن مساحة 4 أمتار من 00000000 إلى 00400000 دولار هي أيضًا منطقة مقيدة.
ولكن بغض النظر عن ماذا ، فإن العناوين التي يمكن أن تستخدمها عمليتنا لا تزال واسعة للغاية. خاصة بعد مساحة المكدس وما بين 80،000،000 دولار ، إنه ساحة المعركة الرئيسية في مساحة العملية. سيتم تعيين مساحة الذاكرة المخصصة للعملية من النظام إلى هذه المساحة ، وسيتم تعيين مكتبة الارتباطات الديناميكية التي يتم تحميلها بواسطة العملية إلى هذه المساحة ، كما سيتم تعيين مساحة ترابط الخيط الجديد إلى هذا الفضاء ، تقريبًا ، كل شيء تقريبًا. سيتم تعيين العمليات التي تنطوي على تخصيص الذاكرة جميعًا إلى هذا الفضاء. يرجى ملاحظة أن التعيين المذكور هنا يعني أن المراسلات بين الذاكرة الفعلية والمساحة الافتراضية. ؟؟؟؟ ".