الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

Garbage Collector لبرامج سي++

بدأه محمد رضى في 3 يوليو 2009 · 26 رد · 3,722 مشاهدة · في مشاريع ومسابقات القسم
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكــم ورحمـة الله وبركاتــه ،،

أعمل حاليا على مكتبة بـ سي++ و أود دمج نظام شبيه بال Garbage Collector الذي تتميز به NET. و كما نعلم جميعا فجامع القمامة هذا ما هو إلا Thread تقوم بعزل و تحرير المناطق التي أصبحت غير مستعملة من الذاكرة و جعلها جاهزة لاستعمالات أخرى.

نحن نعرف أن نظام NET. مختلف كل الإختلاف عن سي++ و لكن ليس هناك مستحيل في عالم البرمجة...

دعونا نبدأ من أن كل العناصر دات الصفة Managed يجب أن تمر عبر gccnew لتكون مرتبطة بجامع القمامة, أي أنها تنشأ في Heap تكون مسيرة من طرف جامع القمامة. كل هذا واضح ولكن السؤال الأساسي في هذا الموضوع هو : ماهي المعايير و الشروط الازم توفرها في عنصر تم إنشاؤه ليعتبره جامع القمامة مرشحا للزوال؟

ملاحظة : هذا الموضوع Native سي++ و ليس سي++ .NET

#2

السلام عليكم ...

موضوع جميل,

الحقيقة في ++C يسمى الـ GC بـ Smart Pointers.

أفضل الموجود بلا منازع هو مجموعة الـ Smart Pointers من Boost

يسمى الـ Smart Pointer الذي يهتم بالتخلص من الذاكرة التي انتهينا من استخدامها بالـ Shared Pointer...

post-89451-1246591251_thumb.png

هناك نقطة بسيطة أود أن أشير إليها,

ما ستحصل عليه هو pointer يشبه كثيراً الموجود في Java و #C و غيرها من اللغات التي تدعم الـ GC...

لذلك لن يكون هناك pointers arithmetic على الإطلاق... و لن يصبح بإمكانك أن تستند عنوان ثابت لهذه المؤشرات الذكية...

بكل بساطة أن المؤشرات الذكية التي ستصممها, لن تكون مساوية في القوة أبداً لقوة مؤشرات ++C الموجودة في اللغة,

هناك مقايضة بين المرونة و بين السهولة في الإستخدام,

عموماً الـ shared_ptr لا يختلف عن الـ GC الموجود في #C و Java إلا بالـ Implementation.

تم استخدام الـ Reference Counting لأنه لا يحتاج إلى معالجة كثيرة, فهو ذو كفاءة, إضافة إلى ذلك فإنه مستخدم في Python و غير ذلك تحصل على ميزة إضافية بتطبيق مفهوم الـ RAII

الذي يمكنك من معرفة الوقت بالضبط الذي يتم فيه تحرير المصادر على عكس الـ GC في Java و #C فلديهم هذه المشكلة أنه لا يمكنك تحديد وقت موت الكائن الذي يشير إليه مؤشر أو كان يشير إليه بالأصح.

هناك أبحاث لتطبيق الـ GC على "مؤشرات ++C الأصلية" و أشهر الـ GCs في هذا المجال هو الـ conservative garbage collecto

هو في الأصل لـ C و لكنه يعمل مع ++C و لكن مع مشاكل بسيطة...

عموماً هناك شركات عملاقة منها HP الذي خرج من معمل أبحاثها و تستخدمه في مشاريعها و كثير من الشركات الأخرى...

و هو يحاول تطبيق مفهوم الـ GC على مؤشرات ++C الأصلية... لاحظ أنه يعطيك القوة الكاملة لمؤشرات ++C.

عموماً, نظرياً لا يمكن التأكد 100% من أن الذاكرة التي لم تعد تستخدم ستحرر... باستخدام هذا النوع من الـ GC.

السبب بكل بساطة أن المؤشرات في C و ++C يمكنك فعل ما تشاء بها... لم أستخدم هذا الـ GC من قبل و لا أريد استخدامه :)

بعد كل هذا أقول لك أنه لا حاجة للـ GC أصلاً في ++C, ربما في C كان الأمر صعباً و لكن في ++C الحديثة, لا حاجة لذلك و إن احتجت فأنت تبرمج بـ C و تترجم برامج باستخدام مترجم ++C لا أكثر..

انظر معي إلى هذه السطور التي تقرأ أسماء من ملف يحتوي على اسم في كل سطر و نقوم بتخزين تلك الأسماء كل اسم لوحده في مصفوفة ديناميكية...

post-89451-1246591243_thumb.png

اللغة توفر لك طرق ذات مرونة دون استخدام المؤشرات و كل هذا دون تضحية بالكفاءة..

الحاجة الوحيدة للمؤشرات هي عندما تقوم بكتابة Data Structure, هناك لابد من المؤشرات, و حتى في هذه الحالة يمكنك استخدام الـ Smart Pointer إذا كنت مهووساً بالسهولة..

في الأسطر السابقة لم أنسى إغلاق الملف, لأنه سيتم إغلاقه فعلاً عند انتهاء عمل الدالة و في هذه الحالة هي main لأنه سيتم استدعاء الـ destructor للـ ifstream.

أجد الـ RAII أسهل من العمل مع Java بالمناسبة في بعض الحالات..

و لكن بكل تأكيد إن كنت تستخدم المؤشرات في كل مكان في برنامجك بلغة ++C فإنك لا تبرمج بـ ++C و إنما بـ C :)

تحياتي ...

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 3 يوليو 2009 في 06:26

#3

موضوع لذيذ :D

اقتباس
تم استخدام الـ Reference Counting لأنه لا يحتاج إلى معالجة كثيرة, فهو ذو كفاءة, إضافة إلى ذلك فإنه مستخدم في Python و غير ذلك تحصل على ميزة إضافية بتطبيق مفهوم الـ RAII

رأيت مشاريع قائمة على هذا المبدأ ( أظن أنه موجودشيء عن هذا في كتاب Effective C++ ).

في الحقيقة , أغلب المشاريع التي تحاول تطبيق هذه المبدأ ( Reference Counting ) , تقوم على الوراثة .. لديك كائن أب له دالّتين , أحدها زيادة عدد المراجع التي تؤشر عليه , والاخر انقاصها ... تعرفون الباقي ...

ما أريد الوصول اليه , أنه يمكننا بناء نظام Reference Counting بسيط , حتى لو كانت Shared_Ptr من Boost تفي بالغرض , لسببين :

1- تعليمي .

2- الاستغناء عن خدمات Boost لعدة أسباب ( Syntax مزعج - انعدام Portability لبرنامجك (اذا لم يكن عند المبرمجين الاخر الجزء الخاص من مكتبة boost .. الخ ) .

الحقيقة , أنا مش مستوعب جميع الأمور الخاصة بعمل GC باستخدام Reference Counting .. لذلك أتمنى مناقشة كيفية تطوير Class نستطيع أن نرث منه .. لنطبّق GC بسيط ..

اقتباس
بعد كل هذا أقول لك أنه لا حاجة للـ GC أصلاً في ++C, ربما في C كان الأمر صعباً و لكن في ++C الحديثة, لا حاجة لذلك و إن احتجت فأنت تبرمج بـ C و تترجم برامج باستخدام مترجم ++C لا أكثر..

أوافقك , اذا كنت تريد استخدام vector بدلاً من مصفوفة ديناميكية باستخدام المؤشرات .

لكن هناك الكثير والكثير من استخدامات المؤشرات .. خاصة اذا كنت تطبّق مبدأ مثل Polymorphism , تحتاج للمؤشر , واذا كان مشروعك بالكامل يعتمد على Polymorphism , حينها ستشاهد الكثير من النجوم ... وستصل لمرحلة كالتي وصل اليها الاخ محمد رضى ;)

تم تعديل هذه المشاركة بواسطة الشمري في 3 يوليو 2009 في 07:19

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#4
اقتباس
رأيت مشاريع قائمة على هذا المبدأ ( أظن أنه موجودشيء عن هذا في كتاب Effective C++ ).

في الحقيقة , أغلب المشاريع التي تحاول تطبيق هذه المبدأ ( Reference Counting ) , تقوم على الوراثة .. لديك كائن أب له دالّتين , أحدها زيادة عدد المراجع التي تؤشر عليه , والاخر انقاصها ... تعرفون الباقي ...

في الحقيقة يمكنك فعل هذا الشيء في ++C بطريقة أفضل,

قم بإنشاء نوع جديد class سمه ما شئت و لتكن فكرته أنه smart pointer,

استخدم operators overloading لمحاكاة المؤشرات بالضبط,

استخدام templates للتعامل مع أي نوع,

هكذا أصبح لديك مؤشر smart دون الحاجة حتى أن تعرف الكائنات التي سوف يشير إليها بوجوده :)

اقتباس
ما أريد الوصول اليه , أنه يمكننا بناء نظام Reference Counting بسيط , حتى لو كانت Shared_Ptr من Boost تفي بالغرض , لسببين :

1- تعليمي .

2- الاستغناء عن خدمات Boost لعدة أسباب ( Syntax مزعج - انعدام Portability لبرنامجك (اذا لم يكن عند المبرمجين الاخر الجزء الخاص من مكتبة boost .. الخ ) .

السبب الأول سبب رائع, و هو ما جعلني في الحقيقة أقرأ حول الموضوع,

الفصل الثاني من هذا الكتاب سيعلمك إنشاء GC باستخدام الـ reference counting بكل سهولة....

إلا أنه يبقى مشكلة واحدة لا يحلها مؤلف الكتاب, و هي الـ Circular References... و يبقى عليك حلها,

هناك خوارزميات عدة لحل المشكلة, و لكن تبقى معقدة لمن يريد التعلم و ليس إنتاج شيء عملي....

السبب الثاني لا أتفق معك فيه,

شئنا أم أبينا, فهذه المكتبة بالذات من Boost هي من أفضل المكتبات, و لا أرى سبباً لعدم استخدامها, و أضمن لك أنها تعمل على منصات أكثر من المنصات التي يوجد لها JVM بأضعاف, و دون مشاكل على الإطلاق :P

و إن لم تكن لديك Boost حالياً, فهذه المكتبة ستصبح من المكتبات القياسية في cpp0x لذلك استعد لمواجهة الأمر :P

و استخدامها رائع, و تحل مشكلة الـ circular references بطريقة رائعة عن طريق تقديم الـ weak pointers :wink: و هذا يسهل المهمة و يزيد الكفاءة في نفس الوقت...

اقتباس
الحقيقة , أنا مش مستوعب جميع الأمور الخاصة بعمل GC باستخدام Reference Counting .. لذلك أتمنى مناقشة كيفية تطوير Class نستطيع أن نرث منه .. لنطبّق GC بسيط ..

أعتقد أن طريقة الوراثة هذه موجودة في COM و ذلك لكي يعمل مع مدى واسع من اللغات.. و ليس فقط ++C,

و لكن إذا أردت بناء smart pointer ليعمل في ++C فيمكنك بناء شيء أفضل بكثير, و بحيث لاتحتاج إلى الوراثة على الإطلاق أو التلاعب بالكائن الذي لديك, و هذه فوائده واضحة بكل تأكيد.

الفكرة تقوم على شيء يشبه التالي ::

نقوم بتصميم class يحاكي المؤشرات في ++C في طريقة استخدامه, و في الخلفية يكون لدينا data structure كـ map مثلاً التي استخدمها مؤلف كتاب art of cpp,

هذه الـ map عبارة عن static variable في الـ class, عندما تقوم بإنشاء أي كائن على الـ heap فإن عنوانه يضاف إلى الـ map إضافة إلى زيادة العداد الخاص بعدد المؤشرات التي تشير إلى هذه القطعة,

بعد ذلك عندما تقوم بإسناد مؤشر جديد إلى المؤشر القديم فإن العداد يزداد,

إذا خرج أي مؤشر من الـ scope فإن الـ destructor يتم استدعاؤه, بالتالي يتم البحث في الـ map لإنقاص العداد,

إذا وصل العداد إلى الصفر, فإن الكائن يتم إرجاعه إلى النظام بإلغاء الذاكرة المحجوز له عن طريق delete مثلاً,

ليست صعبة التطبيق على الإطلاق, ربما أقل من عدة صفحات من الكود,

و لكن الشيء الصعب هو وضع القوانين و فهمها, إضافة إلى اختبار هذا الـ smart pointer و غير ذلك أن يكون ذا كفاءة عالية جداً,

و هذا الشيء موجود في boost بأفضل هندسة موجودة و أفضل كفاءة أيضاً,

و لكن لاحظ أنك تحصل على مؤشر للتعامل معه البيانات المحجوزة فقط و ليس مؤشراً عاماً كالمؤشرات الأصلية,

لن يمكنك أن تجعل المؤشر يتحرك في أي مكان تريده, فقط عمليات إسناد, نفس مؤشرات Java و #C,

استعمالها مطلب ضروري حقيقة, لأن العبث بالمؤشرات الأصلية, ليس لعبة, و الأخطاء فيها قاتلة إن لم يكن المبرمج متمرساً و لكنها عند بناء data structure كالـ containers الموجودة في STL فإنها مطلب أساسي و لا يمكن العمل بدونها :)

اقتباس
كن هناك الكثير والكثير من استخدامات المؤشرات .. خاصة اذا كنت تطبّق مبدأ مثل Polymorphism , تحتاج للمؤشر , واذا كان مشروعك بالكامل يعتمد على Polymorphism , حينها ستشاهد الكثير من النجوم ... وستصل لمرحلة كالتي وصل اليها الاخ محمد رضى

post-89451-1246597034_thumb.png

أي شيء تريده يمكنك عمله بالـ smart pointers إلا الـ arithmetic و تعيين العنوان يدوياً,

و هذا بالضبط قدرة مؤشرات Java و #C, أما بالنسبة للـ syntax البشع, فهذا ليس مشكلة بالنسبة لي على الأقل, هناك typedef :P

تحياتي ...

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 3 يوليو 2009 في 08:02

#5

أولا, دعوني أشكركم على مشاركاتكم القيمة التي استفدت منها الكثير الكثير. كل التقنيات التي تكلمتم عنها فعالة و لاشك في فعاليتها و لكن و لأسباب عدة لا أريد دمج أية مكتبة في المكتبة التي أريد إنشائها. و واحد من هذه الأسباب سبب تعليمي محظ. أعرف أنني سأخسر الكثير من الوقت و الجهد في إنشاء أنضمة موجودة, فعالة و الأكثر من هذا بلا مقابل لكنني في نفس الوقت سأكسب الكثير و خصوصا سهولة النقل بين أنظمة التشغيل.

اقتباس
هناك أبحاث لتطبيق الـ GC على "مؤشرات ++C الأصلية" و أشهر الـ GCs في هذا المجال هو الـ conservative garbage collecto

هو في الأصل لـ C و لكنه يعمل مع ++C و لكن مع مشاكل بسيطة...

عموماً هناك شركات عملاقة منها HP الذي خرج من معمل أبحاثها و تستخدمه في مشاريعها و كثير من الشركات الأخرى...

و هو يحاول تطبيق مفهوم الـ GC على مؤشرات ++C الأصلية... لاحظ أنه يعطيك القوة الكاملة لمؤشرات ++C.

هذا هو ما أحتاج إليه بالظبط و هذا ما أريد أخد آرائكم المحترمة عنه. هناك أجوبة كثيرة تمت الإجابة عنها عند قراءة الصفحة Conservative garbage collector لكن هناك العديد من الأسئلة الأخرى التي لا تزال محتاجة لأجوبة ولا أريد تحديدها هنا لكي لا أحد أكثر من جوانب الموضوع.

بخلاف السيد Khaled.Alshaya الذي حدد موقفه من هذه التقنية, ما رأيكم باقي الأعضاء المحترمين؟

#6

السلام عليكم ...

اقتباس
بخلاف السيد Khaled.Alshaya الذي حدد موقفه من هذه التقنية, ما رأيكم باقي الأعضاء المحترمين؟

ليس تحديد موقف :)

و إنما هذا الموضوع هو مجال بحث كبير جداً و متقدم... و يحتاج دراية بالـ memory model التي توفرها ++C و التي بلاشك معقدة إلى أبعد الحدود...

المشكلة أن المؤشرات في ++C/C مرنة إلى أبعد الحدود, و ما يمكنك فعله بالـ assembly يمكنك فعله بـ ++C/C بخصوص العناوين...

لاحظ أنه حتى في #C الـ unsafe code أنت تقوم بالتعامل معه,

و في cpp.net فهناك ما يسمى بالـ handles و هو شبيه بالـ smart pointers و ليس المؤشرات الحقيقية...

بالتوفيق في تجاربك...

#7

شكرا أخي...

و لصعوبة الموضوع و تشابك أطرافه لجأت إليكم للتحاور و التشاور لكي لا أقع في أخطاء لم تكن في الحسبان. و ماشاء الله فالمنتدى مليئ بالكفاءات التي يمكن الإعتماد عليها في مثل هذه الأمور. و لهذا فالسؤال الذي يمنعني من وضع تصور واضح لنضام GC هو الشروط التي يجب توفرها في كائن تم حجزه بطريقة ديناميكية ليصبح مرشحا للتحرير و الزوال. بوجود المؤشرات العادية الأمر صعب جدا إن لم أقل إنه مستحيل مما يستدعي وجوب استعمال المؤشرات الذكية. لكنني لا أريد الحد من إمكانيات الكائنات بوضعها بعد إنشائها مباشرة داخل مؤشر ذكي ولكن فقط عند نسخ عنوان الكائن في الذاكرة. وإضافة إلى استعمال المؤشرات الذكية أرى أن عملية إنشاء كائن تقوم بحجز اشتراك للكائن في لائحة الكائنات المنشأة بطريقة ديناميكية والتي ستبقى دائما تحت أعينThread GC لإزالتها و قتما لم تعد دات فائدة. وفي هذه الحالة يجب على المبرمج احترام عقد التقة الذي يمنع عليه نسخ عنوان الكائن إلا عن طريق مؤشر ذكي و ليس مؤشر عادي. و بهذه الطريقة يمكننا تحديد الكائنات التي يمكننا إزالتها بأمان. كما يجب أن أذكر وجوب تمتع المؤشرات الذكية بخاصية تعداد النسخ و عدم إزالة الكائن عند الصفر وإنما إبلاغ GC أن الكائن X لم يعد له أي مؤشر ذكي.

#8
اقتباس
أي شيء تريده يمكنك عمله بالـ smart pointers إلا الـ arithmetic و تعيين العنوان يدوياً,

أفحمتني :D .. اذاً نقتنع بجدوى Shared_Ptr , لكن نريد بناء مكتبة لهدف تعليمي ..

حاولت أقرأ اليوم على السريع عن ما ذكره الاخ خالد عن هذا المشروع :

http://www.hpl.hp.com/personal/Hans_Boehm/gc/

قراءة سريعة لم أستوعب شيئاً مما قرأت ( تنسيق الصفحة عموماً يسد النفس .. واضح أن اللي كاتبها دكتور واستاذ مشارك :D ).

لم افهم مثلاً المقصود بالمؤشرات الأصلية :

اقتباس
و هو يحاول تطبيق مفهوم الـ GC على مؤشرات ++C الأصلية... لاحظ أنه يعطيك القوة الكاملة لمؤشرات ++C.

وأمور أخرى , لذلك أقترح عموماً أن يتم تغيير دفّة الحوار ودفعها دفعاً :D نحو بناء GC باستخدام مفهوم Reference Counting .

على الأقل أتمكن من الفلسفة قليلاً ونشارك بما نعرف ..

خاصّة وأن مفهوم Ref Counting , مطبق حتى في COM .. كما قال الاخ خالد , وهذا يدل على أنه فعاّل .

لايهم طريقة بناءه ( سواء بالوراثة أو على طريقة Boost ) .

هذا رأيي في هذا الموضوع .

تم تعديل هذه المشاركة بواسطة الشمري في 3 يوليو 2009 في 18:22

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#9

إذن فلنبدأ بإنشاء كلاس لمؤشراتنا الذكية. الهذف منها تسيير و حماية العناصر المحجوزة بطريقة ديناميكية و إزالتها عند الحاجة و خاصة عند الخروج من السكوب. لهذه الكلاس اثنان من Constructors :

MySmartPointer(T *ptr) يقبل مؤشر لعنصر واحد

MySmartPointer(T *ptr, size_t len) يقبل مؤشر لمصفوفة من العناصر و len هو عدد عناصر المصفوفة

الدوال كلها inline لتسهيل القراءة وفهم الكود

المرجو عدم المقارنة مع بوست لأن الهذف من هذا الموضوع تعليمي و ليس مضاهات بوست

#include <string.h> 

template <class T>
class MySmartPointer
{
private :
	T *m_pTPointer; // المؤشر المسير داخليا
	int m_refCounter; // تعداد الكائنات المنسوخة
	MySmartPointer<T> *m_pParent; // الكائن الأب 
	size_t m_length; // عدد العناصر في حالة ما كانت مصفوفة
public : // Constructors & Destructor
	MySmartPointer(T *ptr) : m_refCounter(0), m_pParent(NULL), m_length(0)
	{
		if(ptr == NULL) throw "Null Reference Argument!";
		m_pTPointer = ptr;
	}

	// للإستعمال مع المصفوفات
	MySmartPointer(T *ptr, size_t len) : m_refCounter(0), m_pParent(NULL), m_length(0)
	{
		if(ptr == NULL) throw "Null Reference Argument!";
		if(len <= 0) throw "Argument Out Of Range!";
		m_pTPointer = ptr;
		m_length = len;
	}

	// لإستعمال في حالة نسخ كائن
	MySmartPointer(MySmartPointer<T> &msptr) : m_refCounter(0), m_length(0)
	{
		m_pParent = &msptr;
		m_pTPointer = msptr.m_pTPointer;
		m_length = msptr.m_length;
		msptr.m_refCounter++;
	}

	// مراعاة العلاقة بين الآباء و الأبناء عند تدمير أي كائن
	~MySmartPointer(void)
	{
		if(m_refCounter == 0) {
			if(m_pParent != NULL) {
				m_pParent->m_refCounter--;
			} else {
				if(m_pTPointer != NULL) {
					if(m_length > 0) {
						delete[] m_pTPointer; // إزالة المصفوفة
					} else {
						delete m_pTPointer;
					}
				}
			}
		} else {
			m_refCounter--;
		}
	}
public : // Operators
	T &operator ->(void)
	{
		return *m_pTPointer;
	}

	T &operator +(const T &value)
	{
		return *m_pTPointer + value;
	}

	T &operator *(const T &value)
	{
		return *m_pTPointer * value;
	}

	T &operator -(const T &value)
	{
		return *m_pTPointer  -value;
	}

	T &operator /(const T &value)
	{
		return *m_pTPointer / value;
	}

	T &operator =(const T &value)
	{
		*m_pTPointer = value;
		return *m_pTPointer;
	}

	T &operator -=(const T &value)
	{
		*m_pTPointer -= value;
		return *m_pTPointer;
	}

	T &operator *=(const T &value)
	{
		*m_pTPointer *= value;
		return *m_pTPointer;
	}

	T &operator /=(const T &value)
	{
		*m_pTPointer /= value;
		return *m_pTPointer;
	}

	bool operator &&(const T &value)
	{
		return *m_pTPointer && value;
	}

	bool operator ||(const T &value)
	{
		return *m_pTPointer || value;
	}

	bool operator ==(const T &value)
	{
		return *m_pTPointer == value;
	}

	bool operator !=(const T &value)
	{
		return *m_pTPointer != value;
	}

	T &operator &=(const T &value)
	{
		*m_pTPointer &= value;
		return *m_pTPointer;
	}

	T &operator |=(const T &value)
	{
		*m_pTPointer |= value;
		return *m_pTPointer;
	}

	bool operator <(const T &value)
	{
		return *m_pTPointer < value;
	}

	bool operator >(const T &value)
	{
		return *m_pTPointer > value;
	}

	bool operator <=(const T &value)
	{
		return *m_pTPointer <= value;
	}

	bool operator >=(const T &value)
	{
		return *m_pTPointer >= value;
	}

	T operator !(void)
	{
		return !*m_pTPointer;
	}

	T operator ~(void)
	{
		return ~*m_pTPointer;
	}

	T &operator [](const int &index)
	{
		if((size_t)index >= m_length) throw "Argument Out Of Range!";
		return m_pTPointer[index];
	}

	T &operator <<=(const int &count)
	{
		*m_pTPointer <<= value;
		return *m_pTPointer;
	}

	T &operator >>=(const int &count)
	{
		*m_pTPointer >>= value;
		return *m_pTPointer;
	}

	T &operator <<(const int &count)
	{
		return *m_pTPointer << count;
	}

	T &operator >>(const int &count)
	{
		return *m_pTPointer >> count;
	}

	T &operator ^(const T &value)
	{
		return *m_pTPointer ^ value;
	}

	T &operator ^=(const T &value)
	{
		*m_pTPointer ^= value;
		return *m_pTPointer;
	}

	T &operator++()
	{
		++*m_pTPointer;
		return *m_pTPointer;
	}

	T operator++(int n)
	{
		T temp = *m_pTPointer;
		++*m_pTPointer;
		return temp;
	}

	T &operator--()
	{
		--*m_pTPointer;
		return *m_pTPointer;
	}

	T operator--(int n)
	{
		T temp = *m_pTPointer;
		--*m_pTPointer;
		return temp;
	}

	T &operator %(const T &value)
	{
		return *m_pTPointer % value;
	}

	T &operator %=(const T &value)
	{
		*m_pTPointer %= value;
		return *m_pTPointer;
	}

	bool operator ==(const MySmartPointer<T> &ptr)
	{
		return ptr.m_pTPointer == m_pTPointer;
	}

	bool operator !=(const MySmartPointer<T> &ptr)
	{
		return ptr.m_pTPointer != m_pTPointer;
	}

	operator T(void)
	{
		return *m_pTPointer;
	}

	void *operator new(size_t n)
	{
		throw "Operation prohibited!";
	}
};

// بعض التجارب على المؤشر الذكي
void main(void)
{
/*
	typedef MySmartPointer<int> IntPtr;
	IntPtr ptr(new int(0));
	int i = ptr;
	ptr = 10;
	ptr *= 5;
	ptr /= 2;
	ptr++;
	++ptr;
	ptr--;
	--ptr;
	{
		IntPtr ptr1 = ptr;
		ptr1 = 1;
		ptr1 = !ptr1;
		~ptr1;
	}
	IntPtr * newptr = &ptr; // أحذ أخطاء المبتدئين و هو تمرير مؤشر متغيرة محلية خارج السكوب
	newptr = new IntPtr(new int); // ممنوع لأنه ينفي أحد أهم الإستعمالا ت المهمة للمؤشرات الذكية
*/
	typedef MySmartPointer<char> SZPTR;
	char *name = new char[13];
	strcpy(name, "Mohamed Rida");
	SZPTR ptr(name, strlen(name));

	// TODO +> Iterator implementation
	for(size_t i = 0; i <= strlen(name); i++) {
		char c = ptr;
	}
}

تم تعديل هذه المشاركة بواسطة محمد رضى في 5 يوليو 2009 في 16:20

#10

لا ردود و لا تعقيبات و لا تصحيح! هل هذا يعني أن MySmartPointer كاملة متكاملة و فعالة!

هذا ما أشك فيه...

هناك خطأ في دالة تشغيل العامل <- المرجو تصحيح الدالة بالشكل التالي :

	T *operator ->(void)
	{
		return m_pTPointer;
	}
#11

السلام عليكم ...

لماذا قمت بتضمين كل هذه المعاملات ؟

يجب أن يكون الـ smart pointer يشابه المؤشر قدر المستطاع... أتمنى أن تقرأ الكتاب الذي أشرت إليه فهو يوفر تصميم جيد يمكن أن تبدأ به..

قبل حتى أن تضع يديك على لوحة المفاتيح, و تبدأ بكتابة الكود, خذ ورقة و قلم و ضع كل الأشياء التي يمكن للمؤشر أن يقوم بها.. بالطبع هناك أشياء ستمنع حدوثها, كـ pointer arithmetic و عمليات الإسناد لعنواين رقمية,

فكر كيف يمكن أن تقوم بهذا ثم صمم مؤشرك على هذا الأساس...

أخييييييييييييييييييراً قم بكتابة الكود,

إذا قرأت الكتاب أنا مستعد للمساعدة, و أنصحك بأن تجرب الـ smart pointer الموجود في المكتبة القياسية autoptr و تعرف عيوبه و فوائده, ثم تجرب مجموعة boost و ترى كيفية عمل المجموعة ثم تجرب ما صنعه غيرك, حتى تأخذ فكرة عن عمله قبل أن تبدأ التفكير في بناء إحداها,

تحياتي ...

#12
اقتباس
لماذا قمت بتضمين كل هذه المعاملات ؟

قمت بتضمين كل هذه المعاملات ليتمكن MySmartPointer من التعامل مع كل أنواع المتغيرات ابتداءا من bool و حتى أكثر الكلاس تعقيدا وهذا ما تم اتباته من التجارب التي قمت بها.

اقتباس
يجب أن يكون الـ smart pointer يشابه المؤشر قدر المستطاع... أتمنى أن تقرأ الكتاب الذي أشرت إليه فهو يوفر تصميم جيد يمكن أن تبدأ به..

أنا لست ملزما بتصميم Smart Pointer نسخة طبق الأصل من تلك المدمجة مع boost

اقتباس
قبل حتى أن تضع يديك على لوحة المفاتيح, و تبدأ بكتابة الكود, خذ ورقة و قلم و ضع كل الأشياء التي يمكن للمؤشر أن يقوم بها..

هذه النصيحة أخدتها و عملت بها منذ بدأت تعلم البرمجة على الباسكال سنة 1995

اقتباس
أنا مستعد للمساعدة

شكرا أخي. MySmartPointer رأيي و أنا محتاج للرأي الآخر على أن يكون بناءا.

اقتباس
أنصحك بأن تجرب الـ smart pointer الموجود في المكتبة القياسية autoptr و تعرف عيوبه و فوائده, ثم تجرب مجموعة boost و ترى كيفية عمل المجموعة ثم تجرب ما صنعه غيرك, حتى تأخذ فكرة عن عمله قبل أن تبدأ التفكير في بناء إحداها,

كما قلت في أول هذا الموضوع, أنا أريد عمل هذا لغرض تعليمي و قد جربت المؤشرات الذكية و أعرف ما بها من عيوب و أنا أريد أن امتحن إمكانياتي على انشاء شبيه للمؤشرات الذكية قبل أن أرى الكود و التصميم ورائها. و أضنني - وحتى ظهور العكس - أنني قد نجحت.

شكرا أخي على الإجابة...

#13

السلام عليكم ...

رجاءً الأمر ليس شخصياً حتى تقلب النقاش.

و خبرتك على العين و الراس, و لكن يبدو أن ليس لديك خبرة بـ ++C :)

انظر إلى قطعة الكود التالية و قل لي ماذا سيحصل عندما استخدمنا MySmartPointer :)

typedef MySmartPointer<int> IntPtr;
IntPtr p(new int(1));
IntPtr p2(new int(10));
p = p2;

أعرف أن هناك مؤشرات في Pascal, و لكن ++C فيها مؤشرات و operators overloading و templates و oop و أنت تدمجهم كلهم مع بعضهم البعض, و هذا هو سر القوة الحقيقية في ++C.

يجب أن تكون شديد الحذر, هذا على افتراض أن تصميمك سليم 100% :), و في المثال الذي وضعته أنت, فاستدعاؤك للـ destructor هو تحطيم لفائدة الـ smart pointer لأن delete كل ما تقوم به هو استدعاء الـ destructor ثم إعادة الذاكرة :)

ثانياً,

أنت قمت ببناء شيء يشبه الـ smart reference إن كان هناك شيء يسمى هكذا أصلاً,

لاحظ معي قطعة الكود التالي ::

typedef MySmartPointer<double> IntPtr;
IntPtr p(new double(2));
IntPtr p2(new double(3));

IntPtr p3(p | p2);

ما تريد عمليه بأن يستبدل المؤشر بالمتغير نفسه وقت حساب العبارة هو فكرة جميلة :)

و لكنها ليست بهذه السهولة على الأقل... سأطرح سببين,

الأول لأن المؤشرات في ++C تعامل على أنها مؤشرات حتى لو كانت smart pointers و هذا يعطي سهولة في الإستخدام بدلاً من التعود على طريقة جديدة.

ثانياً, لأن مؤشرك يعرف جميع المعاملات تقريباً, و عندما يكون النوع الذي يشير إليه لا يعرفها كلها, فسنحصل على شيء مثل التالي ::

typedef MySmartPointer<double> IntPtr;
IntPtr p(new double(2));
IntPtr p2(new double(3));

IntPtr p3(p | p2);

هل تلاحظ... من أين سيصدر الخطأ, من داخل MySmartPointer! و هذا الشيء غير صحيح... لأن الخطأ من المعامل | الغير موجود في حالة الأعداد الحقيقية.

عموماً, لدي نقطتان....

الأولى, قم باستخدام خوارزمية معروفة, كالموجودة في كتاب cpp art...

الثانية, فكرة استبدال المؤشر الذكي بالمتغير نفسه, وقت حساب العبارات فكرة رائعة, و تحتاج إلى مجهود كبير حقيقية... و أعتقد أن ما سيساعدك هو مجموعة الـ Metaprogramming من boost... هناك مكتبة اسمها operators يمكن أن تفيدك,

بحيث أن المعاملات التي يعرفها مؤشرك يتم تحديدها وقت تحديد النوع نفسه, و ليس وقت كتابة الـ smart pointer :)

ممكن جداً في ++C و ستصبح الفكرة أكثر أناقة برأيي :)

تحياتي ...

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 7 يوليو 2009 في 09:31

#14

هكدا أردت و أظنني قد نجحت :thumb_up: أردت منك أن تقوم بتجربة الكلاس من أجلي و قد نجحت الخطة...

أولا باسكال قصة قديمة و شكك في خبرتي في سي++ ينبع من تشبتك بالمبادئ و عدم رغبتك في الخروج عنها أما أنا فالغاية تبرر الوسيلة مع عدم تجاوز الخطوط الحمراء.

شكرا أخي على الوقت الدي بدلته و أنا ماض في هدا الموضوع و من يريد مشاركتي حتى الوصول إلى النتيجة المرجوة و لا يهمني إن لم تكن النتيجة معروفة و تحترم المبادئ. المهم أن تكون فعالة و الأهم أن تكون سهلة عند تفعيلها في بيئة أخرى.

و سأتطرق لاحقا لتصور آخر قد يكون أحسن مما سبق إن شاء الله تعالى.

شكرا أخي Khaled.Alshaya على النصائح و الإفادات القيمة جدا.

تم تعديل هذه المشاركة بواسطة محمد رضى في 7 يوليو 2009 في 17:17

#15

السلام عليكم ....

الفكرة جميلة أخي العزيز, و لكن التطبيق ليس هيناً كما تعتقد :)

الذي تريد القيام به هو بناء garbage collector في لغة لاتدعم الـ garbage collection أصلاً,

و لكن فكرة معاملة المؤشر على أنه reference جميلة و ربما مع قليل من التطوير تصبح الفكرة ناجحة :)

و لكن لكي تصبح ناجحة على قلب الـ garbage collector أن يكون صحيحاً أولاً....

عموماً, إن كنت مهتماً بحق بهذا الموضوع... فهناك مكانان أدلك عليهما أرهما الأفضل إن كنت تفكر في إنتاج شيء جدي :)

الأول هو الكتاب الشهير :: Modern C++ Design

من ثنايا هذا الكتاب خرجت المكتبة الشهيرة loki ...

المكان الآخر هو boost :P

كما قلت لك في البداية, استعمل خوارزمية جيدة للـ garbage collection و أضف عليها فكرتك, و أعتقد أنها ستصبح حقيقة :)

تحياتي ....

#16

أولا وقبل كل شيئ, دعونا لا نخرج عن الموضوع الأساسي ألا و هو الوصول إلى نوع من المؤشرات يمكننا من تفعيل نظام GC إلى حد ما متكامل و لم لا أن يكون متكاملا. دعونا لا نضع حدودا لإمكانياتنا متعدرين أنه صعب الوصول إلى الهدف أو أننا لا يمكن لنا عمل تقنية كالتقنية كذا و التقنية كذا...

ثانيا و بعد أولا, دعونا لا نسمي مؤشرات ذكية أو Smart Pointers للمؤشر الذي نحن بصدد إنشائه حتى نتفادى كل مقارنة بتقنيات أو مكتبات أخرى قد تم بدل جهد سنوات و سنوات للوصول إليها إما نحن فلم نبدأ إلا مند ساعات قليلة أو حتى دقائق معدودة إذا ما نظرنا من جهة أن لكل منا عمله ذا الدوام الكامل و لا نخصص إلا بضع دقائق للتفكير بهذا الموضوع.

إليكم لائحة أمنياتي :

أريد مؤشرا أو سموه ما شئتم يمكن له تسيير أي نوع من أنواع المتغيرات و حتى كل أنواع الكلاس.

أريد من المؤشر أن يكون قادرا على القيام بالأمور الثالية :

{
	typedef MyXYZPointer<int> IntPtr;
	IntPtr ptr = new int; // First int is referenced one time
	IntPtr ptr1 = new int; // Second int is referenced one time
	ptr1 = ptr; // Second int must be freed cause it's not referenced anymore
	ptr1 = NULL; // First int reference count must be decremented
} // First int must be freed cause it's out of scope

أريد من المؤشر أن يكون قادرا على فعل كل هذه الأشياء من دون أي مساعدة من boost أو أي مكتبة أخرى حتى و لو كانت STL نفسها.

المؤشر لن يحاول معالجة هذه الحالة :

{
	typedef MyXYZPointer<int> IntPtr;
	int *pint = new int;
	IntPtr ptr = pint;
	return pint;
}

لنتمنى و كبداية نظام GC بدائي يقوم بحساب عدد المؤشرات التي تستعمل عنصرا أو قطعة من الذاكرة و عندما يصل عدد المستعملين الصفر يقوم بإلغاء العنصر و إزالته. و لنقل و كبداية أن GC يشتغل كلما تم إنشاء عدد من المؤشرات وقد تم استغلال عدد ن من الكيلوبايت من الذاكرة.

آه, كدت أنسى. أريد من المؤشر أن يكون قادرا على تسيير المصفوفات.

و لكم الحرية في زيادة متمنياتكم إلى الائحة...

تحياتي

#17
Khaled.Alshaya كتب:
السلام عليكم ....

الفكرة جميلة أخي العزيز, و لكن التطبيق ليس هيناً كما تعتقد :)

الذي تريد القيام به هو بناء garbage collector في لغة لاتدعم الـ garbage collection أصلاً,

و لكن فكرة معاملة المؤشر على أنه reference جميلة و ربما مع قليل من التطوير تصبح الفكرة ناجحة :)

و لكن لكي تصبح ناجحة على قلب الـ garbage collector أن يكون صحيحاً أولاً....

عموماً, إن كنت مهتماً بحق بهذا الموضوع... فهناك مكانان أدلك عليهما أرهما الأفضل إن كنت تفكر في إنتاج شيء جدي :)

الأول هو الكتاب الشهير :: Modern C++ Design

من ثنايا هذا الكتاب خرجت المكتبة الشهيرة loki ...

المكان الآخر هو boost :P

كما قلت لك في البداية, استعمل خوارزمية جيدة للـ garbage collection و أضف عليها فكرتك, و أعتقد أنها ستصبح حقيقة :)

تحياتي ....

أنا آسف لأنني كنت أكتب عندما أرسلت ردك و كل ردودك جديرة بالإحترام لما فيها من أفكار جديدة و إفادات قيمة. و أعدروني لحبي لكشف و دراسة ما يجري وراء الستار و خبايا التقنيات الجديدة و المشهورة.

شكرا أخي على تشجيعاتك و أرجو ألا تبخل علي بآرائك و اقتراحاتك و نقذك.

#18

السلام عليكم و رحمة الله تعالى و بركاته.

لن أكذب على نفسي أو عليكم لأقول أنني اكتشفت شيئا جديدا أو طورت تقنية جديدة لأن هذا الموضوع ما هو إلا تفكير بصوت عال عسى أن يشاركك أحد في أمر ما خصوصا إذا كنت تعمل على مشروع من النوع الكبير و تفكر وحدك و تصمم و تطور وحدك...

فلنبدآ على بركة الله...

 template<class T>
class GCPointer
{
private :
	T *m_pTPointer;
	size_t m_Count;
private :
	void UpdateObjectRefCount(const int refStep);
	void CheckArray(void);
public :
	const T *GCP_NULL;
public:
	GCPointer(void);
	GCPointer(const GCPointer<T> &ptr);
	GCPointer(const T *pobject);
	~GCPointer(void);
public :
	size_t GetCount(void);
	bool IsUnique(void);
	size_t GetUsageCount(void);
	bool IsEmpty(void);
	void Reset(void);
	T *GetPointer(void);
public : // Operators
	bool operator ==(const GCPointer<T> &ptr);
	bool operator !=(const GCPointer<T> &ptr);
	bool operator ==(const T *pobject);
	bool operator !=(const T *pobject);
	bool operator ==(const T &value);
	bool operator !=(const T &value);
	GCPointer<T> &operator =(const GCPointer<T> &ptr);
	GCPointer<T> &operator =(const T *pobject);
	T &operator =(const T &value);
	T &operator [](const int &index);
	operator T(void);
	T &operator *(void) const;
	T *operator ->(void) const;
};

T *m_pTPointer ;

هذا مؤشر C/C++ عادي حيث سيتم حفظ عنوان العنصر المسير داخليا.

size_t m_Count ;

هنا سيتم تسجيل عدد العناصر إذا كان m_pTPointer مؤشرا لمصفوفة وليس عنصرا واحدا.

void UpdateObjectRefCount(const int refStep);

هذه الدالة الخاصة تقوم بتعديل حساب عدد استعمالات العنصر المسير.

void CheckArray(void);

عند كل تغيير للعنصر المسير و ذلك بتغيير محتوى المؤشر m_pTPointer تقوم هذه الدالة الخاصة بالتعرف على ما إذا كان المؤشر يشير إلى مصفوفة أم إلى عنصر واحد.

const T *GCP_NULL;

هذا متغير ثابت للإستخدام مكان NULL و إخلاء m_pTPointer وجعلها NULL.

GCPointer(void);

لبناء مؤشر خال من أي عنصر مسير داخليا.

GCPointer(const GCPointer<T> &ptr);

للنسخ ابتداء من مؤشر اخر.

GCPointer(const T *pobject);

للنسخ ابتداء من مؤشر C/C++ عادي.

~GCPointer(void);

عندما يصبح المؤشر غير مستعمل, نتأكد من عدد استعمالات المؤشر المسير داخليا. إذا كان الصفر يلغى العنصر المشار إليه من طرف m_pTPointer. و في حالة ما إذا كان مستعملا نقوم بنقص عدد الإستعمالات فقط.

size_t GetCount(void);

دالة مساعدة لمعرفة عدد العناصر في m_pTPointer في حالة أن المؤشر يسير مصفوفة أما في حالة أن m_pTPointer مؤشر لعنصر واحد فالدالة ترجع العدد واحد.

size_t GetUsageCount(void);

دالة مساعدة لمعرفة عدد استعمالات العنصر المشار إليه من طرف m_pTPointer.

bool IsUnique(void);

ترجع true إذا كان المؤشر m_pTPointer مستعملا مرة واحدة.

bool IsEmpty(void);

ترجع true إذا كان m_pTPointer == NULL

void Reset(void);

نفس المفعول ك ptr = ptr.GCP_NULL ; و هو إخلاء المؤشر من العنصر أو المصفوفة أو فقط نقص عدد الإستعمالات.

بالنسبة لإمكانيات هذا المؤشر :

GCPointer<int> ptr = new int;
ptr = 10;
ptr = 5 + ptr;
GCPoitner<int> ptr1 = new int;
ptr1 = 10;
ptr1 = ptr;
ptr = ptr.GCP_NULL;
….

أما بالنسبة للمعاملات المتقدمة :

GCPointer<int> ptr  = new int (0);
*ptr += 10;
(*ptr)++;

أما بالنسبة للوصول إلى محتويال كلاس :

GCPointer<ClassTest> ptr = new ClassTest();
ptr->ValueMember = 0; يساوي (*ptr).ValueMember = 0;

أما بالنسبة للتعامل مع المحتويات و المؤشرات C/C++ :

Int *pi = new int;
GCPointer<int> ptr;
ptr = pi; يعني m_pTPointer = pi; بمعنى آخر حفظ العنوان.
ptr = *pi; يعني *m_pTPointer = *pi بمعنى آخر حفظ المحتوى.

أما بالنسبة لـ GC فما هو الآن إلا كلاس له hash table حيث يتم حفظ عدد الإستعمالات لعنوان في الذاكرة مسير من طرف مؤشر GCPointer. للكلاس SimpleGC دالتان :

int ReferenceCounter(void *pobject, int value)

عند الحاجة يقوم كائن GCPointer بالنداء على هذه الدالة لتعديل عدد استعمالات كائن في الذاكرة أو إشراك كائن جديد عند الحاجة.

void ObjectIsDead(void *pobject)

عندما ينعدم عدد استعمالات كائن بالذاكرة تتم المناداة على هذه الدالة لإعلامها بأن الكائن دي العنوان pobject قد تمت إزالته.

سنرضى لحد الآن بهذا GC البدائي حتى يتم التحقق من فعالية GCPointer و خلو الكلاس من أية أخطاء أو ثغرات حماية للعنصر المسير داخليا.

الملف المرفق يحتوي على مشروع ++C به كل من GCPointer و GC بالإضافة إلى برنامج صغير يقوم بعدد كبير من حجز قطع من الذاكرة و إحالتها إلى عناصر مختلفة GCPointer وفي النهاية يقوم GC بطلب Dump للذاكرة للتأكذ من عدم هروب قطع أو كائنات لم تتم إزالتها.

SimpleGC.rar

تم تعديل هذه المشاركة بواسطة محمد رضى في 10 يوليو 2009 في 16:27

#19

نسيت أن أذكر أن GCPointer يستطيع تسيير المصفوفات كما هو الشأن بالنسبة للمتغيرات :

	GCPointer<int> ptr = (int *)malloc(sizeof(int) * 3);
	ptr[0] = 17;
	ptr[1] = 4;
	ptr[2] = 1979;

و يستطيع التعرف أوتوماتيكيا على طبيعة المؤشر إذا كان مجرد متغيرة أم مصفوفة و التعرف على عددها.

#20

الآن و حتى ظهور أي تصحيح أو تطوير لـ GCPointer أود رفع بعض التقل عن الكلاس GCPointer و وضعها على كاهل GC لأنه و في النهاية هو من يجب أن يتكفل بكل شيْ و بالمقابل يصبح GCPointer خفيفا وليس مصدر ضياع وقت الوحدة المركزية.

أريد أن تصبح العلاقة بين GCPointer و GC على الشكل التالي :

GCPointer : كما في السابق مع تخفيف العبء الذي يكمن في مراقبة تعداد الاستعمالات و إزالة الكائنات عند الصفر. نريد من GCPointer ألا يتكلف إلا بزيادة أو نقصان تعداد استعمالات الكائن المسير.

GC : نريد منه أن يتكلف بكل شيء ابتداء من حفظ تعداد استعمالات الكائنات التي تسيرها كائنات GCPointer و عند وصول عدد استعمالات كائن الصفر يتكلف GC بإزالة الكائن من الذاكرة. إلى حد الآن لا نريد التطرق إلى Multi-threading وفي نفس الوقت نريد من GC ألا يقوم بالبحث عن الكائنات المرشحة للزوال إلا بعد مدة أو عدد من عمليات حجز للذاكرة.

قبل الدخول في تفاصيل تصميم GC الجديد, يجب أن نجد حلولا للمشاكل التالية :

- GC لا يستطيع التعرف على نوع كل كائن من الكائنات التي تم التبليغ عن وجودها من طرف الكائنات GCPointer و في هذه الحالة سنضطر إلى الجواب عن السؤال : كيف يمكننا إزالة كائن لا نعرف نوعه وخصوصا أذا كان من نوع كلاس وله مدمر أو destructor؟

- كيف يمكن لنا التعرف على عنصر من نوع كائن لكلاس أو كائن لنوع أساسي لـ ++C/C كـ bool, char, int, double إلخ لوضع صيغة إزالة لكل منها.

- ما العمل في حالة أن المؤشر الموجود لدى GC هو مؤشر base كلاس لـ derived كلاس؟

تحياتي...

#21

بالنسبة للسؤال عن كيفية إزالة كائن من دون معرفة نوعه فالأمر سهل في حالة عدم حجز الذاكرة ديناميكيا من طرف العنصر و عدم توفره على مدمر أو destructor! فمثلا :

class Test
{
public :
	int i;

	~Test(void)
	{
		i = 0;
	}
};

لنفترض أن pTest و pVoid هما مؤشران لنفس العنصر من نوع Test و لكن الأول مؤشر من نوع Test * أما الثاني فهو مؤشر void *.

delete pTest; تنادي على المدمر تم تزيل العنصر من الذاكرة.

delete pVoid; أو delete (void *) pTest; تزيل العنصر من الذاكرة من دون المناداة على المدمر لأنه لم يتم إخبار delete بنوع المؤشر لتتم إزالته بالطريقة المناسبة.

الحل يكمن في استعمال pure virtual class التي سترت كل كلاس تحتاج الإزالة من دون علم بنوعها الدالة DoExplicitDestructor التي ستتم المناداة عليها قبل الإزالة. مثال :

class XDestructor
{
public :
	virtual void DoExplicitDestructor(void) = 0;
};

class Test : public XDestructor
{
public :
	int i;
	char *buffer;

	Test(void)
	{
		buffer = new char[25];
	}

	void DoExplicitDestructor(void)
	{
		delete[] buffer;
	}

	~Test(void)
	{
		DoExplicitDestructor();
	}
};

تقوم الدالة التي تمت وراتتها من الكلاس XDestructor بعمل عمل المدمر الذي بدوره لا يقوم إلا بالمناداة عليها. و بهذه الطريقة يقوم المدمر بعمله العادي في كل الأحوال سواء عرفنا نوع الكلاس أو لم نعرفه.

الكود التالي يقوم بعمله بالطريقة المناسبة.

	void *ptr = pTest;
	((XDestructor *)ptr)->DoExplicitDestructor();
	delete ptr;

بهذه الطريقة يستطيع نظام GC القادم بإزالة العناصر بغض النظر عن نوعها و لكننا سنصبح غير قادرين (حتى هذه اللحظة) من التعامل مع العناصر الأساسية لـ C/C++ كـ int وdouble و غيرها. فما الحل؟

#22

لا أخفي عليكم... الأمر يزداد تعقيدا. الحل الوحيد الذي وجدته حتى هذه اللحظة و الذي لم أرد أن أطلعكم عليه لأن الكثير من مبرمجي سي++ لن يعجبهم و الأمر كذلك بالنسبة لي لا لأنه لا يحترم القواعد و الأعراف و لكن لأن هذا الحل سيتسبب في بطء شديد و ترد في فعالية البرامج التي ستعتمد على هذا GC في حالته هذه. و إذا, إذا ما أردنا معرفة محتوى قطعة من الذاكرة هل هو نوع أساسي لـ سي++ أم هو عنصر لكلاس فعلينا استعمال المعامل typeid و تمرير مؤشر لقطعة من الذاكرة بعد تحويله إلى نوع عنصر لكلاس ما :

void *pv = عنوان مقطع من الذاكرة
try {
bool b = typeid(*(Test *)pv) == typeid(Test); // هذه الجملة ستسبب استثناء من نوع std::__non_rtti_object إذا كان pv مؤشر لنوع أساسي لـ سي++
// إذا وصلنا إلى هنا فـ pv مؤشر لكائن لكلاس ما
} catch {
// إذا وصلنا هنا فـ pv مؤشر لعنصر أساس لـ سي++
}

لكن و للأسف فتسيير الإستثناءات يضيع الكثير من و قت الوحدة المركزية و بالتالي يسبب بطء شديدا و تدني فعالية البرامج و لهذا فهذا الحل غير مرغوب فيه.

تم تعديل GCPointer و GC و لم يتبقى إلا إيجاد الحل لهذه المشكلة ليكون بين أيدينا نظام GC لبرامج سي++

#23

السلام عليكم , كنت اريد وضع رد ولكن ترددت .. والان سأضيفه .

كنت سأقترح الغاء التعامل مع void pointer وتحذير من يريد استخدامه , بأنه غير مدعوم .

void pointer غير مهم كثيراً مع وجود template , يمكننا تقريباً الاستغناء عنه .. اذا اضطر المبرمج لاستخدامه , فليستخدمه بالطريقة المعتادة, وليستخدم GC في بقية الامور .. أرى أن اختبار البرنامج أهم من التركيز على هذه المشكلة .. المشكلة عويصة .. وحلّلك الذي طرحت .. حل مشكلة Class وادخلنا بمشكلة Primitive type .. لذلك أرى أن تلغيه الان من الاصدارة الاولى من المكتبة GC ( لنطلق عليها "مكتبة" .. ) , وتركز على اجراء تجارب وتحسين الكود .. ونحاول معاً التأكد من جودة من مكتبتك .. Quality Assurance .

هناك بعض النقاط أخي الكريم أتمنى أن أناقشها حول GC الذي بنيته .. ( أسئلة واختبارات لاداء المكتبة ..).. ولكن هل تريد أن افتح باب المناقشة .. حول أساسيات المكتبة التي بنيت .. أم تريد تأجيلها الى حين انتهاءك من تطوير النسخة الاولى منها .. أخشى أن " الخبط " عليك ورشة العمل هذه .. باعادتك لبداية الموضوع .

تم تعديل هذه المشاركة بواسطة الشمري في 15 يوليو 2009 في 21:01

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#24

تفضل أخي... البيت بيتك

#25

السلام عليكــم ورحمـة الله وبركاتــه ،،

تأخرت في الرد .. المعذرة .

عموماً هناك عدد من الاسئلة , ولكن أههمها :

- متى يبدأ GC .. قرأت ماكتبته :

اقتباس
. و لنقل و كبداية أن GC يشتغل كلما تم إنشاء عدد من المؤشرات وقد تم استغلال عدد ن من الكيلوبايت من الذاكرة.

ومع محاولة التطبيق لم يتضح لي أسلوب واضح .. حاولت تتبع الكود لكن ضعت قليلاً .

- الأمر الاخر هو بالنسبة الى Operators Overloading التي قمت باضافتها .. أجد أنها كثيرة كما قال الاخ خالد .

كان يكفي الاستغناء عن كثير من المعاملات ( الرياضية - المنطقية ) .. ويكفي اضافة معاملات تقوم بالحصول على قيمة المؤشر p* و العنوان p .. وبعض الامور الاخرى الهامّة بالنسبة لك .

عندما نحصل على المؤشر p* , نستطيع بعدها بأنفسنا أن نفعل ما نشاء من عمليات .. الهدف من هذا تبسيط الكود .. والتركيز على مهمّة GC .

اذا كانت الاسئلة تبدو بدائية .. فتاجهلها :blush:

بالتوفيق .

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

مواضيع مشابهة