النوع class
في C++
مفهوم الكلاس في C++
الكلاس عبارة عن نوع جديد يتم تعريفه بواسطة الكلمة class
و هذا النوع يمكنه أن يحتوي على دوال, متغيرات, مصفوفات إلخ..
النوع الذي تقوم بتعريفه بوسطة الكلمة class
يشبه بشكل كبير النوع الذي تقوم بتعريفه بواسطة الكلمة struct
التي تعرفنا عليها في الدرس السابق.
لهذا السبب سنبدأ بذكر الفرق بينهما حتى لا ترتبك من شدة التشابه الذي ستلاحظه بينهما.
الفرق الأساسي بين النوع الذي يتم تعريفه بواسطة الكلمة class
و النوع الذي يتم تعريفه بواسطة الكلمة struct
هو أن هذا الأخير يمكن الوصول لأي شيء موجود فيه بشكل مباشر, بينما في النوع class
أنت تحدد ما إن كان يمكن الوصول للأشياء التي تضعها فيه بشكل مباشر أم لا.
إمكانية تحديد الطريقة التي يمكن فيها الوصول للأشياء الموجودة في الكلاس تمكننا من تطبيق كل مبادئ البرمجة الكائنية ( OOP ) المتعارف عليها.
لهذا سنركز بشكل كبير في الدروس القادمة على التعامل مع الكلاس بشكل خاص.
كيفية تعريف كلاس في C++
لتعريف كلاس جديد نكتب الكلمة class
ثم نعطيه إسم, ثم نفتح أقواس تحدد بدايته و نهايته.
المثال التالي يوضح طريقة تعريف كلاس فارغ إسمه Book
.
مثال تعريف كلاس في C++
class Book { // هنا يمكنك تعريف كل ما سيحتويه الكلاس };
معلومة تقنية
في المشاريع الحقيقية, أي كلاس جديد نريد تعريفه نقوم في العادة بإنشاء ملف نوعه cpp
خاص له و نضع الكود الخاص به بداخله.
بعدها في أي مكان نريد أن نتعامل معه نقوم بتضمين الملف الذي يحتويه.
الآن, بهدف جعل الشرح بسيط و سهل الفهم سنكتب الكود كله بداخل نفس الملف و في آخر الدرس سنعلمك كيف تنشئ الكلاس في ملف خاص أيضاً.
مفهوم الخصائص في C++
الخصائص ( Attributes ) هي الأشياء (المتغيرات, المصفوفات و الكائنات) التي يتم تعريفها بداخل الكلاس و التي سيملك نسخة خاصة منها أي كائن ننشئه منه.
أي شيء تنوي تعريفه في الكلاس لا بدل لك من تحديد كيفية الوصول إليه كما قلنا سابقاً.
لتحدد كيفية الوصول للأشياء التي تضعها في الكلاس يمكنك استخدام الكلمات المخصصة لذلك و التي يقال لها Access Specifiers, أي الكلمات التالية.
الكلمة | إستخدامها |
---|---|
public |
تستخدم لتحديد أن الأشياء الموضوعة في الكلاس يمكن الوصول لها من أي مكان. |
private |
تستخدم لتحديد أن الأشياء الموضوعة في الكلاس لا يمكن الوصول لها من خارجه. |
protected |
تستخدم لتحديد أن الأشياء الموضوعة في الكلاس يمكن الوصول لها عند وراثتها. ملاحظة: سنتعلم الوراثة في دروس لاحقة. |
ملاحظة
ستعرف متى يجب أن نستخدم الكلمتين private
و protected
بشكل عملي في دروس لاحقة و لكن الآن يكفي أن تعرف أنها من الخيارات المتاحة في C++ و معرفة كيف يمكن وضعها بداخل الكلاس.
المثال التالي يوضح طريقة وضع الكلمات public
و private
و protected
في الكلاس.
المثال الأول
class Book { public: // public هنا أي شيء نقوم بتعريفه يعتبر أنه private: // private هنا أي شيء نقوم بتعريفه يعتبر أنه protected: // protected هنا أي شيء نقوم بتعريفه يعتبر أنه };
في المثال التالي قمنا بتعريف كلاس إسمه Book
و بداخله قمنا بتعريف 4 متغيرات نوعهم public
.
ملاحظة: لم نضع الكلمتين private
و protected
في الكلاس لأننا لم نحتاجهم.
المثال الثاني
class Book { public: string title; string author; double price; int numberOfPages; };
إنشاء كائن من كلاس و إعطاؤه قيم في C++
إنشاء كائن من كلاس أمر بغاية السهولة, فكل ما عليك فعله هو اعتماد نفس الطريقة التي تعتمدها لإنشاء متغير عادي.
و بالنسبة للوصول للأشياء التي يملكها الكائن, فيمكنك الوصول لأي شيء موجود فيه طالما أن نوعه public
من خلال كتابة إسم الكائن ثم وضع نقطة ثم وضع إسم الشيء الذي تريد الوصول إليه.
في المثال التالي قمنا بتعريف كلاس إسمه Book
و بداخله قمنا بتعريف 4 متغيرات نوعهم public
.
بعدها قمنا بإنشاء كائنين منه و إعطاء قيم للمتغيرات الموجودة فيهما.
مثال في إنشاء كائن من كلاس و إعطاؤه قيم في C++
#include <iostream> using namespace std; // يحتوي على 4 متغيرات Book هنا قمنا بتعريف كلاس إسمه class Book { public: string title; string author; double price; int numberOfPages; }; int main() { // book2 و الثاني إسمه book1 الأول إسمه ,Book هنا قمنا بتعريف كائنين من Book book1; Book book2; // book1 هنا قمنا بإعطاء قيم لمتغيرات الكائن book1.title = "C++ for beginners"; book1.author = "Mhamad Harmush"; book1.price = 9.99; book1.numberOfPages = 420; // book2 هنا قمنا بإعطاء قيم لمتغيرات الكائن book2.title = "Network 1"; book2.author = "Nadine Masri"; book2.price = 22.49; book2.numberOfPages = 310; // book1 هنا قمنا بعرض قيم الكائن cout << "Book 1 ----------- \n"; cout << "Title: " << book1.title << "\n"; cout << "Author: " << book1.author << "\n"; cout << "Price: " << book1.price << "$\n"; cout << "Number of pages: " << book1.numberOfPages << "\n\n"; // book2 هنا قمنا بعرض قيم الكائن cout << "Book 2 ----------- \n"; cout << "Title: " << book2.title << "\n"; cout << "Author: " << book2.author << "\n"; cout << "Price: " << book2.price << "$\n"; cout << "Number of pages: " << book2.numberOfPages; return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
Book 1 ----------- Title: C++ for beginners Author: Mhamad Harmush Price: 9.99$ Number of pages: 420 Book 2 ----------- Title: Network 1 Author: Nadine Masri Price: 22.49$ Number of pages: 310
مفاهيم مهمة حول الكلاسات في C++
المفهوم الأول
هنا وضعنا مثالين حول كيفية تعريف دالة بداخل كلاس ( Member Function ) بالإضافة إلى كيفية إستدعاءها منه.
من المهم جداً معرفة أنه يمكنك تعريف دوال الكلاس بطريقتين:
تعريف الدالة كما هي بداخل الكلاس.
تعريف شكل الدالة ( Function Header أو Prototype ) فقط بداخل الكلاس و تعريف محتوى الدالة ( Function Body ) خارج تعريف الكلاس.
في المثال التالي, قمنا بتعريف كلاس إسمه Book
يمثل المعلومات التي يمكن أن يتضمنها أي كتاب كعنوانه, إسم المؤلف, سعره و عدد صفحاته.
كما أننا قمنا بوضع دالة إسمها printInfo
بداخل الكلاس عند استدعاءها من أي كائن ننشئه منه فتقوم بطباعة قيمه بشكل مرتب.
في الأخير قمنا بإنشاء كائن من Book
و إعطاؤه قيم, و من ثم إستدعاء الدالة printInfo()
منه حتى تقوم بطباعة قيمه.
المثال الأول
#include <iostream> using namespace std; // يحتوي على 4 متغيرات Book هنا قمنا بتعريف كلاس إسمه class Book { public: string title; string author; double price; int numberOfPages; // تقوم بطباعة كل قيم المتغيرات الموجودة فيه Book عند استدعاءها من أي كائن ننشئه من printInfo هنا قمنا بتعريف دالة إسمها void printInfo() { cout << "Title: " << title << "\n"; cout << "Author: " << author << "\n"; cout << "Price: " << price << "$\n"; cout << "Number of pages: " << numberOfPages << "\n"; } }; // main() هنا قمنا بتعريف الدالة int main() { // book إسمه Book هنا قمنا بتعريف كائن من Book book; // book هنا قمنا بإعطاء قيم لمتغيرات الكائن book.title = "C++ for beginners"; book.author = "Mhamad Harmush"; book.price = 9.99; book.numberOfPages = 420; // حتى تقوم بطباعة القيم الموجودة فيه book من الكائن printInfo() هنا قمنا باستدعاء الدالة book.printInfo(); return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
Title: C++ for beginners Author: Mhamad Harmush Price: 9.99$ Number of pages: 420
هنا قمنا بإعادة المثال السابق مع فارق واحد و هو أننا قمنا بتعريف شكل الدالة ( Prototype ) فقط بداخل الكلاس و محتواها خارجه.
المثال الثاني
#include <iostream> using namespace std; // يحتوي على 4 متغيرات Book هنا قمنا بتعريف كلاس إسمه class Book { public: string title; string author; double price; int numberOfPages; // printInfo() هنا قمنا بتعريف شكل الدالة void printInfo(); }; // و جعلناها تقوم بطباعة قيم متغيرات الكائن الذي يستدعيها Book الموجودة في الكلاس printInfo هنا قمنا بتعريف محتوى الدالة void Book::printInfo() { cout << "Title: " << title << "\n"; cout << "Author: " << author << "\n"; cout << "Price: " << price << "$\n"; cout << "Number of pages: " << numberOfPages << "\n"; } // main() هنا قمنا بتعريف الدالة int main() { // book إسمه Book هنا قمنا بتعريف كائن من Book book; // book هنا قمنا بإعطاء قيم لمتغيرات الكائن book.title = "C++ for beginners"; book.author = "Mhamad Harmush"; book.price = 9.99; book.numberOfPages = 420; // حتى تقوم بطباعة القيم الموجودة فيه book من الكائن printInfo() هنا قمنا باستدعاء الدالة book.printInfo(); return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
Title: C++ for beginners Author: Mhamad Harmush Price: 9.99$ Number of pages: 420
المفهوم الثاني
هنا وضعنا مثال حول كيفية تعريف دالة تأخذ باراميتر عبارة كائن ( Object Parameter ) من كلاس بالإضافة إلى كيفية إستدعاءها.
في المثال التالي, قمنا بتعريف كلاس إسمه Book
يمثل المعلومات التي يمكن أن يتضمنها أي كتاب كعنوانه, إسم المؤلف, سعره و عدد صفحاته.
بعدها قمنا بتعريف دالة إسمها printInfo
عند استدعاءها نمرر لها كائن نوعه Book
فتقوم بطباعة قيمه بشكل مرتب.
في الأخير قمنا بإنشاء كائن من Book
و إعطاؤه قيم, و من ثم تمريره للدالة printInfo()
حتى تقوم بطباعة قيمه.
مثال
#include <iostream> using namespace std; // يحتوي على 4 متغيرات Book هنا قمنا بتعريف كلاس إسمه class Book { public: string title; string author; double price; int numberOfPages; }; // فتقوم بطباعة كل قيم المتغيرات الموجودة فيه Book عند استدعاءها نمرر لها كائن من printInfo هنا قمنا بتعريف دالة إسمها void printInfo(Book book) { cout << "Title: " << book.title << "\n"; cout << "Author: " << book.author << "\n"; cout << "Price: " << book.price << "$\n"; cout << "Number of pages: " << book.numberOfPages << "\n"; } // main() هنا قمنا بتعريف الدالة int main() { // book إسمه Book هنا قمنا بتعريف كائن من Book book; // book هنا قمنا بإعطاء قيم لمتغيرات الكائن book.title = "C++ for beginners"; book.author = "Mhamad Harmush"; book.price = 9.99; book.numberOfPages = 420; // لها حتى تقوم بطباعة القيم الموجودة فيه book و تمرير الكائن printInfo() هنا قمنا باستدعاء الدالة printInfo(book); return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
Title: C++ for beginners Author: Mhamad Harmush Price: 9.99$ Number of pages: 420
المفهوم الثالث
هنا وضعنا مثال حول كيفية استخدام العامل this->
للتفرقة بين باراميترات الدوال و الخصائص الموجودة في الكلاس.
هذا العامل يجعلك قادر على استخدام نفس الأسماء للبارميترات و الخصائص.
العامل this->
يقال له This Pointer, و هم يستخدم للتفرقة بين باراميترات الدوال و الخصائص الموجودة في الكلاس مما يجعلك قادر على وضع نفس الأسماء للبارميترات و الخصائص و تجنب حدوث أي أخطاء منطقية بسبب الأسماء المتشابهة.
الآن, سنضع لك عدة أمثلة حتى تلاحظ الأخطاء المنطقية التي قد تقع فيها بسبب الأسماء المتشابهة و كيف أن العامل this->
يحل لك هذه المشكلة.
في المثال التالي, قمنا بتعريف كلاس إسمه Book
يمثل المعلومات التي يمكن أن يتضمنها أي كتاب كعنوانه, إسم المؤلف, سعره و عدد صفحاته.
في الكلاس Book
بتعريف دالة إسمها setTitle()
عند استدعاءها نمرر لها نص فيتم وضعه كعنوان للكائن الذي قام باستدعائها.
المثال الأول
#include <iostream> using namespace std; // يحتوي على 4 متغيرات Book هنا قمنا بتعريف كلاس إسمه class Book { public: // هنا قمنا بتعريف خصائص الكلاس string title; string author; double price; int numberOfPages; // Book عند استدعاءها من أي كائن ننشئه من setTitle هنا قمنا بتعريف دالة إسمها // الذي يملكه الكائن title فتقوم بوضعها كقيمة للمتغير t نمرر لها قيمة مكان الباراميتر void setTitle(string t) { title = t; } }; // main() هنا قمنا بتعريف الدالة int main() { // book إسمه Book هنا قمنا بتعريف كائن من Book book; // book الخاص بالكائن title و تمرير نص لها حتى تقوم بتخزينه في المتغير book من الكائن setTitle() هنا قمنا باستدعاء الدالة book.setTitle("C++ for beginners"); // للتأكد ما إن كانت قد تغيرت أم لا book الموجود في الكائن title هنا قمنا بطباعة قيمة المتغير cout << "Title: " << book.title; return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
Title: C++ for beginners
في حال قمنا بإعادة المثال السابق مع إجراء تعديل بسيط عليه بحيث نجعل إسم الباراميتر الموضوع في الدالة setTitle()
هو نفسه إسم المتغير title
الموجود في الكلاس سيظهر لنا مشكلة منطقية عند تشغيل البرنامج كما سنرى في نتيجة التشغيل.
المثال الثاني
#include <iostream> using namespace std; // يحتوي على 4 متغيرات Book هنا قمنا بتعريف كلاس إسمه class Book { public: // هنا قمنا بتعريف خصائص الكلاس string title; string author; double price; int numberOfPages; // title نمرر لها قيمة مكان الباراميتر Book عند استدعاءها من أي كائن ننشئه من setTitle هنا قمنا بتعريف دالة إسمها void setTitle(string title) { // الموجود في الكلاس title بداخله من جديد. أي لن يتم تمريرها للمتغير title هنا كأننا قلنا, أعد وضع القيمة التي نمررها للباراميتر // لأنهما يملكان نفس الإسم title سبب هذه المشكلة أن المترجم لن يعرف أنك تقصد أنك تريد تمرير القيمة التي تمررها للدالة, إلى المتغير title = title; } }; // main() هنا قمنا بتعريف الدالة int main() { // book إسمه Book هنا قمنا بتعريف كائن من Book book; // book الخاص بالكائن title و تمرير نص لها حتى تقوم بتخزينه في المتغير book من الكائن setTitle() هنا قمنا باستدعاء الدالة book.setTitle("C++ for beginners"); // للتأكد ما إن كانت قد تغيرت أم لا book الموجود في الكائن title هنا قمنا بطباعة قيمة المتغير cout << "Title: " << book.title; return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
•لاحظ أن القيمة التي مررناها للدالة setTitle()
لم يتم تمريرها للمتغير title
الموجود في الكائن book
و هذا ما كنا نقصده بخطأ منطقي.
Title:
لحل المشكلة المنطقة التي شاهدناها في المثال الثاني, أي لجعل المترجم قادر على التفرقة بين البارميترات و المتغيرات الموجودة في الكلاس سنستخدم العامل this->
كلما أردنا الإشارة إلى المتغيرات الموجودة في الكلاس.
المثال الثالث
#include <iostream> using namespace std; // يحتوي على 4 متغيرات Book هنا قمنا بتعريف كلاس إسمه class Book { public: // هنا قمنا بتعريف خصائص الكلاس string title; string author; double price; int numberOfPages; // Book عند استدعاءها من أي كائن ننشئه من setTitle هنا قمنا بتعريف دالة إسمها // الذي يملكه الكائن title فتقوم بوضعها كقيمة للمتغير title نمرر لها قيمة مكان الباراميتر void setTitle(string title) { this->title = title; } }; // main() هنا قمنا بتعريف الدالة int main() { // book إسمه Book هنا قمنا بتعريف كائن من Book book; // book الخاص بالكائن title و تمرير نص لها حتى تقوم بتخزينه في المتغير book من الكائن setTitle() هنا قمنا باستدعاء الدالة book.setTitle("C++ for beginners"); // للتأكد ما إن كانت قد تغيرت أم لا book الموجود في الكائن title هنا قمنا بطباعة قيمة المتغير cout << "Title: " << book.title; return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
Title: C++ for beginners
المفهوم الرابع
الكونستركتور ( Constructor ) عبارة عن دالة مميزة يتم استدعاؤها بشكل تلقائي عند إنشاء كائن من الكلاس.
هذه الدالة تجعلك قادر على تمرير قيم أولية للكائن بشكل مباشر له لحظة إنشائه.
شرح أهمية الكونستركتور في C++
من أهم الأشياء التي عليك التفكير بها عند إنشاء كلاس جديد, هي تسهيل طريقة إنشاء كائنات من هذا الكلاس لاحقاً.
من هنا جاءت فكرة الكونستركتور ( Constructor ) و الذي هو عبارة عن دالة, يتم إستدعائها أثناء إنشاء كائن من الكلاس لإعطاء قيم أولية للخصائص الموجودة فيه.
أيّ كلاس تقوم بتعريفه, يملك كونستركتور إفتراضي حتى لو تقم بتعريفه له بنفسك و سبب ذلك أنه لا يمكن إنشاء كائن من كلاس إلا من خلال كونستركتور هذا الكلاس.
لهذا إن لم تقم بتعريف كونستركتور للكلاس بنفسك, فكن على يقين أن مترجم لغة C++ سيقوم بإنشاء كونستركتور إفتراضي فارغ بدلاً عنك عندما تحاول إنشاء كائن من الكلاس.
نقاط مهمة حول الكونستركتور في C++
كل كلاس يتم إنشاؤه يحتوي على كونستركتور واحد على الأقل. و حتى إن لم تقم بتعريف أي كونستركتور, سيقوم المترجم بإنشاء واحد إفتراضي.
في كل مرة يتم إنشاء كائن جديد من كلاس, يجب استدعاء كونستركتور من الكلاس حتى يتم إنشاء هذا الكائن.
القاعدة الأساسية عند تعريف كونستركتور هي أنه يجب أن يحمل نفس إسم الكلاس و يكون نوعه
public
.في حال قمت بتعريف كونستركتور, لن يقوم المترجم بإنشاء واحد إفتراضي, أي لن يعود هناك كونستركتور إفتراضي.
يمكنك تعريف أكثر من كونستركتور. و يمكنك دائماً إنشاء كونستركتور فارغ, حتى تستخدمه إن كنت لا تريد إعطاء قيم أولية محددة للخصائص عند إنشاء كائن.
في المثال التالي قمنا بتعريف كلاس إسمه Book
و لم نقم بتعريف كونستركتور له.
المثال الأول
class Book { }
•سيقوم المترجم بإنشاء كونستركتور فارغ بشكل تلقائي عنا كالتالي.
class Book { public: // هذا كونستركتور إفتراضي أنشأه المترجم لأن الكلاس لا يحتوي على أي كونستركتور Book() { } }
طريقة استدعاء الكونستركتور في C++
عند استدعاء كونستركتور موجود في كلاس, يجب أن تمرر قيمة لكل باراميتر موجود فيه من خلال وضع قوسين بعد إسم الكائن و تمرير القيم فيه.
فيما ويلي وضعنا بعض الأمثلة التي توضح كيفية إستتدعاء كونستركتور من الكلاس.
إذا كان إسم الكونستركتور هكذا Book()
فإنك لا تحتاج فعل أي شيء حين تنشئ كائن من الكلاس حيث تنشئه كالتالي.
المثال الأول
Book book;
إذا كان إسم الكونستركتور هكذا Book(string title)
فإنك مجبر على تمرير قيمة لكونستركتور الكائن كالتالي عند إنشاءه كالتالي.
المثال الثاني
Book book("C++ for beginners");
إذا كان إسم الكونستركتور هكذا Book(string title, string author)
فإنك مجبر على تمرير قيم لكونستركتور الكائن كالتالي عند إنشاءه كالتالي.
المثال الثالث
Book book("C++ for beginners", "Mhamad Harmush");
إذا كان إسم الكلاس يحتوي على كونستركتور فارغ Book()
و كونستركتور يأخذ قيمتين هكذا Book(string title, string author)
فهنا تكون مخيّر على استدعاء الكونستركتور الذي تريده منهما عند إنشاء كائن من الكلاس.
المثال الرابع
// يمكنك أن تنشئ الكائن من خلال استدعاء الكونستركتور الأول كالتالي Book book(); // و يمكنك أن تنشئ الكائن من خلال استدعاء الكونستركتور الثاني كالتالي Book book("C++ for beginners", "Mhamad Harmush");
مثال شامل حول التعامل مع الكونستركتور في C++
في المثال التالي, قمنا بتعريف كلاس إسمه Book
يمثل المعلومات التي يمكن أن يتضمنها أي كتاب كعنوانه, إسم المؤلف, سعره و عدد صفحاته.
في الكلاس Book
قمنا بتعريف كونستركتور يمكننا من خلاله إدخال قيم مباشرةً للخصائص الموجودة في أي كائن ننشئه من الكلاس.
كما أننا قمنا بتعريف دالة إسمها printInfo()
عند استدعاءها تقوم بطباعة قيم الخصائص التي يمكلها أي كائن ننشئه من الكلاس.
مثال شامل حول التعامل مع الكونستركتور في C++
#include <iostream> using namespace std; // يحتوي على 4 متغيرات Book هنا قمنا بتعريف كلاس إسمه class Book { public: // هنا قمنا بتعريف خصائص الكلاس string title; string author; double price; int numberOfPages; // هنا قمنا بتعريف كونستركتور يجب تمرير 4 قيم له عندما يتم إستدعاؤه Book(string title, string author, double price, int numberOfPages) { // هنا قمنا بوضع القيم التي نمررها للكونستركتور بالترتيب في خصائص الكائن this->title = title; this->author = author; this->price = price; this->numberOfPages = numberOfPages; } // تقوم بطباعة كل قيم المتغيرات الموجودة فيه Book عند استدعاءها من أي كائن ننشئه من printInfo هنا قمنا بتعريف دالة إسمها void printInfo() { cout << "Title: " << title << "\n"; cout << "Author: " << author << "\n"; cout << "Price: " << price << "$\n"; cout << "Number of pages: " << numberOfPages << "\n"; } }; // main() هنا قمنا بتعريف الدالة int main() { // مع تمرير القيم التي نريد وضعها فيه بشكل مباشر من خلال الكونستركتور book إسمه Book هنا قمنا بتعريف كائن من Book book("C++ for beginners", "Mhamad Harmush", 9.99, 420); // حتى تقوم بطباعة القيم الموجودة فيه book من الكائن printInfo() هنا قمنا باستدعاء الدالة book.printInfo(); return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
Title: C++ for beginners Author: Mhamad Harmush Price: 9.99$ Number of pages: 420
المفهوم الخامس
الدستركتور ( Destructor ) عبارة عن دالة مميزة يتم استدعاؤها بشكل تلقائي عندما يتم مسح الكائن من الذاكرة.
هذه الدالة تجعلك قادر على فعل أي عملية تريدها بشكل مباشر قبل أن يتم حذف الكائن من الذاكرة.
شرح أهمية الدستركتور في C++
الدستركتور ( Destructor ) عبارة عن دالة مميزة يتم استدعاؤها بشكل تلقائي عندما يتم مسح الكائن من الذاكرة.
هذه الدالة تجعلك قادر على فعل أي عملية تريدها بشكل مباشر قبل أن يتم حذف الكائن من الذاكرة.
أيّ كلاس تقوم بتعريفه, يملك ديستكرتور إفتراضي حتى لو تقم بتعريفه له بنفسك و يتم إستدعاؤه بشكل تلقائي مباشرةً إذا قبل أن يتم مسح الكائن من الذاكرة.
نقاط مهمة حول الدستركتور في C++
كل كلاس يتم إنشاؤه يحتوي على دستركتور. أي حتى إن لم تقم بتعريف دستركتور بنفسك سيقوم المترجم بإنشاء واحد إفتراضي عنك.
الدستركتور يتم إستدعاؤه بشكل تلقائي عندما يتم مسح الكائن من الذاكرة.
القاعدة الأساسية عند تعريف دستركتور هي أنه يجب أن يحمل نفس إسم الكلاس و قبله رمز
~
و يكون نوعهpublic
.يمكنك تعريف دستركتور واحد فقط في الكلاس.
لا يمكن وضع باراميترات في الدستركتور.
متى يتم استدعاء الدستركتور في C++ ؟
في حال تم إنشاء الكائن بداخل دالة و تم تنفيذ كل محتوى الدالة لأنه من الطبيعي أن يتم حذف أي شيء تم تعريفه فيها بعد أن تتنفذ.
في حال تم تنفيذ جميع أوامر البرنامج الموضوعة بداخل الدالة
main()
لأنه من الطبيعي أن يتم حذف أي شيء تم تعريفه فيها بعد أن تتنفذ.في حال تم حذف الكائن من الذاكرة بأي طريقة أخرى.
أمثلة على الدستركتور في C++
في المثال التالي قمنا بتعريف كلاس إسمه Book
و قمنا فيه بتعريف كونستركتور إفتراضي يطبع الجملة "Constructor is called"
حين يتم استدعاؤه و دستركتور يقوم بطباعة الجملة "Destructor is called"
حين يتم استدعاؤه.
تذكر: الكونستركتور سيتم استدعاؤه حين يتم إنشاء كائن من الكلاس, و الدستركتور سيتم استدعاؤه بشكل تلقائي حين يتم مسح الكائن من الذاكرة.
مثال على الدستركتور في C++
#include <iostream> using namespace std; // يحتوي على 4 متغيرات Book هنا قمنا بتعريف كلاس إسمه class Book { public: // هنا قمنا بتعريف الكونستركتور الذي سنقوم باستدعائه حين ننشئ كائن من الكلاس Book() { cout << "Constructor is called" << endl; } // هنا قمنا بتعريف الدستركتور الذي سيتم استدعائه بشكل تلقائي حين يتم حذف الكائن من الذاكرة ~Book() { cout << "Destructor is called" << endl; } }; // main() هنا قمنا بتعريف الدالة int main() { // و بالتالي سيتم تنفيذ أمر الطباعة الموجود في الكونستركتور Book هنا قمنا بتعريف كائن من Book book; // بما أن البرنامج إنتهى هنا فهذا يعني أنه سيتم تنفيذ أمر الطباعة الموجود في الدستركتور return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
Constructor is called Destructor is called
المفهوم السادس
الدالة الصديقة ( Friend Function ) عبارة عن دالة يمكنها الوصول لأي شيء يمكله الكائن حتى إن كائن في الكلاس نوعه private
أو protected
.
شرح مفهوم الدالة الصديقة في C++
الدالة الصديقة ( Friend Function ) عبارة عن دالة يمكنها الوصول لأي شيء يمكله الكائن حتى إن كائن في الكلاس نوعه private
أو protected
.
ماهي شروط تعريف دالة صديقة في C++
أن يتم وضع شكل الدالة ( Prototype ) فقط بداخل الكلاس و محتواها ( Body ) خارج الكلاس.
عند وضع شكل الدالة بداخل الكلاس يجب أن تذكر الكلمة
friend
و لكن لا يجب أن تذكرها عندما تكتب محتوى الدالة خارج الكلاس.في الكلاس لا يهم المكان الذي تقوم فيه بوضع شكل الدالة ( Prototype ), أي لا يهم إن جعلتها
public
أوprivate
أوprotected
.محتوى الدالة ( Function Body ) يجب تعريفه خارج الكلاس.
أن تكون الدالة تملك باراميتر عبارة عن كائن من الكلاس الذي تنوي الوصول لأي شيء موجود فيه.
مثال شامل حول تعريف دالة صديقة في C++
في المثال التالي, قمنا بتعريف كلاس إسمه Demo
يحتوي على 4 متغيرات كل واحد منهم له Modifier مختلف.
بعدها قمنا بتعريف دالة صديقة إسمها printValues
عند استدعاءها نمرر لها كائن من الكلاس Demo
فتقوم بطباعة قيمه بشكل مرتب.
في الأخير قمنا بإنشاء كائن من Demo
و إعطاؤه قيم, و من ثم إستدعاء الدالة printValues()
منه حتى تقوم بطباعة قيمه.
مثال شامل حول تعريف دالة صديقة في C++
#include <iostream> using namespace std; // يحتوي على 4 متغيرات و دالة صديقة Demo هنا قمنا بتعريف كلاس إسمه class Demo { // printValues هنا قمنا بتعريف شكل الدالة الصديقة friend void printValues(Demo demo); int a = 1; public: int b = 2; private: int c = 3; protected: int d = 4; }; // في أولها friend و لاحظ أننا لم نضع الكلمة printValues هنا قمنا بتعريف محتوى الدالة الصديقة void printValues(Demo demo) { cout << "a = " << demo.a << "\n"; cout << "b = " << demo.b << "\n"; cout << "c = " << demo.c << "\n"; cout << "d = " << demo.d << "\n"; } // main() هنا قمنا بتعريف الدالة int main() { // demo إسمه Demo هنا قمنا بتعريف كائن من Demo demo; // لها حتى تقوم بطباعة القيم الموجودة فيه demo و تمرير الكائن printValues() هنا قمنا باستدعاء الدالة printValues(demo); return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
a = 1 b = 2 c = 3 d = 4
المفهوم السابع
هنا وضعنا مثال حول كيفية تعريف دالة تأخذ باراميتر نوعه مؤشر لكائن من كلاس ( Pointer to class ) بالإضافة إلى كيفية إستدعاءها.
التعامل مع مؤشر لكلاس هو نفسه تماماً التعامل مع مؤشر لـ struct
.
إذاً للوصول للأشياء الموجودة في مؤشر لكائن من كلاس نستخدم العامل ->
و ليس العامل .
المعتاد.
في المثال التالي, قمنا بتعريف كلاس إسمه Book
يمثل المعلومات التي يمكن أن يتضمنها أي كتاب كعنوانه, إسم المؤلف, سعره و عدد صفحاته.
بعدها قمنا بتعريف دالة إسمها printInfo
عند استدعاءها نمرر لها عنوان كائن من Book
موجود في الذاكرة فتقوم بطباعة قيمه بشكل مرتب.
في الأخير قمنا بإنشاء كائن من Book
و إعطاؤه قيم, و من ثم تمرير عنوانه في الذاكرة للدالة printInfo()
حتى تقوم بطباعة قيمه.
مثال تعريف دالة تأخذ باراميتر نوعه مؤشر لكائن من كلاس ( Pointer to class ) بالإضافة إلى كيفية إستدعاءها.
#include <iostream> using namespace std; // يحتوي على 4 متغيرات Book هنا قمنا بتعريف كلاس إسمه class Book { public: string title; string author; double price; int numberOfPages; }; // فتقوم بطباعة كل قيم المتغيرات الموجودة فيه Book عند استدعاءها نمرر لها عنوان كائن من printInfo هنا قمنا بتعريف دالة إسمها void printInfo(Book* book) { cout << "Title: " << book->title << "\n"; cout << "Author: " << book->author << "\n"; cout << "Price: " << book->price << "$\n"; cout << "Number of pages: " << book->numberOfPages << "\n"; } // main() هنا قمنا بتعريف الدالة int main() { // book إسمه Book هنا قمنا بتعريف كائن من Book book; // book هنا قمنا بإعطاء قيم لمتغيرات الكائن book.title = "C++ for beginners"; book.author = "Mhamad Harmush"; book.price = 9.99; book.numberOfPages = 420; // لها حتى تقوم بطباعة القيم الموجودة فيه book و تمرير عنوان الكائن printInfo() هنا قمنا باستدعاء الدالة printInfo(&book); return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
Title: C++ for beginners Author: Mhamad Harmush Price: 9.99$ Number of pages: 420
المفهوم الثامن
الأشياء المشتركة ( Static Members ) عبارة عن أي شيء يتم تعريفه في الكلاس بشكل يكون موّحداً بالنسبة لجميع الكائنات التي يتم إنشاءها منه.
شرح مفهوم الأشياء المشتركة في C++
الأشياء المشتركة ( Static Members ) عبارة عن أي شيء يتم تعريفه في الكلاس بشكل يكون موّحداً بالنسبة لجميع الكائنات التي يتم إنشاءها منه.
بشكل عام لجعل الشيئ الذي يتم تعريفه في الكلاس مشترك بين جميع الكائنات التي قد ننشئها منه نستخدم الكلمة static
كما سنرى بعد قليل في الأمثلة.
ماهي فوائد الأشياء المشتركة في C++
الأشياء المشتركة لها عدة استخدامات و فوائد فعلى سبيل المثال إذا أردت جميع الكائنات التي يتم إنشاءها من كلاس محدد تملك نفس قيمة متغير موجود في هذا الكلاس, يمكنك جعل نوع المتغير static
و عندها سيصبج مشترك بين جميع الكائنات.
من ناحية الذاكرة, بما أن المتغير المشترك يكون نفسه موضوع لجميع الكائنات التي يتم إنشاؤها من نفس الكلاس, فهذا يعني أنك لو قمت بإنشاء 50 كائن من الكلاس فإنه لن يتم إنشاء 50 نسخة من المتغير في الذاكرة, بل سيكون هناك نسخة واحدة منه مشتركة بين جميع الكائنات.
في هذا الدرس سنتعرف على كيفية تعريف متغيرات و دوال مشتركة و كيفية التعامل معها.
شرح المتغيرات المشتركة في C++
تعريف المتغير في الكلاس كمتغير مشترك يجعلك قادر على الوصول لقيمة هذا المتغير بشكل مباشر من الكلاس, أي بدون الحاجة لإنشاء كائن منه.
عند تعريف المتغير المشترك, لا يسمح لك بإعطائه قيمة أولية بداخل الكلاس حيث أنك يجب أن تعطيه قيمة أولية خارج الكلاس.
بعد إسناد قيمة أولية للمتغير المشترك, تستطيع تغيير قيمته لاحقاً من خلال أي كائن يمكنه الوصول إليها.
المتغير المشترك يجب تعريفه على مرحتلين:
يجب تعريفه بداخل الكلاس بدون إعطائه قيمة أولية مع الإشارة إلى أنه يجب تحديد نوعه
static
.يجب إعطاؤه قيمة أولية خارج الكلاس.
في المثال التالي, قمنا بتعريف كلاس إسمه Demo
يحتوي على متغير عادي إسمه x
و متغير مشترك إسمه y
.
المتغير المشترك y
قمنا بإعطائه 0
كقيمة أولية خارج الكلاس كما يفترض.
بعدها قمنا بإنشاء ثلاث كائنات من الكلاس Demo
و إعطاء قيمة مختلفة للمتغير x
الخاص بكل الكائن.
في النهاية قمنا بتغيير قيمة المتغير المشترك y
مرة واحدة حتى نلاحظ كيف أنها تغيرت بالنسبة لجميع الكائنات.
مثال في المتغيرات المشتركة في C++
#include <iostream> using namespace std; // Demo هنا قمنا بتعريف الكلاس class Demo { // y و المتغير المشترك x هنا قمنا بتعريف المتغير public: int x; static int y; }; // القيمة 0 كقيمة أولية y هنا قمنا بإعطاء المتغير المشترك int Demo::y = 0; // main() هنا قمنا بتعريف الدالة int main() { // Demo هنا قمنا بتعريف ثلاث كائنات من الكلاس Demo demo1, demo2, demo3; // التي يملكها كل كائن قمنا بإنشائه x هنا قمنا بتغيير قيمة المتغير demo1.x = 10; demo2.x = 20; demo3.x = 30; // التي يملكها كل كائن قمنا بإنشائه x هنا قمنا بطباعة قيمة المتغير cout << "demo1.x = " << demo1.x << endl; cout << "demo2.x = " << demo2.x << endl; cout << "demo3.x = " << demo3.x << endl; // التي هي نفسها لكل الكائنات و بالطبع الآن قيمته هي 0 لأننا لم نقم بتغيير قيمته الأولية y هنا قمنا بطباعة قيمة المتغير المشترك cout << "Demo.y = " << demo1.y << endl; cout << "Demo.y = " << demo2.y << endl; cout << "Demo.y = " << demo3.y << endl; // demo1 إلى 5 من خلال الكائن y هنا قمنا بتغيير قيمة // سيتم إعتبار أنها تغييرت بالنسبة لجميع الكائنات لأنها مشتركة فيما بينهم demo3 أو demo2 أو demo1 ملاحظة: سواء غيرناها من خلال الكائن demo1.y = 5; // demo1 من جديد للتأكد ما إن كانت حقاً أصبحت تساوي 5 أم لا بالنسبة لجميع الكائنات و ليس فقط الكائن y هنا قمنا بطباعة قيمة المتغير المشترك cout << "Demo.y = " << demo1.y << endl; cout << "Demo.y = " << demo2.y << endl; cout << "Demo.y = " << demo3.y << endl; return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
demo1.x = 10 demo2.x = 20 demo3.x = 30 Demo.y = 0 Demo.y = 0 Demo.y = 0 Demo.y = 5 Demo.y = 5 Demo.y = 5
شرح الدوال المشتركة في C++
فكرة الدوال المشتركة ببساطة هي تعريف دالة في كلاس يمكن استدعاءها بشكل مباشر منه بدون الحاجة لإنشاء كائن منه.
الدالة التي يتم تعريفها كدالة مشتركة يمكنها فقط التعامل مع المتغيرات المشتركة, أي التي نوعها static
مثلها.
بالنسبة لطريقة تعريف دالة مشتركة فهي نفسها طريقة تعريف دالة عادية مع إضافة الكلمة static
إليها.
في المثال التالي, سنستخدم متغير و دالة مشتَركين لمعرفة عدد الكائنات التي يتم إنشاؤها من الكلاس.
في البداية قمنا بتعريف كلاس إسمه Demo
يحتوي على متغير مشترك إسمه counter
و دالة مشتركة إسمها getCounter
عند استدعاءها تقوم بإرجاع قيمة المتغير counter
فقط.
في الكلاس Demo
قمنا بتعديل الكونستركتور الإفتراضي لجعله يقوم بإضافة 1
على قيمة المتغير counter
كلما تم إستدعاؤه.
بمعنى آخر, كلما قمنا بإنشاء كائن - من خلال الكونستركتور الإفتراضي - سيتم إضافة 1
على قيمة المتغير counter
و بالتالي فإن قيمة coutner
ستمثل عدد الكائنات التي يتم إنشاءها من الكلاس Demo
.
في النهاية قمنا بإنشاء ثلاث كائنات من الكلاس Demo
و من ثم إستدعاء الدالة getCounter()
لمعرفة عدد الكائنات التي تم إنشاؤها.
مثال في الدوال المشتركة في C++
#include <iostream> using namespace std; // Demo هنا قمنا بتعريف الكلاس class Demo { public: // counter هنا قمنا بتعريف المتغير المشترك static int counter; // كلما تم استدعاؤه counter هنا قمنا بتعريف الكونستركتور و جعلناه يزيد 1 على قيمة المتغير المشترك Demo() { counter++; } // فقط عندما يتم استدعاءها counter و قلنا أنها ترجع قيمة المشترك getCounter() هنا قمنا بتعريف الدالة المشتركة static int getCounter() { return counter; } }; // القيمة 0 كقيمة أولية counter هنا قمنا بإعطاء المتغير المشترك int Demo::counter = 0; // main() هنا قمنا بتعريف الدالة int main() { // Demo هنا قمنا بتعريف ثلاث كائنات من الكلاس Demo demo1, demo2, demo3; // لطباعة عدد الكائنات التي تم إنشاءها منه Demo من الكلاس getCounter() هنا قمنا باستدعاء الدالة cout << "total created object = " << Demo::getCounter(); return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
total created object = 3