C++ تعدد الأشكال
مفهوم تعدد الأشكال في C++
تعدد الأشكال أو بوليمورفيزم ( Polymorphism ) هو مجرد أسلوب في كتابة الكود يقصد منه بناء دالة تنفذ أوامر مختلفة على حسب الكائن الذي نمرره لها عند إستدعاءها.
في العادة تعدد الأشكال يكون مربتط بشكل أساسي بالوراثة حيث تكون الدالة مبنية على أساس الكلاس الأب, و لكننا عند إستدعاءها نمرر لها كائن من إحدى الكلاسات التي ترث منه.
تطبيق مبدأ تعدد الأشكال في C++
في المثال التالي قمنا بتعريف كلاس مجرّد إسمه Country يعتبر الكلاس الأساسي لأي كلاس يمثل بلد و بالتالي أي كلاس سننشئه ليمثل بلد ما يجب أن يرث منه. في هذا الكلاس قمنا بتجهيز 3 دوال مجرّدة أيضاً.
بعدها قمنا بتعريف كلاس إسمه Egypt و كلاس إسمه Australia يرثان من الكلاس Country و يفعلان Override لكل الدوال التي ورثوها منه.
بعدها قمنا بإنشاء دالة إسمها printCountryInfo() مهمتها إستدعاء جميع الدوال الموجودة في الكائن الذي نمرره لها بشرط أن يكون هذا الكائن قد تم إنشاؤه من كلاس يرث من الكلاس Country.
في الأخير قمنا بإنشاء كائن من الكلاس Egypt و كائن من الكلاس Australia و تمرير كل كائن منهما للدالة printCountryInfo().
مثال
#include <iostream>
using namespace std;
// يحتوي على 3 دوال مجردة Country هنا قمنا بإنشاء كلاس إسمه
class Country
{
public:
virtual void name() = 0;
virtual void capital() = 0;
virtual void language() = 0;
};
// للدوال الثلاثة الموجودة التي ورثها منه Override و يفعل Country يرث من الكلاس Australia هنا قمنا بإنشاء كلاس فارغ إسمه
class Australia : public Country
{
public:
void name() override
{
cout << "Country: Australia\n";
}
void capital() override
{
cout << "Capital: Canberra\n";
}
void language() override
{
cout << "Language: English\n";
}
};
// للدوال الثلاثة الموجودة التي ورثها منه Override و يفعل Country يرث من الكلاس Egypt هنا قمنا بإنشاء كلاس فارغ إسمه
class Egypt : public Country
{
public:
void name() override
{
cout << "Country: Egypt\n";
}
void capital() override
{
cout << "Capital: Cairo\n";
}
void language() override
{
cout << "Language: Arabic\n";
}
};
// فتقوم بتنفيذ الدوال الثلاثة الموجودة فيه Country عند استدعاءها نمرر لها عنوان كائن يرث من الكلاس printCountryInfo هنا قمنا بتعريف دالة إسمها
void printCountryInfo(Country& country)
{
country.name();
country.capital();
country.language();
cout << "----------------\n";
}
// main() هنا قمنا بتعريف الدالة
int main()
{
// eg إسمه Egypt و كائن من الكلاس au إسمه Australiaypt هنا قمنا بإنشاء كائن من الكلاس
Australia au;
Egypt eg;
// لكي يتم إستدعاء الدوال الثلاثة منهم au و au و مررنا لها الكائنين printCountryInfo() هنا قمنا باستدعاء الدالة
printCountryInfo(au);
printCountryInfo(eg);
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
Country: Australia Capital: Canberra Language: English ---------------- Country: Egypt Capital: Cairo Language: Arabic ----------------
تطبيق مبدأ تعدد الأشكال مع المصفوفات في C++
المصفوفات العادية لا يمكن تخزين كائنات من كلاسات مختلفة فيها و ضمان أن لا يسبب هذا الأمر أي مشاكل لأنها ليست مهيئة لهذا الأمر, لذلك سنستخدم نوع متقدم من المصفوفات للتعامل بهدف تعليمك كيف يمكن تطبيق مبدأ تعدد الأشكال مع المصفوفات.
من أحد أول أنواع المصفوفات المتقدمة هو النوع vector و هو النوع الذي سنستخدمه بعد قليل مع الإشارة إلى أننا سنشرحه بتفصيل ممل لاحقاً في الدورة.
الكائن الذي تنشئه من الكلاس vector يمكن تخزين عدد غير محدد من القيم العادية أو الكائنات بداخله و متى شئت يمكن إضافة المزيد أو حذف الموجود فيه على عكس المصفوفات العادية التي عليك تحديد حجمها لحظة إنشائها.
حتى تتمكن من إنشاء كائنات من الكلاس vector يجب أن تضمّن الكلاس vector أولاً.
لوضع أي شيء قيمة بداخل vector نستخدم الدالة push_back() و لحذف أي قيمة نسخدم الدالة erase().
في المثال التالي قمنا بإعادة المثال السابق و لكننا هذه المرة في الدالة main() قمنا بإنشاء كائن من الكلاس vector إسمه countries و تخزين الكائنات التي أنشأناها من الكلاس Australia و Egypt فيه.
مثال
#include <iostream>
#include <vector>
using namespace std;
// يحتوي على 3 دوال مجردة Country هنا قمنا بإنشاء كلاس إسمه
class Country
{
public:
virtual void name() = 0;
virtual void capital() = 0;
virtual void language() = 0;
};
// للدوال الثلاثة الموجودة التي ورثها منه Override و يفعل Country يرث من الكلاس Australia هنا قمنا بإنشاء كلاس فارغ إسمه
class Australia : public Country
{
public:
void name() override
{
cout << "Country: Australia\n";
}
void capital() override
{
cout << "Capital: Canberra\n";
}
void language() override
{
cout << "Language: English\n";
}
};
// للدوال الثلاثة الموجودة التي ورثها منه Override و يفعل Country يرث من الكلاس Egypt هنا قمنا بإنشاء كلاس فارغ إسمه
class Egypt : public Country
{
public:
void name() override
{
cout << "Country: Egypt\n";
}
void capital() override
{
cout << "Capital: Cairo\n";
}
void language() override
{
cout << "Language: Arabic\n";
}
};
// فتقوم بتنفيذ الدوال الثلاثة الموجودة فيه Country عند استدعاءها نمرر لها عنوان كائن يرث من الكلاس printCountryInfo هنا قمنا بتعريف دالة إسمها
void printCountryInfo(Country& country)
{
country.name();
country.capital();
country.language();
cout << "----------------\n";
}
// main() هنا قمنا بتعريف الدالة
int main()
{
// eg إسمه Egypt و كائن من الكلاس au إسمه Australiaypt هنا قمنا بإنشاء كائن من الكلاس
Australia au;
Egypt eg;
// Country سنضع فيه فيه مؤشرات لكائنات ترث من الكلاس countries إسمه vector هنا قمنا بإنشاء كائن من الكلاس
vector<Country*> countries;
// countries في الكائن eg و au هنا قمنا بإضافة عناوين الكائنين
countries.push_back(&au);
countries.push_back(&eg);
// و تمرير عنوان الكائن لها printCountryInfo() و من ثم تقوم باستدعاء الدالة countries هنا قمنا بإنشاء حلقة في كل مرة تمر على عنصر موجود في الكائن
for (unsigned i=0; i<countries.size(); i++)
{
printCountryInfo(*countries[i]);
}
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
Country: Australia Capital: Canberra Language: English ---------------- Country: Egypt Capital: Cairo Language: Arabic ----------------
C++ الثوابت
مفهوم الثوابت في C++
الثابت ( Constant ) عبارة عن أي شيء يتم تعريفه بشكل لا يمكن إعادة تغيير قيمته.
لتعريف المتغير أو الكائن كثابت نستخدم الكلمة const و عندها نصبح غير قادرين على تغيير قيمته.
الكلمة const يمكن استخدامها مع الأشياء التالية:
المتغيرات (Variables).
المؤشرات (Pointers).
باراميترات لدوال (Functions Parameters).
خصائص الكلاس (Class Member Variables).
دوال الكلاس (Class Member Functions).
الكائنات (Objects).
تعريف متغير ثابت في C++
إذا قمت بتعريف متغير ثابت فلا يمكنك تغيير قيمته لاحقاً و لا بد لك من إعطاؤه قيمة مباشرةً عند تعريفه.
القيمة الموضوعة بداخل المتغير يمكن الحصول عليها بشكل عادي مثل القيمة التي يتم وضعها بداخل متغير عادي و لكن لا يمكن تغييرها فقط.
في المثال التالي قمنا بتعريف متغير ثابت إسمه x قيمته 10 و من ثم حاولنا إعادة تغيير قيمته.
المثال الأول
#include <iostream>
using namespace std;
int main()
{
// قيمته تساوي 10 x هنا قمنا بتعريف متغير ثابت إسمه
const int x = 10;
// x هنا قمنا بطباعة قيمة المتغير
cout << "x = " << x;
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
x = 10
في المثال التالي قمنا بتعريف متغير ثابت إسمه x قيمته 10 و من ثم حاولنا إعادة تغيير قيمته.
المثال الثاني
#include <iostream>
using namespace std;
int main()
{
const int x = 10; // قيمته تساوي 10 x هنا قمنا بتعريف متغير ثابت إسمه
x = 5; // الأمر الذي سيؤدي لحدوث خطأ x هنا حاولنا تغيير قيمة المتغير
return 0;
}
•سيظهر الخطأ التالي عند التشغيل و الذي يعني أنه لا يمكن تغيير القيمة الموجودة في المتغير x.
تعريف مؤشر كثابت في C++
المؤشر العادي يمكن في أي وقت جعله يشير لأي شيء موجود في الذاكرة كما سبق و شاهدنا.
تعريف المؤشر نفسه كثابت يجعل المؤشر مخصص للشيء الذي تم جعله يشير إليه عند تعريفه مع عدم القدرة على جعله يشير لشيء آخر لاحقاً.
في المثال التالي قمنا بتعريف متغير إسمه x قيمته 5 و متغير إسمه y قيمته 7.
بعدها قمنا بإنشاء مؤشر ثابت إسمه ptr يشير لقيمة المتغير x و من ثم حاولنا جعله يشير لقيمة المتغير y مما سيؤدي لحدوث مشكلة عند التشغيل.
مثال
#include <iostream>
using namespace std;
int main()
{
// y و x هنا قمنا بتعريف المتغيرين
int x = 5;
int y = 7;
// في الذاكرة x و جعله يشير لعنوان المتغير ptr هنا قمنا بتعريف المؤشر الثابت
int* const ptr = &x;
// في الذاكرة مما سيؤدي لحدوث خطأ عند التشغيل y يشير لعنوان المتغير ptr هنا حاولنا جعل المؤشر
ptr = &y;
return 0;
}
•سيظهر الخطأ التالي عند التشغيل و الذي يعني أنه لا يمكن تغيير القيمة التي يشير إليها المؤشر ptr.
تعريف مؤشر لثابت في C++
إذا كنت تملك متغير ثابت يمكنك إنشاء مؤشر له و لكن عندها يجب أن تنشئ المؤشر كثابت أيضاً مما يجعلك غير قادر على جعله يشير لشيء آخر لاحقاً.
في المثال التالي قمنا بتعريف متغير ثابت إسمه x قيمته 10 و مؤشر ثابت إسمه ptr يشير لقيمته.
في الأخير قمنا بعرض القيمة التي يشير إليها المؤشر ptr في الذاكرة.
مثال
#include <iostream>
using namespace std;
int main()
{
// قيمته تساوي 10 x هنا قمنا بتعريف متغير ثابت إسمه
const int x = 10;
// في الذاكرة x و جعله يشير لعنوان المتغير الثابت ptr هنا قمنا بتعريف المؤشر الثابت
const int* ptr = &x;
// ptr هنا قمنا بطباعة قيمة المتغير الثابت الذي يشير إليه المؤشر الثابت
cout << "*ptr = " << *ptr;
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
*ptr = 10
في المثال السابق, المؤشر الثابت ptr الذي قمنا بتعريفه في السطر 11 يمكننا تعريفه كالتالي و الحصول على نفس النتيجة.
const int* const ptr = &x;
و هذه طريقة مختلفة أيضاً لتعريفه.
int const* ptr = &x;
تعريف باراميتر الدالة كثابت في C++
في حال كنت تنوي تمرير قيمة متغير ثابت لدالة و تريد أن يتم معاملة القيمة التي يتم تمريرها كأنها قيمة ثابتة أيضاً, يجب أن تعرف نوع البارميتر في الدالة كثابت.
في المثال التالي قمنا بتعريف دالة إسمها addOne عند استدعاءها نمرر لها عدد فتقوم بإضافة 1 عليه و ترجع قيمته.
المثال الأول
#include <iostream>
using namespace std;
// عند استدعاءها نمرر لها أي عدد فترجع قيمته مضافاً إليها 1 addOne هنا قمنا بتعريف دالة إسمها
int addOne(int n)
{
// n هنا قمنا بإضافة 1 على القيمة الموجودة في المتغير
n++;
// n هنا قمنا بإرجاع قيمة المتغير
return n;
}
// main() هنا قمنا بتعريف الدالة
int main()
{
// قيمته 5 x هنا قمنا بتعريف متغير ثابت إسمه
const double x = 5;
// لها و طباعة الناتج الذي سترجعه x و تمرير قيمة addOne() هنا قمنا بتمرير قيمة باستدعاء الدالة
cout << "5 + 1 = " << addOne(x);
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
5 + 1 = 6
في المثال التالي قمنا بإعادة المثال السابق و لكننا هذه المرة جعلنا القيمة التي يتم تمريرها للدالة يتم إعتبارها قيمة ثابتة مما سيؤدي لحدوث خطأ عند التشغيل.
ملاحظة: قمنا بتعليم السطر الذي أجرينا عليه التعديل باللون الأصفر.
المثال الثاني
#include <iostream>
using namespace std;
// كثابت n هنا قمنا بتعريف الباراميتر
int addOne(const int n)
{
// n السطر التالي سيسبب خطأ عند التشغيل لأنه لا يسمح بتغيير قيمة الثابت
n++;
return n;
}
int main()
{
const double x = 5;
cout << "5 + 1 = " << addOne(x);
return 0;
}
•سيظهر الخطأ التالي عند التشغيل و الذي يعني أنه لا يمكن تغيير قيمة الثابت n.
في المثال التالي قمنا بتعريف متغير ثابت يمثل سعر منتج إسمه price و قيمته 50.
بعدها قمنا بتعريف دالة إسمها finalPrice() عند استدعائها نمرر لها الثابت price فترجع لنا سعر المنتج محسوم منه 20 بالمئة.
المثال الأول
#include <iostream>
using namespace std;
// أيضاً double و عند استدعاءها يجب أن نمرر لها إسم متغير ثابت نوعه double ترجع قيمة نوعها finalPrice هنا قمنا بتعريف دالة إسمها
double finalPrice(const double n)
{
// هنا كتبنا معادلة القيمة التي سترجعها الدالة و التي ستعطي في النهاية قيمة العدد الذي نمرره لها - 20% منها
return n - (n * 0.2);
}
// main() هنا قمنا بتعريف الدالة
int main()
{
// يمثل سعر منتج و قيمته تساوي 50 price هنا قمنا بتعريف متغير إسمه
const double price = 50;
// و طباعة النتيجة النهائية التي سيتم إرجاعها على أنها السعر النهائي بعد الحسم finalPrice() للدالة price هنا قمنا بتمرير قيمة المتغير
cout << "Price after 20% sold = " << finalPrice(price) << "$";
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
Price after 20% sold = 40$
سنعيد نفس المثال السابق و لكننا هذه المرة سنخزن القيمة التي نمررها للدالة في متغير و من ثم نتعامل مع المتغير الجديد الذي تم تمريره للدالة.
ملاحظة: إذا كنت تنوي إجراء عدة عمليات على القيمة التي يتم تمريرها للدالة فهذا الأسلوب أفضل و أسهل لك.
المثال الثاني
#include <iostream>
using namespace std;
double finalPrice(const double n)
{
// thePrice هنا قمنا بتخزين قيمة الثابت في متغير إسمه
int thePrice = n;
// بمقدار 20% و الناتج النهائي أيضاً قمنا بتخزينه فيه thePrice هنا قمنا بإجراء حسم على القيمة الموجودة في المتغير
thePrice -= (thePrice * 0.2);
// thePrice هنا قمنا بإرجاع قيمة المتغير
return thePrice;
}
int main()
{
const double price = 50;
cout << "Price after 20% sold = " << finalPrice(price) << "$";
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
Price after 20% sold = 40$
تعريف خصائص ثابتة في الكلاس في C++
إذا أردت تعريف خصائص ثابتة في الكلاس, بمعنى أنه لا يمكن تغيير القيم التي نعطيهم إياها لاحقاً, فلا بد لك من تمرير قيم لكل خاصية ثابتة قمت بتعريفها في الكائن لحظة إنشاء الكائن و نفعل ذلك من خلال كونستركتور الكائن كما سنرى الآن.
في المثال التالي قمنا بتعريف كلاس إسمه Player يحتوي على متغير ثابت إسمه name بالإضافة إلى كونستركتور يسمح لنا بتمرير قيمة للثابت name.
مثال
#include <iostream>
using namespace std;
// Player هنا قمنا بإنشاء كلاس إسمه
class Player
{
public:
// name هنا قمنا بتعريف متغير (خاصية) ثابت إسمه
const string name;
// هنا قمنا بتعريف كونستركتور يجب إعطاؤه قيمة عند استدعاؤه
// name و من ثم يتم وضعها في الخاصية n القيمة التي نمررها لها يتم تخزينها بشكل مؤقت في المتغير
Player(string n):name(n)
{
}
};
// main() هنا قمنا بتعريف الدالة
int main()
{
// name و التي سيتم وضعها كقيمة للخاصية n و تمرير قيمة له مكان الباراميتر player إسمه Player هنا قمنا بإنشاء كائن من الكلاس
Player player("Mhamad");
// player التي يملكها الكائن name هنا قمنا بعرض قيمة الخاصية
cout << "Player Name = " << player.name;
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
Player Name = Mhamad
في المثال السابق, إذا أردت تسمية الباراميتر الموضوع في الكونستركتور name كإسم الخاصية و الحصول على نفس النتيجة فسيصبح شكل الكونستركتور كالتالي.
const string name;
Player(string name):name(name)
{
}
معلومة: في حال كنت تريد تمرير قيمة لأكثر من ثابت في الكونستركتور, فيجب أن تضع فواصل بين كل قيمتين كالتالي.
const string name;
const int age;
Player(string n, int a):name(n), age(a)
{
}
تعريف الكائن كثابت في C++
إذا أردت إنشاء كائن ثابت بمعنى أنه لا يمكن تغيير قيمة أي خاصية موجودة فيه فيمكنك إنشاء الكائن ككائن ثابت.
عند إنشاء كائن ثابت من كلاس لا يمكنك استدعاء أي دالة في الكلاس تتعامل مع أحد الخصائص الموجودة فيه إلا إذا قمت بجعل الدالة من الأساس ثابتة أيضاً.
في المثال التالي قمنا بتعريف كلاس إسمه Player يحتوي على متغيرين إسمهما name و age كخصائص عادية.
كما أننا قمنا بتعريف كونستركتور يسمح لنا بتمرير قيم للخصائص name و age و دالة ثابتة يمكن استخدامها لعرض قيمهما بشكل مرتب.
مثال
#include <iostream>
using namespace std;
// Player هنا قمنا بإنشاء كلاس إسمه
class Player
{
public:
// age و name هنا قمنا بتعريف الخصائص
string name;
int age;
// هنا قمنا بتعريف الكونستركتور الذي يسمح لنا بتمرير قيم للخصائص عند تعريف الكائن
// لأنه يجب أن نمرر قيم أولية لكل خصائص الكائن في حال كنا سننشئه ككائن ثابت
Player(string name, int age)
{
this->name = name;
this->age = age;
}
// كدالة ثابتة حتى نتمكن من استدعاءها في حال قمنا إنشاء كائن ثابت من الكلاس printInfo() هنا قمنا بتعريف الدالة
void printInfo() const
{
cout << "Name = " << this->name << endl;
cout << "Age = " << this->age << endl;
}
};
// main() هنا قمنا بتعريف الدالة
int main()
{
// مع تمرير قيم أولية للخصائص التي يملكها من خلال الكونستركتور player إسمه Player هنا قمنا بإنشاء كائن ثابت من الكلاس
const Player player("Mhamad", 26);
// حتى تعرض قيم الخصائص الموجودة فيه بشكل مرتب player هنا قمنا باستدعاء الدالة من الكائن الثابت
player.printInfo();
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
Name = Mhamad Age = 26
الكلمة mutable في C++
في حال كنت تنوي إنشاء كائن ثابت من الكلاس و لكنك تريد جعل بعض الخصائص الموجودة فيه غير ثابتة بمعنى أنه يمكن تغيير قيمها لاحقاً بعد إنشاء الكائن, يجب أن تعرف الخاصية التي تريد جعل قيمتها غير ثابتة mutable حتى يفهم المترجم أنك تريد ذلك.
في المثال التالي قمنا بتعريف كلاس إسمه Player يحتوي على ثلاث متغيرات إسمهم name و age و job كخصائص في الكلاس.
الخاصية job جعلنا نوعها mutable حتى نستطيع تغيير قيمتها متى أردنا من الكائن الذي ننشئه من الكلاس حتى و إن قمنا بإنشائه ككائن ثابت.
كما أننا قمنا بتعريف كونستركتور يسمح لنا بتمرير قيم للخصائص name و age و دالة ثابتة يمكن استخدامها لعرض قيم كل الخصائص بشكل مرتب.
مثال
#include <iostream>
using namespace std;
// Player هنا قمنا بإنشاء كلاس إسمه
class Player
{
public:
// mutable نوعها job و لاحظ أن الخاصية ,job و age ,name هنا قمنا بتعريف الخصائص
string name;
int age;
mutable string job;
// عند إنشاء كائن لأنه يجب age و name هنا قمنا بتعريف الكونستركتور الذي يسمح لنا بتمرير قيم للخاصيتين
// mutable أن نمرر قيم أولية لكل خاصية موجودة في الكائن في حال كنا سننشئه ككائن ثابت طالما أن نوعها ليس
Player(string name, int age)
{
this->name = name;
this->age = age;
}
// كدالة ثابتة حتى نتمكن من استدعاءها في حال قمنا إنشاء كائن ثابت من الكلاس printInfo() هنا قمنا بتعريف الدالة
void printInfo() const
{
cout << "Name = " << this->name << endl;
cout << "Age = " << this->age << endl;
cout << "Job = " << this->job << endl;
}
};
// main() هنا قمنا بتعريف الدالة
int main()
{
// مع تمرير قيم أولية للخصائص الثابتى التي يملكها من خلال الكونستركتور player إسمه Player هنا قمنا بإنشاء كائن ثابت من الكلاس
const Player player("Mhamad", 26);
// كما سبق و قلنا mutable و نستطيع فعل ذلك لأن نوعها player التي يملكها الكائن job هنا قمنا بإعطاء قيمة للخاصية
player.job = "Programmer";
// حتى تعرض قيم الخصائص الموجودة فيه بشكل مرتب player هنا قمنا باستدعاء الدالة من الكائن الثابت
player.printInfo();
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
Name = Mhamad Age = 26 Job = Programmer
الكلمة #define في C++
الكلمة #define يمكن أن تستخدم لتعريف متغير قيمته ثابتة مثل الكلمة const و لكن بأسلوب مختلف.
في المثال التالي قمنا بتعريف متغير ثابت إسمها MAX_VALUE قيمته تساوي 1000.
مثال
#include <iostream>
using namespace std;
#define MAX_VALUE 1000
int main()
{
// MAX_VALUE قيمته تساوي قيمة الثابت x هنا قمنا بتعريف متغير إسمه
int x = MAX_VALUE;
// x هنا قمنا بطباعة قيمة المتغير
cout << "x = " << x;
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
x = 1000
تنبيه
الكلمة #define قد تسبب مشاكل غير متوقعة في البرنامج لأن نوع القيمة التي يتم وضعها فيها ليس محدداً, لذلك يفضّل تعريف المتغير الثابت دائماً بواسطة الكلمة const بدلاً من الكلمة #define.
C++ النوع enum
مفهوم النوع enum في C++
النوع enum يستخدم لتعريف قائمة من القيم أو عدة مجموعة قيم ثابتة بشكل منطقي.
في نهاية الأمر تعتبر هذه المجموعة كنوع جديد من البيانات و فيه عدة قيم محتملة يمكن الإختيار منها فقط.
النوع enum مفيد جداً في حال أردت بناء كود مخصص فقط للتعامل مع نوع محدد من القيم و في حال حاول أي مبرمج آخر عدم الإلتزام بنوع البيانات الذي فرضته أنت و قام بتمرير قيم من نوع آخر فإنه يتم تنبيهه مباشرةً قبل تشغيل الكود و في حال أصر على ذلك و قام بتشغيل الكود فإن مترجم لغة C++ سيمنعه وقت التشغيل و يظهر له خطأ.
بشكل عام, في حال أردت تعريف مجموعة قيم مترابطة يستحيل أن تتبدل فالخيار الأمثل هو تعريف هذه القيم في الأساس بداخل enum.
أمثلة حول بعض المعلومات الثابتة في الحياة و التي أيضاً تعتبر ثابتة في المنطق هي:
فصول السنة ( الخريف, الشتاء, الربيع, الصيف )
الإتجاهات ( الشمال - الجنوب - الشرق - الغرب )
أيام الأسبوع ( الإثنين - الثلاثاء - الأربعاء إلخ.. )
أشهر السنة ( كانون الثاني - شباط - آذار إلخ.. )
الجنس ( ذكر - أنثى )
عند التعامل مع هذا النوع الثابت من البيانات و الذي لا نريد لأحد أن يعبث به فإننا نستخدم النوع enum و هذا ما سنراه لاحقاً في الأمثلة.
طريقة تعريف enum في C++
تعريف enum يشبه كثيراً طريقة تعريف struct و لكن التعامل مع المتغيرات التي نضعها فيه لاحقاً هو ما يختلف.
عند وضع متغيرات بداخله يمكنك إعطاؤهم قيم بنفسك أو ترك المترجم يعطيهم قيم إفتراضية بنفسه.
إفتراضياً المتغيرات التي تضعها يتم إعطاؤها قيم بالترتيب إبتداءاً من صفر, فمثلاً أولاً أول متغير تكون قيمته 0 و الثاني تكون قيمته 1 و هكذا.
في المثال التالي قمنا بتعريف enum إسمه Days يمثل أيام الأسبوع.
بعدها قمنا بإنشاء كائن منه و جعلناه يساوي أحد الثوابت الموجودة في Days و من ثم طباعة قيمته.
مثال
#include <iostream>
using namespace std;
// وضعنا فيه 7 ثوابت Days إسمه enum هنا قمنا بتعريف
enum Days
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
int main()
{
// فقط Days و الهدف منه تخزين إحدى القيم الموجودة في today إسمه Days هنا قمنا بإنشاء كائن من
Days today;
// today في الكائن Days الموجود بالأساس في Monday هنا قمنا بتخزين قيمة الثابت
today = Monday;
// today هنا قمنا بطباعة قيمة الكائن
cout << "today value = " << today;
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
today value = 0
في المثال السابق قد تقول لما كل هذا العذاب!
لما لا نقوم بتعريف المتغير today بشكل مباشر كالتالي ؟!
int today = 0;
معلومة مهمة
في المثال السابق, لو قمت بتعريف today كمتغير عادي, كان بإمكانك وضع أي قيمة تريد فيه مثل 20 أو أي قيمة أخرى.
فائدة تعريف today ككائن من Days هو أنك أصبحت مجبر على استخدام الثوابت الموجودة فيه لتحديد قيمة today مما يجعلك تضمن أنه لا يمكن تمرير أي قيمة عشوائية غير القيم التي قمت أنت بتحديدها فمثلاً في المثال السابق يمكنك أن تغيير قيمة الكائن today كالتالي.
// Days لأنها موجودة في الأصل في Monday يمكنك وضع القيمة today = Monday; // Days لأنها موجودة في الأصل في Friday يمكنك وضع القيمة today = Friday;
مفاهيم مهمة حول النوع enum في C++
المفهوم الأول
هنا وضعنا مثال يوضح طريقة تعيين قيم للثوابت الموضوعة في enum و كيفية الإستفادة من هذه القيم.
في حال أردت تعيين قيم للثوابت التي تنوي وضعها في enum بنفسك بدل القيم الإفتراضية التي يضعها المترجم, يمكنك وضع أعداد صحيحة كقيم لها.
ملاحظة: لا يمكنك وضع قيم نصية (كلمات أو جمل) كقيم للثوابت التي نضعها فيه.
في المثال التالي قمنا بتعريف enum إسمه Months يحتوي على ثوابت تمثل أشهر السنة و عدد الأيام الموجودة فيها.
بعدها قمنا بإنشاء كائن منه و جعلناه يساوي أحد الثوابت (أي الأشهر) الموجودة في Months و من ثم طباعة قيمته.
مثال
#include <iostream>
using namespace std;
// وضعنا فيه 12 ثابت مع إعطائهم قيم Months إسمه enum هنا قمنا بتعريف
enum Months
{
Jan = 31,
Feb = 28,
Mar = 31,
Apr = 30,
May = 31,
Jun = 30,
Jul = 31,
Aug = 31,
Sep = 30,
Oct = 31,
Nov = 30,
Dec = 31
};
int main()
{
// فقط Months و الهدف منه تخزين إحدى القيم الموجودة في selectedMonth إسمه Months هنا قمنا بإنشاء كائن من
Months selectedMonth;
// selectedMonth في الكائن Months الموجود بالأساس في May هنا قمنا بتخزين قيمة الثابت
selectedMonth = May;
// May و التي تمثل عدد الأيام الموجودة في الشهر selectedMonth هنا قمنا بطباعة قيمة الثابت
cout << "May contain " << selectedMonth << " days";
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
May contain 31 days
المفهوم الثاني
هنا وضعنا مثال يعلمك كيف تقوم بالتشييك على قيمة الكائن الذي ننشئه من enum باستخدام الجملة switch مما يجعل كتابة الكود أكثر سهولة و وضوح.
من أكثر الأشياء التي تميّز النوع enum أنه يمكن استخدام الجملة switch للتشييك على قيمة الكائن الذي ننشئه منه مما يجعل كتابة الكود أكثر سهولة و وضوح.
في المثال التالي قمنا بتعريف enum إسمه Days يمثل أيام الأسبوع.
بعدها قمنا بإنشاء كائن منه و جعلناه يساوي أحد الثوابت (الأيام) الموجودة في Days.
في الأخير قمنا باستخدام الجملة switch للتشيك على قيمة الكائن و على أساسها نطبع له الجملة المناسبة.
مثال
#include <iostream>
using namespace std;
// وضعنا فيه 7 ثوابت Days إسمه enum هنا قمنا بتعريف
enum Days
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
int main()
{
// فقط Days و الهدف منه تخزين إحدى القيم الموجودة في selectedDay إسمه Days هنا قمنا بإنشاء كائن من
Days selectedDay;
// selectedDay في الكائن Days الموجود بالأساس في Thursday هنا قمنا بتخزين قيمة الثابت
selectedDay = Thursday;
// selectedDay هنا قمنا بالتشييك على قيمة الكائن
switch(selectedDay)
{
// سيتم تنفيذ أمر الطباعة التالي Thursday أو Wednesday أو Tuesday أو Monday إذا كانت تساوي قيمة الثابت
case Monday:
case Tuesday:
case Wednesday:
case Thursday:
cout << "We are available from 8:00 AM to 4:00 PM.";
break;
// سيتم تنفيذ أمر الطباعة التالي Friday إذا كانت تساوي قيمة الثابت
case Friday:
cout << "We are available from 8:00 AM to 12:00 PM.";
break;
// و هنا سيتم تنفيذ أمر الطباعة التالي Sunday أو Saturday إذا كانت لا تساوي أي قيمة سابقة فهذا يعني أنها تساوي
default:
cout << "We are not available on vacations.";
break;
}
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
We are available from 8:00 AM to 4:00 PM.
المفهوم الثالث
هنا وضعنا مثال يوضح كيفية تعريف دالة تأخذ باراميتر نوعه enum و كيفية تمرير قيمة لها عند استدعاءها.
المثال التالي يوضح كيفية تعريف دالة تأخذ باراميتر نوعه enum و كيفية تمرير قيمة لها عند استدعاءها.
في المثال التالي قمنا بتعريف enum إسمه Gender يمثل أنواع الجنس (ذكر, أنثى).
بعدها قمنا بتعريف دالة إسمها printInstructions() عند استدعاءها نمرر لها كائن من Gender فتقوم بالتشييك على قيمته و على أساسه تقوم بطباعة نص محدد.
في الأخير قمنا بإنشاء كائن من Gender و قمنا بتمريره للدالة printInstructions().
مثال
#include <iostream>
using namespace std;
// وضعنا فيه ثابتين Gender إسمه enum هنا قمنا بتعريف
enum Gender
{
Male,
Female
};
// Gender عند استدعاءها يجب أن نمرر لها قيمة عبارة عن كائن من printInstructions هنا قمنا بتعريف دالة إسمها
void printInstructions(Gender gender)
{
// سيتم تنفيذ أمر الطباعة الموضوع هنا Male تساوي gender إذا كانت القيمة التي مررناها لها في الكائن
if (gender == Male)
{
cout << "Males should go to right building.";
}
// سيتم تنفيذ أمر الطباعة الموضوع هنا Male لا تساوي gender إذا كانت القيمة التي مررناها لها في الكائن
else
{
cout << "Females should go to the left building.";
}
}
// main() هنا قمنا بتعريف الدالة
int main()
{
// فقط Gender و الهدف منه تخزين إحدى القيم الموجودة في selectedGender إسمه Gender هنا قمنا بإنشاء كائن من
Gender selectedGender;
// selectedGender في الكائن Gender الموجود بالأساس في Male هنا قمنا بتخزين قيمة الثابت
selectedGender = Male;
// و التي ستقوم بالتشييك على قيمته و من ثم طباعة جملة تناسب قيمته selectedGender و تمرير الكائن printInstructions() هنا قمنا باستدعاء الدالة
printInstructions(selectedGender);
return 0;
}
•سنحصل على النتيجة التالية عند التشغيل.
Males should go to right building.