الفئات classes
الفئات تميز لغة سي++ وتعطيها ادوات قوية جدا فقد تقوم بعملها انت والكثير منها يأتي اليك جاهزا لتسهيل عملك واظهار برامجك في شكل جيد بقدر ماساعدت مطوري البرامج بقدر ماجعلتهم دائمي الاطلاع على ماهو جديد منها وعودة دائمة الى كتب اللغة الصادرة من الشركات مثل بورلاند وميكروسوفت والبرمجة تحت الوندوز ماهي الا فئات عديدة تورث صفاتها الى برامجك لتعمل تحت الوندوز ربما يكون هذا رد على من يقول اننا ندرس اللغة تحت الدوس انها مقدمة وعودة الى موضوعنا فالفئات تشبه الى حد كبير هياكل البيانات التى سبق وشرحناها في درس سابق ولكن يمكن ان يكون احد اعضائها دالة تعمل معها ويمكن ان تورث احدى الفئات صفاتها الى فئة اخرى واليك الشكل العام للفئة وستكون الفئة التي سننشئها تحت اسم MyExample
#include <iostream.h>
class MyExample //Standard way of defining the class
{
public:
//This means that all of the functions below this(and any variables)
//are accessible to the rest of the program.
//NOTE: That is a colon, NOT a semicolon...
MyExample(); //Constructor
~MyExample(); //Destructor
void setspeed(int p);
int readspeed();
//These functions will be defined outside of the class
protected:
//This means that all the variables under this, until a new type of
//restriction is placed, will only be accessible to other functions in // the class. NOTE: That is a colon, NOT a semicolon...
int processorspeed;
}; //Do Not forget the trailing semi-colon
MyExample::MyExample()
{ //Constructors can accept arguments, but this one does not
processorspeed = 0;
//Initializes it to zero
}
MyExample::~MyExample()
{ //Destructors do not accept arguments
}
//The destructor does not need to do anything.
void MyExample::setspeed(int p)
{ //To define a function outside put the name of the function
//after the return type and then two colons, and then the name
//of the function.
processorspeed = p;
}
int MyExample::readspeed()
{ //The two colons simply tell the compiler that the function is part
//of the clas
return processorspeed;
}
int main()
{
MyExample compute;
//To create an 'instance' of the class, simply treat it like you would
//a structure. (An instance is simply when you create an actual object
//from the class, as opposed to having the definition of the class)
compute.setspeed(100);
//To call functions in the class, you put the name of the instance,
//a period, and then the function name.
cout<<compute.readspeed();
//See above note.
return 0;
}
الشرح
لقد بدئنا بالسطور التالية
#include <iostream.h>
class MyExample //Standard way of defining the class
{
public:
الامر calss للاعلان عن فئة يليه اسم الفئة وهو اختياري وتذكره جيدا فسوف تلتزم به فيما بعد في صياغة باقي هيكل الفئة ثم قوس لبداية بلوك الفئة ثم الامر public: وهو بالطبع يعنى عام وكل متغير او دالة تعلن بعده معناها يمكن استخدامها في بقية اجزاء البرنامج يلي ذلك الاعلان عن باني الفئة وهي دالة تنفذ آليا بمجرد بداية عمل الفئة وصيغتها كما يلي
MyExample(); //Constructor
اسمها ليس اختياريا ولكن عليك الالتزام باسم الفئة التي انشأتها ويوضع تحت هذه الدالة كما سيلي تفصيله ما تحتاج من تخصيص لمتغيرات عند بداية البرنامج
يلي ذلك هادم الفئة وهي دالة تستدى ايضا آليا عند انتهاء عمل الفئة ويمكن ان تضع فيها اوامر لتحرير الذاكرة مثلا من متغيرات تم تخصيصها او تهمل وضع اي شئ فيها حسب فكرة البرنامج وتكون بنفس اسم الفئة يسبقها العلامة ~ كما بالصيغة التالية
~MyExample(); //Destructor
كل ما سبق كان تحت الامر public: كما تتذكر ثم تحته ايضا فنحن لم ننهي العمل معه بعد وضعنا اعلان عن دالتين عامتين هما
void setspeed(int p);
int readspeed();
ثم دخلنا بعد ذلك الى الامر protected: ومعناه كل ما سيعلن عنه بعد ذلك لا يستخدم الا في الدوال اعضاء الفئة ولا يستخدم في باقي اجزاء البرنامج وكان ذلك في الصورة التالية
protected:
int processorspeed;
}
ثم انتهى الهيكل العام للفئة بقوس اغلاق البلوك ليبدء العمل في بناء الدوال المعلنه فكلها كانت اعلانات ولن يكون شكل الدالة بالطريقه السابق شرحها في درس الدوال لانها كانت دوال عامة ولكن ستأخذ شكل آخر لتعرف الكومبايلر انها تنتمي للفئة المعلنة وليس غيرها وستكون دالة الباني او باني الفئة اول دالة سننشئها هنا كما يلي
MyExample::MyExample()
{
processorspeed = 0;
}
بدء الاعلان عن الدالة بوضع اسم الفئة ثم اسم الدالة وبالطبع اسم الفئة واسم الدالة شئ واحد كما اتفقنا ان باني الفئة يأخذ اسم الفئة كفرض عليك ثم وضعنا قيمة ابتدائية للمتغير processorspeed تساوي صفر انتهازا لفرصة ان هذه الدالة اول ما ينفذ عند العمل على الفئة
ثم بناء دالة هادم الفئة وكما سبق شرحة هو ايضا اسم الفئة يسبقها العلامة ~ وهي اخر دالة تنفذ وذلك كما يلي
MyExample::~MyExample()
{ //Destructors do not accept arguments
}
لم نضع فيها شئ لعدم حاجتنا لذلك ثم بناء باقي الدوال اعضاء الفئة وسوف نرى فيها شكل الاعلان عن دالة عضو لفئة
void MyExample::setspeed(int p)
{
processorspeed = p;
}
لقد كان الاعلان عن الدالة داخل هيكل الفئة بالصورة التي تعودنا عليها وهي
void setspeed(int p);
عبارة عن دالة لاتعيد قيمة بناء على الامر void ثم اسم الدالة وبين القوسين القيمة التي ستمرر للدالة وهنا عبارة عن تمرير عدد صحيح للدالة كل ذلك سبق ان شرحناه ولكن الجديد هنا هو عنوان الدالة عند أنشائها كان كما يلي
void MyExample::setspeed(int p)
|
void |
نوع الدالة |
|
MyExample |
اسم الفئة التي تنتمي لها |
|
:: |
زوجين من نقطتين فوق بعضهما |
|
setspeed |
اسم الدالة |
|
(int p) |
القيم الممرة للدالة |
سوف اوفر عليك جهد التفكير في ذلك ضع نفس الاعلان عن الدالة كما هو وبعد نوع الدالة وقبل اسمها ضع اسم الفئة يليها نقتطتين وهذه امثلة
|
الاعلان داخل الفئة |
شكل الاعلان في تعريف وبناء الدالة |
|
void seeme (void) |
voidItsCalssName::seeme (void) |
|
int seeme (void) |
|
|
float seeme (void) |
float ItsCalssName:: seeme (void) |
|
void seeme (int a) |
void ItsCalssName:: seeme (int a) |
|
void seeme (float a) |
void ItsCalssName::seeme (float a) |
|
int seeme (float a) |
int ItsCalssName:: seeme (float a) |
كان ذلك على فرض ان اسم الفئة التابع لها هو ItsCalssName والذي يناظر معنا MyExample
ثم تم بعد ذلك بناء باقي الدوال او الدالة الباقية كما يلي
int MyExample::readspeed()
{
return processorspeed;
}
تم الانتهاء من بناء الفئة ودوالها حسب فكرة البرنامج ثم دخلنا الى الدالة الرئيسية بالبرنامج وأي برنامج آخر لنستدعي الفئة ونراها كيف تعمل
int main()
{
MyExample compute;
compute.setspeed(100);
cout<<compute.readspeed();
//See above note.
return 0;
}
لاستدعاء الفئة يجب ان نعطيها حدث او معامل مثل هياكل البيانات تماما فقد سبق ودرسنا ذلك وهو متغير اختياري نستخدمه لاستدعاء دوال الفئة وهنا افترضناه compute
والاعلان عنه يكون باسم الفئة ثم هو اي المتغير الافتراضي كما يلي
MyExample compute;
بعد ان اخترت اسم المعامل اصبح فرضنا عليك عدم تغييره ولاستدعاء دالة ضع اسم المعامل يليه نقطة والدالة المطلوبة تماما كما كان في هياكل البيانات
compute.setspeed(100);
استدعينا الدالة setspeed والمعلن عنها داخل الفئة بالشكل التالي للتذكره
void setspeed(int p);
وبالتالي مررنا اليها قيمة عددية وهي 100 وهل لاحظت شئ لقد استدعينا دالة عضو للفئة خارج فئة كيف ذلك – نعم لقد وضعناها تحت بند public وقد قلنا عما يندرج تحته اقصد الامر public ان دواله يمكن استخدامها في اي مكان وهي دوال عامة
تم تمرير القيمة 100 للدالة setspeed فبدء استدعاء باني الفئة ليخصص للمتغير القيمة صفر وبعد ذلك استعيت الدالة المطلوبة حيث يوجد بداخلها امر لتخصيص القيمة المررة للمتغير processorspeed وداخل الدالة الرئيسة للبرنامج تم استدعاء دالة عامة من دوال الفئة وهي الدالة readspeed وهي تعيد قيمة المتغير processorspeed وذلك لطباعتها وهي 100 ايضا لتريك ان متغيرات الفئة مرئية بالنسبة لباقي الفئات دون الحاجة لاعادة الاعلان عنها داخل كل دالة من دوال الفئة.
ولبيان عمل باني الدالة لتعرف حقيقته دعنا نعدل البرنامج السابق وسوف اضع لون اصفر على التعديل الطفيف الذي سوف اجريه وسيكون على الصورة التالية
#include <iostream.h>
class Computer //Standard way of defining the class
{
public:
//This means that all of the functions below this(and any variables)
//are accessible to the rest of the program.
//NOTE: That is a colon, NOT a semicolon...
Computer();
//Constructor
~Computer();
//Destructor
void setspeed(int p);
int readspeed();
//These functions will be defined outside of the class
protected:
//This means that all the variables under this, until a new type of
//restriction is placed, will only be accessible to other functions in the
//class. NOTE: That is a colon, NOT a semicolon...
int processorspeed;
};
//Do Not forget the trailing semi-colon
Computer::Computer()
{ //Constructors can accept arguments, but this one does not
processorspeed = 17;
//Initializes it to zero
}
Computer::~Computer()
{ //Destructors do not accept arguments
}
//The destructor does not need to do anything.
void Computer::setspeed(int p)
{ //To define a function outside put the name of the function
//after the return type and then two colons, and then the name
//of the function.
processorspeed = p * processorspeed;
}
int Computer::readspeed()
{ //The two colons simply tell the compiler that the function is part
//of the clas
return processorspeed;
}
int main()
{
Computer compute;
//To create an 'instance' of the class, simply treat it like you would
//a structure. (An instance is simply when you create an actual object
//from the class, as opposed to having the definition of the class)
compute.setspeed(100);
//To call functions in the class, you put the name of the instance,
//a period, and then the function name.
cout<<compute.readspeed();
//See above note.
return 0;
}
لقد وضعت في باني الفئة القيمة 17 بدلا من صفر للمتغير processorspeed وقمت بتعديل الدالة setspeed لكي تكون قيمة المتغير عبارة عن قيمته السابقة اي التي سبق تخصيصها في الباني لانه اول ما يستدعى وتضرب هذه القيمة في القيمة الممررة بالمعادلة التالية
processorspeed = p * processorspeed;
اي ان القيمة الجديدة تساوي القيمة القديمة اي 17 مضروبة في القيمة p التي ستمرر للدالة لتصبح قيمة processorspeed = 17*100 اي 1700
اذا نفذت البرنامج سوف تعيد الدالة readspeed قيمة المتغير الجديدة 1700 وبالتالي اتضحت امامك فائدة باني الفئة ولك مني كل تحية.
المتغيرات العامة والمحلية Global & Local Variables
المتغيرات العامة هي متغيرات يعلن عنها خارج الدالة الرئيسة للبرنامج وتكون متاحة لكل الدوال لاستخدامها دون اعادة الاعلان عنها اما المتغيرات المحلية فيعلن عنها داخل اي دالة ولا يمكن استخدامها الا داخل الدالة نفسها وبانتهاء عمل الدالة ينتهي مفعولها وتفقد قيمتها ربما كان هذا الدرس مكانه الدروس الاولى ولكني ارى غير ذلك حيث ان المستخدمين الجدد في برامجهم الاولى والتي تكون بسيطة لايحتاجون ذلك فمعظم المتغيرات التي يستخدمونها تكون محلية ولنأخذ مثال لتوضيح ذلك وهو عبارة عن شرح فقط
#include <iostream.h>
int A,B,C;
void first (void);
void second (void);
void third (int X);
int main()
{
int D,E,F;
/*
يمكنك هنا التعامل مع المتغيرات A,B,C لانها متغيرات عامة خاصة
بكل البرنامج واي تعديل فيها سينعكس على قيمتها عن اعادة
استخدامها في اي جزء من البرنامج واي دالة
*/
/*
يمكنك هنا التعامل مع المتغيرات D,E,F لانها متغيرات محلية خاصة
بهذه الدالة واي تعديل فيها سيؤثر فقط داخل هذه الدالة
*/
/*
لايمكنك التعامل مع المتغيرات G,H,I لانها متغيرات محلية لدالة اخرى
*/
/*
لايمكنك التعامل مع المتغيرات K,L,M لانها متغيرات محلية لدالة اخرى
*/
}
void first (void)
{
int G,H,I;
/*
يمكنك هنا التعامل مع المتغيرات A,B,C لانها متغيرات عامة خاصة
بكل البرنامج واي تعديل فيها سينعكس على قيمتها عن اعادة
استخدامها في اي جزء من البرنامج واي دالة
*/
/*
يمكنك هنا التعامل مع المتغيرات G,H,Iلانها متغيرات محلية خاصة
بهذه الدالة واي تعديل فيها سيؤثر فقط داخل هذه الدالة
*/
/*
لايمكنك التعامل مع المتغيرات D,E,F لانها متغيرات محلية لدالة اخرى
*/
/*
لايمكنك التعامل مع المتغيرات K,L,M لانها متغيرات محلية لدالة اخرى
*/
}
void second (void)
{
int K,L,M;
/*
يمكنك هنا التعامل مع المتغيرات A,B,C لانها متغيرات عامة خاصة
بكل البرنامج واي تعديل فيها سينعكس على قيمتها عن اعادة
استخدامها في اي جزء من البرنامج واي دالة
*/
/*
يمكنك هنا التعامل مع المتغيرات K,L,Mلانها متغيرات محلية خاصة
بهذه الدالة واي تعديل فيها سيؤثر فقط داخل هذه الدالة
*/
/*
لايمكنك التعامل مع المتغيرات G,H,I لانها متغيرات محلية لدالة اخرى
*/
/*
لايمكنك التعامل مع المتغيرات D,E,F لانها متغيرات محلية لدالة اخرى
*/
}
void third (int X);
{
int A,B,C;
/*
يمكنك هنا التعامل مع المتغيرات A,B,C لانها اصبحت متغيرات محلية
في هذه الدالة والتغيير فيها لن يؤثر الا هنا فقط ولن يؤثر على
قيمتها في باقي البرنامج لاعادة الاعلان عنها كمتغير محلي للدالة
*/
/*
يمكنك الاعلان عن اي نوع من المتغيرات بالرموز والاسماء التي تفضلها
ما عدا المتغير X فقد تم تمريره للدالة وبالتالي كل متغير يمرر لداله
يكون محجوز كاسم ولا تستطيع استخدامه بينما يمكنك اعادة تعريف متغير
عام فقط سيصبح متغير محلي – هذه ملاحظة هامة جدا
*/
/*
لايمكنك التعامل مع المتغيرات G,H,I لانها متغيرات محلية لدالة اخرى
*/
/*
لايمكنك التعامل مع المتغيرات D,E,F لانها متغيرات محلية لدالة اخرى
*/
}
لقد اوضحت لك بشرح واضح جدا الفرق بين نوعيات المتغيرات المحلية والعامة بما يكفي للمرحلة التي انت فيها الآن مع العلم بأن هناك الكثير مما يجب ان يقال ولكن لاتشغل بالك يأتي ذلك في كورس متقدم قليلا للغة سي ++
المؤشرات Pointers
المؤشرات في سي ++ من الاشياء التي تبدو غامضة وقد يبدو في بعض الاحيان انها تتعامل مع اشياء غامضة وقد يطرح سؤال لماذا استخدامها انا احب استخدامها مع قلة استخدامي لها الا في الاشياء التي تحتاجها لقد نوهنا عنها في درس سابق ولكن اود ان اقوال انها من مميزات سي++ انها تجعلك تقترب من ذاكرة الكومبيوتر كثيرا سأوضح لك ببساطة شديدة طبيعة عمل المؤاشرات او pointers المؤشرات اما ان تعطيك عنوان متغير بالذاكرة او تعطيك القيمة التي يحملها هذا العنوان كمثال عند سؤالك عن رقم غرفة محمد على بفندق السعادة الاجابة ستكون 813 وهذا يشبه السؤال عن عنوان تخزين متغير بالذاكرة أو ان تسأل عمن يسكن الغرفة رقم 813 بفندق السعادة فستكون الاجابة انه محمد علي وهو يشبه السؤال عن قيمة المتغير المخزن في عنوان ذاكرة
ان اي متغير يسبق بالعلامة * يعتبر مؤشر ودعني اوضح بمثال الفرق بين الاعلان عن مؤشر والاعلان عن قيمة
int x;
سبق وتعاملنا مع هذه الحالة انها اعلان عن متغير اسمه x كمتغير عددي صحيح ولكن
int *x;
انه مؤشر – اذا ما الفرق بينهما الاول يشير الى متغير سيحمل قيمة عدد صحيح وهي حالة عادية وقد تعودنا عليها اما المؤشر فهو يشير الى عنوان تخزين المتغير x بالذاكرة هل وضحت الآن ان الذاكرة تتكون من وحدات تخزين ولابد ان يكون لكل وحدة تخزين رقم بالطبع نحن نتعامل مع ارقام ويسمى هذا الرقم عنوان اي ان المؤشر يشير الى عنوان ذاكرة ودعنا نستخدم مصطلح لو حفظناه عن ظهر قلب لن يختلط علينا الامر كلما رأينا علامة المؤشر * عند الاعلان عن مؤشر نقول at address of ولنقل على التعليمة التالية
int *x;
at address of x;
وفي الحقيقة انه يشير الى عنوان بداية تخزين المتغير ولكن كيف ستعرف نهاية التخزين هل بعده برقم او اثنين او اكثر دعني اوضح اكثر
اسرة ابو الفضل تتكون من اربعة افراد ويقيمون بحجرات فردية بفندق السعادة
تبدء ارقام غرف اسرة ابو الفضل بالغرفة رقم 13
لسنا هنا في حاجة الى اسئلة اخرى فالاجابة معرفة انهم يسكنون بالغرف 13و14و15و16
لمجرد معرفة اول غرفة وعدد الغرف
نفس الشئ الاعلان عن مؤشر يعطي عنوان بداية التخزين ونوع المتغير يحدد عدد البايتات التي يحتاجها المتغير ليخزن والنوع المذكور هو int فاءذا رجعت للدروس الاولى ستجد معلومات عن ذلك هل ادركت الآن اهمية الاعلان عن نوع المتغير بالنسبة للكومبايلر بنفس الطريقة التي تخبر بها موظف الفندق ان يحجز لاسرتك اربعة غرف دون معرفة العدد لن يستطيع خدمتك.
ولكن ليكن هنا شخص اكثر انتباها ليتذكر اننا استخدمنا المؤشرات في درس المتغيرات الحرفية لجز مكان لمتغير حرفي فكيف يعرف طول المتغير كنت ربما ستدخل اسمك بالكامل او جزء منه لنتذكر الاعلان
char *MyName;
الاجابة كانت في نفس الدرس ارجو العودة اليه وهي ان البرنامج يلحق \0 والتي تعني نهاية المتغير الحرفي او مصفوفة الحروف في نهاية الادخال وبالتالي عرفت بداية التخزين من المؤشر ونهايته من وجود NULL TRMINATOR ملحق بنهايته
كنا نتحدث عن استخدام المؤشرات في تحديد متغير يحمل عنوان ذاكرة ولكن كيف نجلب عنوان الذاكرة وهي الفائدة الاخرى من المؤشرات فقط ضع قبل المتغير العلامة & كمثال
Ptr = &X;
Address of x;
لنحفظ العبارة الثانية عن ظهر قلب وتعالى الى المثال
#include <iostream.h>
int main()
{
int x; //A normal integer
int *pointer; //A pointer to an integer
pointer=&x; //Read it, "pointer equals the address of x"
cin>>x; //Reads in x
cout<<*pointer; //Note the use of the * to output the actual number stored in x
return 0;
}
ولنفسر خطوة بخطوة استخدام المؤشرات مع مثال خارجي
int x;
اعلان عادي عن متغير يحمل قيمة عددية صحيحة وسبق دراستها
التشابة : x هو انسان نوعة ذكر نرجو حجز غرفة بفندق السعادة تناسب سكن لذكر
int *pointer;
اعلان عن متغير لمؤشر من نوع عدد صحيح اي يمكنه حمل عنوان ذاكرة تخزين عدد صحيح
التشابة : استمارة الاقامة بالفندق التي يسجل فيها ارقام غرف النزلاء الذكور
pointer = &x;
المؤشر والذي يمكنه حمل عناوين تخزين لاعداد صحيحة سيحمل عنوان تخزين المتغير x وذلك بمساوته ب &x اي address of x
التشابة : اكتب على استمارة الذكور البيضاء رقم غرفة السيد x فهي تصلح لذلك
Cin>>x;
عملية ادخال للمتغير بطريقة الادخال العادية ليعرف الكومبيوتر قيمة
التشابة : ما هو الاسم الحقيقي للسيد x لنفرض الادخال كان انه السيد ابوالكلام
cout<<*pointer;
اطبع ما بالعنوان pointer والذي يحمل قيمة عنوان تخزين x بالطبع ستكون الاجابة هي قيمة x وتناظر تماما الطبع المباشر لقيمة x بالامر cout<<x
التشابه اذكر لي اسم من يسكن برقم الغرفة المدون على الاستمارة الخاصة بالذكور ستكون الاجابة انه السيد ابو الكلام لان العنوان المدون على الاستمارة هو حيز التخزين او رقم الغرفة 813 وهذه الغرفة يسكنها السيد ابو الكلام وعليه كانت الاجابة