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

تقنية تعدد الوجهيات (الاوجه - الاشكال) polymorphism

بدأه time1 في 31 أغسطس 2006 · 9 رد · 9,586 مشاهدة · في المواضيع والدروس
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

بسم الله الرحمن الرحيم

اقدم اليوم لكم أول درس لي في هذا المنتدى وهو درساً في تقنية من أهم تقنيات البرمجة الغرضية التوجيه opject-oriented programming

الا وهو تعدد الاشكال او الاوجه (الوجهيات ) او كما يسمى polymorphism

polymorphism

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

تعتمد تقنية تعدد الوجهيات polymorphism على تقنية الوراثة inheritance و المؤشرات pointers

ونعلم نحن اهمية الصفوف في انشاء اغراض عامه تستخدم في برامج كثيرة حسب حاجتنا لها مما يسهل لنا تصميم البرامج وهذا ماسميى بـ "التعميم" ثم نعلم ان الوراثة inheritance تزيد من مبدأ التعميم حيث اننا نستغل صفوفنا حسب حاجة البرنامج . اما تقنية تعدد الوجهيات (الاشكال - الاوجه) polymorphism تزيد من عمومية الصفوف وتسهل استخدامها خاصه تلك التي ترتبط في بعضها عن طريق وراثه متعددة المستويات.

_______________________________________________________________________

دعونا نتفحص هذا المثال:

#include<iostream>
using std::cout;
using std::endl;

class A
{
public:
	A(int=0);
	print();
protected:
	int x;
};
A::A(int a)
{
x=a;
}
A::print()
{
		cout<<"This Class is A and The value of X is :"<<x<<endl;
}

class B:public A
{
public:
	B(int=0,int=0);
	print();
private:
	int y;
};
B::B(int a,int b):A(a)
{
	y=b;
}
B::print()
{
		cout<<"This Class is B and The value of X+Y is :"<<x+y<<endl;
}
main()
{
	A one(1);
	B two(5,10);
	one.print();
	two.print();

	A *p=NULL;
	p=&one;
	p->print();
	return 0;
}

من خلال هذا المثال ستكون المخرجات

This Class is A and The value of X is :1

This Class is B and The value of X+Y is :15

This Class is A and The value of X is :1

السطران الاول والثاني من المخرجات واضحان , لكن السطر الثالث لم يفترض به ان يستدعي دالة print في الصف A بل كان من المفروض ان يستدعي دالة print الخاصة للصف B لانه تم اسناد عنوان الغرض الخاص بالصف B إلى المؤشر الخاص بالصف A

لذلك نتوصل هنا إلى قاعدة مهمة جدا ً

استدعاء مؤشر يعتمد على نمط المؤشر وليس الغرض المؤشر عليه.

حسنا الان لو اردنا ان نصحح ماحدث سابقا ً ,فعندها سنتكلم عن التوابع الظاهرية.

تأشير مؤشر صف مشتق على اغراض الصف الاساسي

اي ان نعطي مؤشر من نوع صف مشتق عنوان غرض صف اساسي.

وينتج من خلال هذه المحاولة خطأ لان اغراض الصف الاساسي ليست اغراض للصف المشتق لذلك :

يجب ان نفهم حاليا: بأن أي مؤشر من صف لا يمكن ان يأخذ الا عنوان الصف نفسه او عنوان صف اقل من مستوى في البنية الهرمية (تختلف هذه النظرية عندما نتعلم القسر الادنى) .

محاولة استدعاء توابع الصف المشتق باستخدام مؤشر الى الصف الاساسي

ايضا تحدث خطا.لعدم توفر التوابع في الصف الاساسي

التوابع الظاهرية

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

ولتفعيل هذا السلوك يجب علينا تعريف التوابع الاعضاء على انها ظواهر (ظاهرية) ويكون تعريفها في الصف الاساسي.

قاعدتها virtual function

لنرا استخدامها على مثالنا السابق

#include<iostream>
using std::cout;
using std::endl;

class A
{
public:
	A(int=0);

	virtual print();

protected:
	int x;
};
A::A(int a)
{
x=a;
}
A::print()
{
		cout<<"This Class is A and The value of X is :"<<x<<endl;
}

class B:public A
{
public:
	B(int=0,int=0);
	print();
private:
	int y;
};
B::B(int a,int b):A(a)
{
	y=b;
}
B::print()
{
		cout<<"This Class is B and The value of X+Y is :"<<x+y<<endl;
}
main()
{
	A one(1);
	B two(5,10);
	one.print();
	two.print();

	A *p=NULL;
	p=&two;
	p->print();
	return 0;
}

ستكون المخرجات

This Class is A and The value of X is :1

This Class is B and The value of X+Y is :15

This Class is B and The value of X+Y is :15

هنا نكون قد صححنا السلوك السابق

وهنا نجد ان:

التوابع الظواهر(الظاهرية - الافتراضية) تعيد الدالة التابعه للغرض المسند للمؤشر وليس للنمط المؤشر.

الان ماذا لو قررنا ان نحذف دالة print من صف B ونفذنا البرنامج كما هو :

main()
{
	A one(1);
	B two(5,10);
	one.print();
	two.print();

	A *p=NULL;
	p=&two;
	p->print();
	return 0;
}

فعندها ستكون المخرجات

This Class is A and The value of X is :1

This Class is A and The value of X is :5

This Class is A and The value of X is :5

بما ان دالة print حذفت من B فقد تم استدعاء دالة print في صف A ولكن بقيمة X التي اعطيت لصف B , هذا السلوك تم بسبب تعريف التابع print على انه ظاهري. ملاحظة:

"يلاحظ المجتهد : بأنه لو حذفنا كلمة virtual من دالة print في صف A فستكون نفس المخرجات " .

هذا صحيح . ولكن وقتها سيكون استدعاء الدالة في الصف الاساسي بغض النظر عن تواجد الدالة بالصف المشتق اما لا.

فلو كان هناك ثلاثة صفوف A,B,C وجميعهما يحتويان على دالة print وتم استدعاء دالة print الخاصة بالصف C بواسطة مؤشر على A فانه سيتم استدعاء دالة print بالصف الاساسي , اما اذا كانت دالة print معرفة على انها ظاهرية وتم استدعاء دالة print الخاصة بالصف C بواسطة مؤشر على A فسيتم استدعاء الدالة الخاصة بـ C بشكل صحيح لكن .؟!

ماذا ان كانت دالة print غير موجودة بصف C

هنا ننظر اذا كانت دالة print بصف B معرفة على انها ظاهرية فسيتم استدعائها .. وان لم تكن كذلك فسيتم استدعاء دالة الصف الاساسي A

إلى هنا اكون قد اكفيت درسي الأول في هذا الصدد

وقريبا سنتكلم عن:

استعمالات اخرى لـ

polymorphism

تعلميات switch ومقارنتها مع polymorphism

الصفوف المجردة والصفوف المجسدة

الصوافي pure virtual

جدول التوابع الظاهرية virtual function table

vtable

المدمرات الظاهرية

القسر الديناميكي واستخدام

dynamic_cast

و

typeid

اتمنى ان اكون وفقت في هذا الدرس , هذا والله أعلم العالمين

سبحانك اللهم لا علم لنا الا ماعلمتنا

تقبلوا تحياتي

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

#2

درس جميل تشكر عليه..

رغم ان الترجمة لم تعجبني (أقصد المصطلحات).

#3

اشكرك استاذي على ردك الجميل الذي انار الموضوع.

اما بشأن الدرس فهو غير مترجم من اي كتاب ,بل الدرس من اجتهادي الشخصي.

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

تم تعديل هذه المشاركة بواسطة time1 في 1 سبتمبر 2006 في 00:31

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

#4

درس جميل .. وانا لا اعرف لماذا الى الان لم أهضم تعدد الاوجه .. يمكن لاني لم استخدمها في برامجي الى الان :) .

بالنسبة للمصطلحات كلها مرت علي .. الا مصطلح ( الصف ... class ) بعضهم يسميها الصنف ...او الفصيل .

الافضل ان نبقى على التسمية الانقليزي للمصطلحات .

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

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

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

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#5

شيء جميل

جزاك الله خيرا

بالنسبة للمصطلحات اكتبها كما هي بالإنجليزية

سيكون أفضل

إلى أن تنتهي مهزلة عدم وجود ترجمة موحدة للمصطلحات

System Engineer

Red Hat Certified Engineer

Red Hat Certificate Number

805010381752078

#6

السلام عليكم

اموت لنا في Polymorphism :)

بالنسبة للمصطلحات شخصيا لا اعرف اي مصطلح بالعربية

من الأحسن كتابة المصطلح باللغتين، العربي والأنجليزي بين فقوسين، ودعنا نتعلم :D

d4baa0.gif
#7

تحية..

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

على كلّ هذا مجرّد رأي وكنت اتمنّى الاّ يحرف مسار الموضوع..

اما عن class مثلا فالدارج ترجمتها على انها : فئة.

وpolymorphism تعدد الأشكال - أوتعدد الأوجه | أما وجهيات فلا أعلم اذا كانت كلمة وجهيّة لها معنى أصلا...

لكن لا أنسى أن أشكرك حقّا وليس مجاملة على تطرقك لهذه المواضيع..

درس مفيد جدّا وأسلوب شيّق (لولا قصة الترجمة :):) ) واهم شيء هو خروج من دائرة - دروس للمبتدئين وHello world>

#8

اشكرك استاذي على ردك الي اعتز فيه واعتبره شهاده افتخر بها .

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

اشكر جميع من رد على الموضوع واليوم بإذن الله سأطرح الدرس الثاني .

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

#9

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

أعلم أن الموضوع قديم ولكن لاحظت أنكم تكلمتم بالمصطلحات ونسيتم الأخطاء في الكود !! :happy:

يوجد خطأ على ما أعتقد في الكود1 :

main()
{
	A one(1);
	B two(5,10);
	one.print();
	two.print();

	A *p=NULL;
	p=&one;
	p->print();
	return 0;
}

حيث أن الصحيح هو :

main()
{
	A one(1);
	B two(5,10);
	one.print();
	two.print();

	A *p=NULL;
	p=&two;
	p->print();
	return 0;
}

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

وأشكر أخي Time1 على الموضوع الرائعة جدا

جزاك الله كل خير يا أخ Time1

يعطيكم العافية ,,

تحيااااتي ,,

للصداقة البرمجية

pro-vb6@hotmail.com

#10
اقتباس
ونسيتم الأخطاء في الكود !! :happy:

يوجد خطأ على ما أعتقد في الكود1

من خلال الشرح تستطيع ان تستنتج انه يوجد خطأ مطبعي في الكود .

اقتباس
السطران الاول والثاني من المخرجات واضحان , لكن السطر الثالث لم يفترض به ان يستدعي دالة print في الصف A بل كان من المفروض ان يستدعي دالة print الخاصة للصف B لانه تم اسناد عنوان الغرض الخاص بالصف B إلى المؤشر الخاص بالصف A

لذلك نتوصل هنا إلى قاعدة مهمة جدا ً

ايضا انا كنت اعمل على نفس المثال طوال الشرح ولاحظ انه تم تعديل الخطأ في الكود الثاني والثالث

اقتباس
لنرا استخدامها على مثالنا السابق

شكرا لك ,

تحياتي . . .

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

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