شرح الكلاس class في لغة C++ - ProgrammerTech
Language:

Search

شرح الكلاس class في لغة C++

  • Share this:
شرح الكلاس class في لغة C++

مفهوم الكلاس في C++

الكلاس ( class ) هو عبارة عن نوع جديد يتم تعريفه بواسطة كلمة class و يمكنه أن يحتوي على دوال Functions, متغيرات Variables, مصفوفات Arrays إلخ..

قبل أن نتكلم عن الكلاسات هُناك مفهوم مهم جداً يجب معرفته أولاً و هو التجريد أو ما يسمى Abstraction.

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

 

الكلاس و فائدة التجريد

لقد تَحدَثنا سابقاً عن المتغيرات في الدروس الأولى و كانت أنواعها (int, char, double, bool, string).

و قٌلنا أنه عندما نقوم بإنشاء متغير جديد فإنه يتم إنشاء مكان خاص لهذا المتغير في الذاكرة و يحمل هذا المتغير اربع خواص و هي (الإسم, القيمة, النوع, عنوان التخزين).

إذا أردنا تخزين قيمة التاريخ مثلا Mar 23 1995 في الذاكرة, فهل التاريخ يمكن التعبير عنه برقم int او double أو string.

 

كيف يتم تخزين قيمة تاريخ معين في الذاكرة؟

اولاً علينا أن نعرف ما هي الخواص التي نُريد تخزينها في التاريخ, فكل تاريخ به سنة و شهر و يوم.

و من هنا يجب أن نعلم ما هي المتغيرات الذي يجب إنشاءها لأي تاريخ, نحتاج إذاً لثلاثة متغيرات و هي:

  1. لليوم int day.
  2. للشهر int month.
  3. للسنة int year.

و نوع المتغير يكون عددي int, سوف نأخذ مثال لفهم هذا التاريخ اكثر.

الشكل العام للكلاس في C++

class Date{
    //تعريف محتويات الكلاس
};

نٌلاحظ أنه بدأنا إسم الكلاس بحرف كبير Capital و هي من العادات الجيدة في البرمجة أن نقوم ببدأ إسم الكلاس بحرف كبير.

حيث يتم تفريقه عن المتغيرات و الفنكشن.

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

و في نهاية القوس المعكوف وضعنا فاصلة منقوطة ; في نهاية جسم الدالة بعكس الفنكشن مثل main لا تحتاج لفاصلة بنهاية جسم الدالة.

مثال 1

#include <iostream>
using namespace std;
class Date
{
public:
  int year;
  int month;
  int day;
};
int main ()
{
  Date birthday;
  birthday.year = 1995;
  birthday.month = 3;
  birthday.day = 23;
  cout << birthday.day << "/" << birthday.month << "/" << birthday.year << endl;
    return 0;
}

قُمنا بإنشاء كلاس إسمه Date أي كلاس التاريخ و كان به ثلاث متغيرات عامة و هي للسنة year و للشهر month و لليوم day.

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

و قمنا بتعيين قيمة للسنة عبر الوصول لها عن طريق الكائن و كانت قيمتها 1995 و بنفس الأمر اسندنا قيمة للشهر و اليوم.

ثم قُمنا ببناء دالة طباعة cout و طلبنا أن تُطبَع لنا في البداية اليوم ثم تفصله بإشارة سلاش / . ثم يطبع الشهر ثم تفصله سلاش ثم يطبع السنة و عند تشغيل هذا الكود ستكون النتيجة هي.

23/3/1995

كما لآحظنا من شاشة التنفيذ السابقةأنه طبع لنا تاريخ الميلاد مثلما طلبنا منه.

مثال 2

قم بإنشاء كلاس عام يحتوي على تاريخي ميلاد مختلفين.

#include <iostream>
using namespace std;
class Date
{
public:
    int year;
    int month;
    int day;
};
int main ()
{
    Date birthday;
    birthday.year = 1995;
    birthday.month = 3;
    birthday.day = 23;
    cout << birthday.day << "/"<<birthday.month<<"/"<<birthday.year<<endl;
    Date birthday2;
    birthday2.year = 1994;
    birthday2.month = 2;
    birthday2.day = 22;
    cout << birthday2.day <<"/"<<birthday2.month<<"/"<<birthday2.year<<endl;
    return 0;
}

قُمنا بإنشاء كلاس إسمه Date أي كلاس التاريخ و كان به ثلاث متغيرات عامة و هي للسنة year و للشهر month و لليوم day هذا كان في الكلاس.

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

و قمنا بتعيين قيمة للسنة عبر الوصول لها عن طريق الكائن و كانت قيمتها 1995 و بنفس الأمر اسندنا قيمة للشهر و اليوم.

ثم قُمنا ببناء دالة طباعة cout و طلبنا أن تطبَع لنا في البداية اليوم ثم تفصله بإشارة سلاش / ثم يطبع الشهر ثم تفصله سلاش ثم يطبع السنة.

و قُمنا بإنشاء كائن آخر إسمه birthday2 لنصِل لمحتويات الكلاس كما في التاريخ الأول.

و عند تشغيل الكود ستكون نتيجته هي.

23/3/1995
22/2/1994

 

إستخدام الدوال مع الكلاس

تعرّفنا على الكلاس و قُلنا أنهُ هو طريقة تجريبية لتنسيق البيانات من خلال كتابة المتغيرات المستخدمة.

كما في مثال التاريخ يوجد متغير لليوم و متغير للشهر و متغير للسنة.

لكن ليس هذا كل شيئ يمكن الكلاس فعله أو تخزينه.

يمكن للكلاس تخزين بعض الدوال و التي تكون خاصة فقط لهذا الكلاس و هذا هو مبدأ المكتبات Libraries.

كما في مكتبة math حيت يتم تخزين بعض الدوال في الكلاس ثم يتم إدراج جميع الدوال المخزَّنة في هذا الكلاس.

تعلّمنا كيفية كتابة الكلاس و قُلنا أن المتغيرات الآن (فقط) ستكون عامة public.

و تحت بوبلك نقوم بكتابة جميع المُتغيرات التي نُريدها لأي كائن نقوم بتعريفه من نوع Date.

فعند تعريف كائن جديد على سبيل المثال birthday و عند كتابة إسم الكائن ووضع نقطه بعده. سوف نجِد الإكمال التلقائي قد أتى لنا بجميع المُتغيرات العامة التي وضعناها في الكلاس في الأعلى.

نُلاحظ أنَّ هُناك فرق بين الكائن object و كلاس class, فالكلاس هنا نقوم بتصميم مجرد.

فقط نقوم بكتابة ما هي الخواص المُهِمة لكل تاريخ و كل تاريخ به سنة و شهر و يوم.

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

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

كعمل مثل تصميم لمنزل لكن لا يوجد منزل على أرض الواقع فقط قُمنا بكتابة عدد الأعمدة الموجودة و طريقة تصميم الأعمدة.

هل طبقّنا هذه الأعمدة على أرض الواقع؟ هل قمت بإنشاء منزل؟ و إن كان الجواب لا فلا وجود للمنزل.

كذلك بالنسبة للكلاس و الكائن إذا لم نقُم بكتابة متغير من نوع الكلاس فإنه لا وجود لهذا الكلاس في الذاكرة, أي لا وجود لمتغير لهذا الكلاس.

بإختصار فإن الكائن هو مُتغير لديه قيم مخزَّنة في الذاكرة فإن الكلاس هو تصميم قالب لأي مُتغير من نوعه.

مثال 3

#include <iostream>
using namespace std;
class Date
{
public:
    int year;
    int month;
    int day;
    void print()
    {
        cout << day << "/" << month<<"/"<<year<<endl; cout
    }
};
int main ()
{
    Date birthday;
    birthday.year = 1995;
    birthday.month = 3;
    birthday.day = 23;
    birthday.print();
    Date birthday2;
    birthday2.year = 1994;
    birthday2.month = 2;
    birthday2.day = 22;
    birthday2.print();
    return 0;
}

قُمنا بإنشاء كلاس إسمه Date أي كلاس التاريخ و كان به ثلاث مُتغيرات عامة و هي للسنة year و للشهر month و لليوم day هذا كان في الكلاس.

و به دالة للطباعة و هي void print من أجل إستدعاءها لطباعة ما نحتاجه و أنشأنا دالة طباعة cout لأجل أن تقوم بطباعة تنسيق هذا الميلاد كيفما نُريد.

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

و قُمنا بتعيين قيمة للسنة عبرَ الوصول لها عن طريق الكائن و كانت قيمتها 1995 و بنفس الأمر أسندنا قيمة للشهر و اليوم.

ثم قُمنا بإستدعاء دالة الطباعة print من أجل طباعة الناتج.

و قُمنا بإنشاء كائن آخر إسمه birthday2 لنصِل لمحتويات الكلاس لأجل طباعة الميلاد الثاني.

عند تشغيل هذا الكود ستكون نتيجته هي

23/3/1995
22/2/1994

مثال 4

#include <iostream>
using namespace std;
class Date
{
public:
    int year;
    int month;
    int day;
    void print()
    {
        cout << day << "/" << month << "/" << year << endl;
    }
    void fill(int d, int m, int y)
    {
        day = d;
        month = m;
        year = y;
    }
};
int main ()
{
    Date birthday;
    birthday.fill(23, 3, 1995);
    birthday.print();
    Date birthday2;
    birthday2.fill(22, 2, 1994);
    birthday2.print();
    return 0;
}

قُمنا بإنشاء كلاس إسمه Date أي كلاس التاريخ و كان به ثلاث مُتغيرات عامة و هي للسنة year و للشهر month و لليوم day هذا كان في الكلاس.

و به دالة للطباعة و هي void print من أجل إستدعاءها لطباعة ما نحتاجه.

و أنشأنا دالة طباعة cout لأجل أن تقوم بطباعة تنسيق هذا الميلاد كيفما نُريد.

ثم أنشأنا الدالة fill و وضعنا بها المُتغيرات و هي d و m و y و عرّفنا إختصارات المُتغيرات أن d يعني اليوم day و m يعني الشهر month و y يعني السنة year.

و قُمنا بالدخول لدالة main و قُمنا بإنشاء كائن خاص بالمواليد و هو birthday ثم عيننا قيم للتاريخ الأول عبر إستدعاء الدالة fill و وضعنا التاريخ الذي يمرر هو 23/3/1995.

و نفس الأمر بالنسبة للمواليد الثاني.

و عند التنفيذ كانت النتيجة هي.

23/3/1995
22/2/1994

 

دالة البناء Constructor في لغة C++

الكونستركتور ( Constructor ) هو عبارة عن دالة يتم استدعاءها بشكل تلقائي عند إنشاء كائن object من الكلاس.

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

دالة البناء Constructor هي أول دالة يتم إستدعائها عند إنشاء أي كائن object من أي كلاس.

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

 

صفات دالة البناء كونستركتور Constructor

  • لا تقوم بإرجاع قيمة و لا حتى void.
  • إسمها يأتي نفس إسم الكلاس (فإذا كان إسم الكلاس Date فيكون إسمها أيضاً Date).
  • يتم إستدعائها عند إنشاء أي كائن من الكلاس مباشرتاً.

مثال 5

لنُفكر ماذا يوجد حولنا "هواتف ذكية و يوجد أشخاص" سوف نقوم بعمل كلاس يقوم بحفظ أشخاص. فالشخص لديه إسم و عمر و طول و الطول يُعتبَر مُهم, سوف نقوم بعمل كلاس يحفظ بيانات الشخص.

#include <iostream>
using namespace std;
class Person
{
public:
    string name;
    int age;
    double height;
    Person(string n, int a, double h)
    {
        name = n;
        age = a;
        height = h;
    }
    void print()
    {
        cout << "الأسم " << name << endl;
        cout << "العمر" << age << endl;
        cout << "الطول" << height << endl;
    }
};
int main()
{
    Person saja("Saja", 27, 155.7);
    saja.print();
    return 0;
}

لقد قمنا بإنشاء كلاس إسمه Person و كان به ثلاث متغيرات عامة و هي متغير نصي string من أجل تخزين الإسم فيه. و متغير عددي int لتخزين العمر فيه و متغير بفاصلة عشريه double لتخزين الطول فيه.

ثم قمنا بإنشاء كونستركتور Constructor و أخذ نفس إسم الكلاس Person و عرفنا به المتغيرات الثلاث. بأن المتغير الذي يرمز للإسم نختصره بحرف n و المتغير الذي يرمز للعمر بحرف a و المتغير الذي يرمز للطول h ثم عرّفناهم.

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

ثم دخلنا بداخل الدالة main و أنشأنا كائن من إسم الكلاس إسمه saja ووضعنا بداخل الأقواس القيم التي سوف يتم إستدعائها و تعيينها للمتغيرات الثلاث.

و إستدعينا دالة الطباعة عن طريق الكائن saja.

و عند تنفيذ هذا البرنامج سوف نحصل على النتيجة الآتية.

Name: Saja
Age: 27
Length: 155.7

كما نلاحظ أنه طبع لنا الإسم Saja في الأعلى ثم العمر 27 ثم الطول 155.7.

مثال 6

قم بكتابة برنامج يكون به كلاس بإسم Car و به أربع متغيرات لإسم السيارة و لونها و سرعتها و تاريخ صنعها.

#include <iostream>
using namespace std;
class Car {
public:
    string name;
    string color;
    int speed;
    int model;
    Car(string name, string color, int speed, int model)
    {
        this->name = name;
        this->color = color;
        this->speed = speed;
        this->model = model;
    }
    void printInfo()
    {
        cout << "Name: " << name << "\n";
        cout << "Color: " << color << "\n";
        cout << "Speed: " << speed << "\n";
        cout << "Model: " << model << "\n";
    }
};
int main()
{
    Car car("BMW", "black", 240, 2001);
    car.printInfo();
    return 0;
}

لقد قمنا بإنشاء كلاس إسمه Car و قمنا بإنشاء اربع متغيرات عامة public و هي:

  1. متغير نصي لإسم السيارة name.
  2. متغير نصي آخر للون السيارة color.
  3. متغير عددي صحيح لسرعة السيارة speed.
  4. متغير عددي لتاريخ صنعها أو موديلها model.

و قمنا بإنشاء كونستركتور أخذ نفس إسم الكلاس Car و مرّرنا بيانات به من أجل إستدعاءه.

ثم قمنا بوضع القيم التي يجب تمريرها للكونستركتور.

و بعدها قمنا بإنشاء دالة طباعة عن طريق void و الدالة هي printInfo مهمتها طباعة كل قيم المتغيرات الموجودة في الكلاس Car.

و دخلنا لداخل الدالة main قمنا بإشتقاق كائن من الكلاس Car و الكائن هو car و وضعنا به القيم التي يجب تمريرها للكونستركتور بشكل مباشر.

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

و عندما قمنا بتشغيل الكود السابق سنحصل على النتيجة.

Name: BMW
Color: black
Speed: 240
Model: 2001

 

تعريف الدوال خارج الكلاس

سوف نتعلم عن كيفية كتابة الدوال الخاصة بالكلاس خارج الكلاس.

مثال 7

#include <iostream>
using namespace std;
class Student{
private:
    string name;
    double grade;
public:
    Student(string n, double g)
    {
        setName(n);
        setGrade(g);
    }
    void setName(string n)
    {
        name = n;
    }
    void setGrade(int g)
    {
        grade = g;
    }
    string pass()
    {
        return (grade > 60 ? "yes" : "no");
    }
    void printStatus()
    {
        cout << "Name : " << name << endl;
        cout << "Degree : " << grade << endl;
        cout << "Success : " << pass() << endl;
        cout << endl;
    }
};
int main()
{
    Student saja("Saja", 100);
    saja.printStatus();
    Student data("Mohammad", 30);
    data.printStatus();
    return 0;
}

لقد قمنا بتعريف كلاس إسمه Student و كان به خاصيتان و هما name أي الإسم و نوعه نصي string و grade أي الدرجة نوعه double.

و كما تعلمنا مسبقاً أن جميع الخواص أو المتغيرات يتم تعريفها كـ private كي لا يتم الوصول إليها. و التعديل على قيمها مباشرتاً من قبل الدوال خارج كلاس Student مثل دالة main.

ثم في public نضع جميع الـ function ( جميع الدوال ) و من ضمن هذه الدوال الأولى هي دالة البناء الكونستركتور.

و قمنا بإعادة تعريف دالة البناء الكونستركتور Student بإضافه معاملين المعامل الأول n و المعامل الثاني g.

كي نقوم بتغير name من خلال إستدعاء دالة setName و هي طريقة افضل لكتابة الكونستركتور أن نقوم بإستعمال setName بدلاً من كتابة name=n.

و ايضاً قُمنا بكتابة setGrade فسيتم تعريف name في n و الـ Grade في g و هما دالتان من نوع void.

و كما قُلنا أن Setters من نوع void و يتم تمرير معامل من نفس نوع المتغير name.

و المتغير name نوعه string فسيتم تمرير string n و يتم تعيين name بهذا المعامل و ايضاً setGrade بنفس الطريقة.

ثم انشأنا دالة تسمى pass و هي تقوم بإرجاع string تقوم هذه الدالة بالتأكد ما إن كان Grade اكبر من 60 سيقوم بطباعة نعم أي ناجح.

و غير ذلك سوف يقوم بطباعة لا أي راسب.

و دالة printStatus هذه الدالة سوف تقوم بطباعة الإسم و الدرجة و الحالة ما إن كان ناجحاً ام لا.

فسيتم طباعة النجاح من الدالة pass و في الدالة main قُمنا بتعريف كائن إسمه saja و كان هذا الكائن يُمرر قيمة Saja كإسم و 100 كدرجة.

ثم سيتم طباعة الحالة عن طريق الكائن.

ثم انشانا كائن data و كان يمرر Mohammad كإسم و 30 كدرجة ثم يقوم بطباعة الحالة له.

Name: Saja
Degree: 100
Success: yes
Name: Mohammad
Degree: 30
Success: no

نُلاحظ أنه عند دالة الطباعة cout لم نكتب Get لأننا لم نقُم بالحاجة إلى إستدعاء Grade لأي من الكائنات.

لنَقُم بإعادة ترتيب الكود السابق, تحدثنا في دروس سابقة عن طريقة كانت بأن نقوم بكتابة رأس الدالة فقط مثلاً.

void printName()
{
هنا نقوم بكتابة الكود
}

و نقوم بنسخ الكود و لصقه أسفل main, فهل يمكننا فعل ذلك بدوال الكلاس؟

سوف نُلاحظ أن دوال الكلاس كبيرة بعض الشيئ و قد يكون في بعض الكلاسات دوال كثيرة و كبيرة ايضاً.

هل يمكننا إختصار ذلك؟ نعم يُمكننا بكل بساطة سوف نقوم بنسخ جميع الدوال الذي في public في المثال السابق و لصقها خارج الـ main.

حيث أن main غير معرًّفة خارجها لأن هذا النطاق خارج جميع الدوال و خارج الكلاس ايضاً.

لكن نحن نُريد تحديد مثلاً دالة setName تنتمي إلى الكلاس Student بكل بساطة يمكننا كتابة.

Student::Student(string n, double g)

بهذه الطريقة يُمكننا الوصول إلى الدوال التي قُمنا بتعريفها.

نقوم في الأعلى بإلغاء جميع أجسام الدوال و نقوم فقط بإبقاء رؤوس الدوال بإضافة الفاصله المنقوطة.

كما كنا نفعل سابقاً أعلى main فهذا يسمى function prototype أي راس الدوال بحيث فقط نقوم بإبقاء رؤوس العناوين لبعض الدوال.

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

سيكون الكود بالشكل.

#include <iostream>
#include <iomanip>
using namespace std;
class Student{
private:
    string name;
    double grade;
public:
    Student(string n, double g);
    void setName(string n);
    void setGrade(int g);
    string pass();
    void printStatus();
};
int main()
{
    Student saja("Saja", 100);
    saja.printStatus();
    Student data("Mohammad", 30);
    data.printStatus();
    return 0;
}
    Student::Student(string n, double g)
    {
        setName(n);
        setGrade(g);
    }
    void Student::setName(string n)
    {
        name = n;
    }
    void Student::setGrade(int g)
    {
        grade = g;
    }
    string Student::pass()
    {
        return (grade > 60 ? "yes" : "no");
    }
    void Student::printStatus()
    {
        cout << "Name : " << name << endl;
        cout << "Degree : " << grade << endl;
        cout << "Success : " << pass() << endl;
        cout << endl;
    }

عند تشغيل هذا الكود سنحصل على النتيجة.

Name: Saja
Degree: 100
Success: yes
Name: Mohammad
Degree: 30
Success: no

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

و في الحقيقة هذه الطريقة الأمثل لتصميم الكلاس و يسمى الجزء الأول من البرنامج برأس الكلاس أو class header.

فقط نقوم بتعريف المُتغيرات و الدوال الموجودة و لا نقوم بكتابة أي كود.

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


Muhammad Alush

Muhammad Alush

إسمي محمد تركي العلوش من سورية من ريف حلب الشرقي 1994, محب للتقنية و التعلم الذاتي, و جميع خبراتي تعلمتها ذاتياً, و اللغات البرمجية التي أتقنها هي html, css, javascript, php, c, python بالإضافة إلى حبي الكبير لمجال الشبكات.

موقعي programmer-tech.com