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

هل من تفصيل عن الــ OOP في السي++ ؟

مغلق
بدأه saroona في 10 مارس 2005 · 4 رد · 3,656 مشاهدة · في الأسئلة المجابة
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

مرحبا

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

و شكرا.

تم تعديل هذه المشاركة بواسطة أحمد الشنقيطي في 28 يونيو 2012 في 01:46 — السبب: تعديل العنوان.

#2

(ملاحظة, رجاءا يا مشرفي المنتدى عدلو الـ code tags كي لا تمحو الـ indenting اللذي قمنا به :( )

ما هو الكلاس؟

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

سأضرب مثالا صغيرا: أذكر في احدى المرات عندما كنت أقرأ كتابا عن برمجة الألعاب ثنائية الأبعاد باستخدام direct draw, قدم الكاتب مثالا صغيرا يرسم فيه كرة صغيرة تتحرك (و هناك أشياء اخرى .. ليس هذا موضوعنا) فقام بكتابة حوالي أربع متغيرات أو أكثر و كانت كلها global variables من أجل تمثيل موقع الكرة في الشاشة و سرعتها ball_x, ball_x ball_dx, ball_dy ثم قام بكتابة بعض الـ functions من أجل تحريك الكرة و اختبار سرعتها .. الخ. هذه طريقة سيئة جدا في البرمجة, أولا متغيرات الكرة كلها global, يعني لو كان لدينا ثلاث كرات مثلا, فعلى هذا الأساس نحتاج الى 12 متغير ball1_x, ball2_x ball3_x الخ. ثانيا, المتغيرات بهذه الطريقة لا علاقة تربطها مع بعضها, كان من الأفضل جمعها في struct و من ثم بدل إنشاء اربع متغيرات يتم إنشاء متغير واحد من هذا الـ struct, حتى لو أردنا عدة كرات فنستطيع إنشاء array تحتوي عدة عناصر من هذا الـ struct.

على فكرة, كان يمكن أيضا لو أردنا عدة كرات ان نعمل عدة arrays من كل متغير ball_x[3] و لكن ماذا لو قمنا بإنشاء ball_x[3] و ball_y[5] ؟!

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

الخطوة التالية للوصول الى الـ class هي أخذ الـ functions اللتي تقوم بالتعامل مع متغيرات الكرة و دمجها في الـ struct.

فالـ class من وجهة النظر اللغوية البحتة (لغة البرمجة) هو جمع الـ variables مع الـ functions في هيكل واحد.

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

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

قبل البدء في كتابة البرنامج عليك أن تقوم بتحليل المشكلة الموجودة بين يديك, ثم تقوم بتصميم حل برمجي لها.

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

بالنسبة للتصميم, فعادة في البرمجة "الإجرائية" procedural programming فإننا نبحث عن functions أي عن عمليات تحدث داخل البرنامج, مثلا فتح ملف جديد قد يكون function, طباعة التقرير على ورقة أيضا قد يعتبر function .. الخ. (و هذا كلام غير دقيق تماما)

أما في البرمجة الشيئية, فإننا نبحث عن "أشياء" او لنقل "كائنات", مثلا, الملف هو عبارة عن كائن, البرنامج هو عبارة عن كائن, التقرير أيضا كائن, البرنامج نفسه هو كائن, لو فرضنا أن لدينا برنامجا للرسم يستخدم الـ layers مثل الفوتوشوب, فإن الـ layer هو كائن. مثلا في لعبة حاسوب, فإن اللاعب هو كائن, و السلاح اللذي يمسكه هو كائن, بل الحقيبة التي يضع فيها أسلحته هي أيضا كائن, و كذلك الطلقات عبارة عن كائنات, و أي وحش أو جندي هو كائن, الخ. و ليس هذا فقط, بل هناك كائنات لا نراها, فهي مجرد مفاهيم .. فالكائن ليس بالضرورة كائن حقيقي موجود في الواقع, بل ربما أي مفهوم عام يمكن ان نحوله إلى كائن, مثل كائن "العنوان", أو رقم الهاتف .. الخ.

فلطباعة الملف, نقوم بأخذ الملف (و هو عبارة عن كائن) و نخبر الطابعة (و هي كائن أيضا) عنه و نقول لها بأن تقوم بطباعته, مثلا.

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

بالنسبة للمصطلحات, فـ class تعني صنف, أو فئة, و هو يعني مجرد تعريف لنوع جديد من المتغيرات .. فهو تصنيف لفئة معينة من الكائنات, أما الكائن فهو ترجمة لكلمة object.

لنأخذ مثالا لـ class لا يقوم بشيء ذو فائدة و إنما هو مجرد مثال:

class Example
{
public:
	int number;
	char letter;
	bool valid;

	void Initialize()
	{
  number = 0;
  letter = 0;
  state = false;
	}

	void Fill( int n, char l )
	{
  number = n;
  letter = l;
  state = true;
	}

	void PrintData()
	{
  cout << "number = " << number << endl;
  cout << "letter = " << letter << endl;
	}
}

هذا الصنف من الكائنات له ثلاثة خصائص: رقم و حرف و حالة صحة (صحيح او خاطيء), و يمكننا القيام عليه بثلاث عمليات: نستطيع ان نصفره, و أن نملئه ببيانات معينة, و نستطيع جعله يطبع هذه البانات (كما هو واضح من اسماء الـ functions) ولاحظوا طريقة تعريف المتغيرات و العمليات, لن أشرحها لأنها واضحة, الشيء الوحيد الغيب بعض الشي هو شعار public و هو محدد لدرجة الوصول لما يليه من المتغيرات .. الآن فقط خذوه كما هو, سنشرحه بشكل مفصل أكثر لاحقا إن شاء الله.

بعض المصطلحات اللتي ينبغي التنويه إليها:

المتغيرات في الـ class تمسى member variables و العمليات تسمى عادة methods او member functions.

لاحظوا أيضا ان Example هو اسم الـ class يعني هو اسم "النوع" type الجديد اللذي تم تعريفه من المتغيرات, فهو بحد ذاته ليس متغير, فلا يمكن القول مثلا:

Example.Fill( 3, 'r' );

لأنه لا يوجد كائن محدد إسمه Example, بالظبط مثل ما لا نستطيع القول:

int = 10;

فالـ int مجرد اسم لنوع من المتغيرات, و ليس اسما لمتغير .. فعلينا أولا إنشاء متغير من هذا النوع قبل استخدامه.

لإنشاء كائن من صنف معين فإن ما نقوم به هو بالظبط ما نقوم به عند إنشاء أي متغير من أي نوع عادي.

int x;
Example a;

فالآن نستطيع ان نرسل "رسائل" الى هذا الكائن a كالتالي:

a.Initialize();
a.Fill( 3, 'r' );

هذه الأسطر تتصرف مثل اي سطر عادي فهو مجرد استدعاء لـ functions و لكن .. هذا الـ function يتم استدعائه في مقابل كائن معين, أو على كائن معين, و هذا الكائن هنا هو a, ولاحظوا تعريف الـ function المسمى Fill:

	void Fill( int n, char l )
	{
  number = n;
  letter = l;
  state = true;
	}

كل ما يقوم به هو تعيين البارامترات المرسلة الى المتغيرات المحلية الخاصة بالكائن .. فلو كان لدينا كائن آخر من نفس النوع لكن اسمه b, فإن استدعاء a.Fill لن يؤثر أبدا على الكائن b.

... يتبع إن شاء الله.

تم تعديل هذه المشاركة بواسطة hasan_aljudy في 11 مارس 2005 في 08:25

#3

السلام عليكم

شرح جيد وجميل مع اني لم اقرا الا يدايته.

بس حبيت اسجل اعجابي فيك اخي hasan_aljudy وحقيقة كسبنا عضو جيدا واتوقع انه محترف.

ماشاء الله عليك أكمل على هذا المنوال ولاتتردد.

بالتوفيق .

اللهم علمنا ما ينفعنا وأنفعنا بما علمتنا أنك أنت العليم الحكيم

سبحان الله وبحمده سبحان الله العظيم

لاحول ولاقوة الا بالله( كنز من كنوز الجنة ).

#4

شكرا يعني بس انا لا محترف ولا شي .. بس متعلم كلمتين جاي اتفلسف بيهم :lol:

تم تعديل هذه المشاركة بواسطة hasan_aljudy في 12 مارس 2005 في 01:10

#5

تكملة:

بالنسبة للـ public, هذا قصته طويلة شوية.

المثال الأول اللذي طرحته هنا عن الكرة اللتي متغيراتها عبارة عن global variables فيه مشكلة أخرى .. و هي أن المتغيرات كلها global يعني يستطيع اي function أن يصل إليها و أن يغيرها و أن يجعل الكرة تتصرف بشكل غير متوقع أو غير مرغوب, لذلك عندما نضع هذه المتغيرات في class فإننا نريد أن نحل هذه المشكلة! بمعنى أننا نريد منع اي شخص خارج الـ class من الوصول الى هذه المتغيرات أو تغييرها بطريقة مباشرة؛ هذا يتم عن طريق وضع كلمة private: قبل المتغيرات, و نستطيع وضع هذه الكلمات (private و public) عدة مرات, بحيث بعض العناصر تصبح private و بعضها يصبح public.

بشكل عام, من الأفضل دائما أن تكون المتغيرات كلها private و العمليات methods ممكن جعل بعضها public و بعضها private حسب الحاجة.

الفكرة هي أنك عندما يكون لديك class فهو تعريف لصنف من الـobjects او الكائنات, و هذه الكائنات تتصرف بطريقة معينة, يعني لديها أفعال و تصرفات تحدد صفاتها؛ السيارة مثلا, هي كائن, و لها تصرفات او أفعال, ولنفرض مثلا أنك تريد فتح باب السيارة الرئيسي, كل ما عليك القيام به هو إرسال "رسالة" الى السيارة لفتح الباب, مثلا:

car.openDoor();

و لكن ما اللذي تفعله السيارة لتفتح بابها؟ ماللذي يحدث داخل هذا الـ function؟ لا يهم, و ربما من كتب الـ class لا يريدك أن تعرف, و بغض النظر عما لو كنت تعرف أم لا, فالفكرة هي أن هناك أجزاء من الـ class غير مسموح لك بالوصول إليها, فهناك متغيرات داخلية في هذا الكائن, و انت كمستخدم لهذا الكائن لا يجب ان تحاول العبث بالمتغيرات الداخلية له, لأن ذلك قد يؤثر سلبا عليه و يجعله يتصرف بطريقة غير متوقعة او غير مرغوب بها.

ربما لديك الكود الكامل للـclass و ربما تعرف بالظبط ما اللذي يحدث داخل هذا الـ method, و لكن الكائن فقط هو من يستطيع الوصول الى متغيراته الداخلية.

مثلا لو كان عندنا لاعب يتحرك داخل مرحلة في لعبة ما, و هناك متغير داخل اللاعب لتمثيل احدثيات موقعه في المرحلة, و قمنا بتوفير واجهة (مجموعة من الـ public methods) لتحريك اللاعب يمينا و يسارا و في كل الاتجاهات.

ما اللذي سيحدث لو قام شخص آخر بالوصول الى المتغير الداخلي للاعب و وضع له رقما كبيرا بحيث يصبح هذا الموقع خارج نطاق الخريطة أو المرحلة؟ لتفرض أن مرحلتنا صغيرة و أنها ثنائية الأبعاد و أن أكبر مساحة لديها هو (100,100), ماذا سيحدث لو قام ذلك الشخص بتعيين القيمة (200,200) لملمتغير المخصص لمكان اللاعب؟! سيخرج اللاعب خارج المرحلة, و هو أمر غير متوقع و ليس من المفروض أن يحدث, و لو حدث أثناء لعب اللعبة لأعتبر خطأ برمجيا bug.

المفروض ان الـ class و الـ methods الموجودة به و المسؤولة عن تحريك اللاعب, تقوم بالتأكد من هذه الأمور (من ان موقع اللاعب هو موقع صحيح!) لذلك فإن أي محاولة خارجية للتدخل في هذه التفاصيل ستعرقل عمل الـ class و تسبب مشاكل و نتائج غير متوقعة.

من هنا جائت فكرة الـ private و وضع متغيرات أو عمليات خاصة بالـ class بحيث لا يستطيع أحد خارج الـclass من الوصول اليها و تغييرها, و أي محالة للوصول الى هذه المتغيرات يجب ان تتم عن طريق methods يقوم الـ class بتوفيرها.

هنا نسخة معدلة توضح تطبيق بعض المبادئ اللتي تحدثنا عنها:

class Example
{
private:
	int number;
	char letter;
	bool valid;

public:
	void Initialize()
	{
  number = 0;
  letter = 0;
  state = false;
	}

	void Fill( int n, char l )
	{
  //don't allow negative numbers
  if( n < 0 )
  {
 	 n = 0;
  }

  number = n;
  letter = l;
  state = true;
	}

	int GetNumber()
	{
  return number;
	}

	char GetLetter()
	{
  return letter;
	}

	bool IsValid()
	{
  return state;
	}

	void PrintData()
	{
  cout << "number = " << number << endl;
  cout << "letter = " << letter << endl;
	}
}

هنا قمت بجعل كل المتغيرات private و قمت بكتابة accessor methods لتسمح للعالم الخارجي بقراة بعض المعلومات عن الكائن مثل GetNumber الخ.

كما قمت أيضا بتعديل Fill بحيث يرفض استقبال الأرقام السالبة, لكي أوضح تحكم الـ class بمتغيراته الداخلية, فنحن هنا و لسبب ما لا نريد لهذا الكائن ان يكون المتغير الداخلي الخاص به number في السالب أبدا؛ فهنا لو سُمِحَ لشخص خارجي بتغير هذا الرقم فمن الممكن ان يجعله في السالب, بعكس ما نريد و نتوقع! في هذا المثال طبعا فإن وضع الرقم في السالب لن يؤثر شيئا, لكن في البرامج الحقيقة فإن مسألة المحافظة على سلامة المتغيرات مهمة جدا.

هذا الموضوع مغلق.

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