في C ++ / CLI ، غالبًا ما يتم استخدام ما يسمى بفئات الواصف - الفئات المدارة التي تحتوي على مؤشر للفئة الأصلية كعضو. تتناول المقالة مخططًا ملائمًا ومضغوطًا لإدارة عمر الكائن الأصلي المقابل ، بناءً على استخدام القوالب المُدارة. يتم النظر في الحالات المعقدة للانتهاء.
جدول المحتويات
مقدمة
1. نمط التخلص الأساسي في C ++ / CLI
1.1. تعريف المدمر والمُنهي
1.2. باستخدام دلالات المكدس
2. القوالب المدارة
2.1. مؤشرات ذكية
2.2. مثال للاستخدام
2.3. خيارات إنهاء أكثر تعقيدًا
2.3.1. قفل Finalizer
2.3.2. باستخدام SafeHandle
المراجع
مقدمة
نادرًا ما يتم استخدام C ++ / CLI - إحدى لغات .NET Framework - لتطوير مشاريع مستقلة كبيرة. الغرض الرئيسي منه هو إنشاء تجميعات للتفاعل .NET مع التعليمات البرمجية الأصلية (غير المُدارة). وفقًا لذلك ، يتم استخدام الفئات التي تسمى فئات الواصف على نطاق واسع ، والفئات المدارة التي تحتوي على مؤشر للفئة الأصلية كعضو. عادةً ما تمتلك فئة الواصف الكائن الأصلي المقابل ، أي يجب حذفه في الوقت المناسب. من الطبيعي جدًا جعل مثل هذا الفصل معفيًا ، أي تنفيذ System::IDisposable
. يجب أن يتبع تطبيق هذه الواجهة في .NET نمطًا خاصًا يسمى Basic Dispose [Cwalina]. ميزة ملحوظة لـ C ++ / CLI هي أن المترجم يأخذ كل الأعمال الروتينية لتطبيق هذا القالب تقريبًا ، بينما في C # يجب أن يتم كل شيء تقريبًا يدويًا.
1. نمط التخلص الأساسي في C ++ / CLI
هناك طريقتان رئيسيتان لتنفيذ هذا النموذج.
1.1. تعريف المدمر والمُنهي
في هذه الحالة ، يجب تعريف المدمر والمُنهي في الفئة المُدارة ، وسوف يقوم المترجم بالباقي.
public ref class X { ~X() {}
على وجه الخصوص ، يقوم المترجم بما يلي:
- بالنسبة للفئة
X
تطبق System::IDisposable
. - في
X::Dispose()
يوفر استدعاء المدمر ، واستدعاء المدمر للفئة الأساسية (إن وجد) ، واستدعاء GC::SupressFinalize()
. - Overrides
System::Object::Finalize()
، حيث يوفر استدعاء للمُنهي و النهائية من الفئات الأساسية (إن وجدت).
يمكنك تحديد الوراثة من System::IDisposable
بشكل صريح ، ولكن لا يمكنك تعريف X::Dispose()
بنفسك.
1.2. باستخدام دلالات المكدس
يتم تنفيذ نمط التخلص الأساسي أيضًا بواسطة المحول البرمجي إذا كان للفئة عضو من النوع المحرر وتم التصريح باستخدام دلالات المكدس. هذا يعني أن اسم النوع بدون gcnew
(' ^
') يستخدم للإعلان ، gcnew
التهيئة في قائمة تهيئة المنشئ ، وليس باستخدام gcnew
. يتم وصف دلالات المكدس في [Hogenson].
هنا مثال:
public ref class R : System::IDisposable { public: R();
يقوم المترجم في هذه الحالة بما يلي:
- بالنسبة للفئة
X
تطبق System::IDisposable
. - في
X::Dispose()
يوفر استدعاء لـ R::Dispose()
لـ m_R
.
يتم تحديد الإنهاء من خلال وظيفة الفئة المقابلة R
كما في الحالة السابقة ، يمكن تحديد الوراثة من System::IDisposable
بشكل صريح ، ولكن لا يمكنك تحديد X::Dispose()
بنفسك. بطبيعة الحال ، قد يكون لدى الصف أعضاء آخرين أعلنوا باستخدام دلالات المكدس ، ويتم أيضًا توفير استدعاء Dispose()
لهم.
2. القوالب المدارة
وأخيرًا ، هناك ميزة رائعة أخرى لـ C ++ / CLI تجعل من الممكن تبسيط إنشاء فئات الواصف قدر الإمكان. نحن نتحدث عن القوالب المدارة. هذه ليست أدوية عامة ، ولكنها قوالب حقيقية ، كما هو الحال في C ++ الكلاسيكي ، لكن النماذج ليست أصليًا ، ولكنها فئات مُدارة. يؤدي إنشاء مثل هذه الأنماط إلى إنشاء فئات مُدارة يمكن استخدامها كفئات أساسية أو كأعضاء في فئات أخرى داخل التجميع. القوالب المُدارة موصوفة في [Hogenson].
2.1. مؤشرات ذكية
تسمح لك القوالب المُدارة بإنشاء فئات مثل المؤشرات الذكية التي تحتوي على مؤشر للكائن الأصلي كعضو وتوفر إزالته في أداة التدمير و وحدة الإنهاء. يمكن استخدام هذه المؤشرات الذكية كفئات أساسية أو أعضاء (بشكل طبيعي ، باستخدام دلالات المكدس) عند تطوير فئات واصف يتم تحريرها تلقائيًا.
هنا مثال على هذه الأنماط. القالب الأول هو قالب أساسي ، والثاني مخصص للاستخدام كفئة أساسية ، والثالث كعضو في الفئة. تحتوي هذه القوالب على معلمة قالب (أصلية) مصممة لحذف كائن. تحذف فئة الحذف الكائن باستخدام عامل الحذف بشكل افتراضي.
2.2. مثال للاستخدام
class N // { public: N(); ~N(); void DoSomething();
في هذه الأمثلة ، يتم تحرير الفئتين U
و V
بدون أي جهد إضافي ؛ توفر Dispose()
استدعاء عامل التشغيل delete
لمؤشر إلى N
يتيح لك الخيار الثاني ، باستخدام ImplPtrM<>
، إدارة فئات أصلية متعددة في فئة واصف واحد.
2.3. خيارات إنهاء أكثر تعقيدًا
الإنهاء هو جانب إشكالي إلى حد ما من .NET. في سيناريوهات التطبيق العادية ، لا ينبغي استدعاء أدوات الإنهاء ؛ يجب تحرير الموارد في Dispose()
. ولكن في سيناريوهات الطوارئ ، يمكن أن يحدث هذا ويجب أن يعمل المصممون بشكل صحيح.
2.3.1. قفل Finalizer
إذا كانت الفئة الأصلية موجودة في مكتبة الارتباط الحيوي (DLL) التي يتم تحميلها وإلغاء تحميلها ديناميكيًا - باستخدام LoadLibrary()/FreeLibrary()
، فقد تنشأ حالة عندما تكون هناك بعد تفريغ مكتبة الارتباط الديناميكي كائنات غير منشورة تحتوي على مراجع لمثيلات هذه الفئة. في هذه الحالة ، بعد فترة سيحاول جامع القمامة الانتهاء منها ، وبما أن DLL غير محمل ، فمن المرجح أن يتعطل البرنامج. (الميزة المميزة هي حدوث عطل بعد عدة ثوانٍ من إغلاق التطبيق على ما يبدو.) لذلك ، بعد إلغاء تحميل ملف DLL ، يجب حظر أدوات الإنهاء. يمكن تحقيق ذلك من خلال تعديل صغير في قالب ImplPtrBase
الأساسي.
public ref class DllFlag { protected: static bool s_Loaded = false; public: static void SetLoaded(bool loaded) { s_Loaded = loaded; } }; template <typename T, typename D> public ref class ImplPtrBase : DllFlag, System::IDisposable {
بعد تحميل مكتبة الارتباط الحيوي (DLL) ، تحتاج إلى استدعاء DllFlag::SetLoaded(true)
، وقبل إلغاء تحميل DllFlag::SetLoaded(false)
.
2.3.2. باستخدام SafeHandle
SafeHandle
فئة SafeHandle
معقدة وأكثر موثوقية إلى حد ما ، انظر [Richter]. يمكن إعادة تصميم القالب ImplPtrBase<>
لاستخدام SafeHandle
. لا يلزم تغيير القوالب المتبقية.
using SH = System::Runtime::InteropServices::SafeHandle; using PtrType = System::IntPtr; template <typename T, typename D> public ref class ImplPtrBase : SH { protected: ImplPtrBase(T* p) : SH(PtrType::Zero, true) { handle = PtrType(p); } T* Ptr() { return static_cast<T*>(handle.ToPointer()); } bool ReleaseHandle() override { if (!IsInvalid) { D del; del(Ptr()); handle = PtrType::Zero; } return true; } public: property bool IsInvalid { bool get() override { return (handle == PtrType::Zero); } } };
المراجع
[ريختر]
ريختر ، جيفري. البرمجة على منصة Microsoft .NET Framework 4.5 في C #. الطبعة الرابعة: Per. من اللغة الإنجليزية - سانت بطرسبرغ: بيتر ، 2016.
[Cwalina]
Tsvalina ، Krzhishtov. أبرامز ، براد. البنية التحتية لمشاريع البرمجيات: الاصطلاحات والمصطلحات والقوالب لمكتبات .NET القابلة لإعادة الاستخدام: الترجمة. من اللغة الإنجليزية - م: LLC "I.D. ويليامز ، 2011.
[هوجنسون]
هوجنسون ، جوردون. C ++ / CLI: لغة Visual C ++ لبيئة .NET.: Per. من اللغة الإنجليزية - م: LLC "I.D. وليامز ، 2007.