C++الثوابت
- مفهوم الثوابت
- تعريف متغير ثابت
- تعريف مؤشر كثابت
- تعريف مؤشر لثابت
- تعريف باراميتر الدالة كثابت
- تعريف خصائص ثابتة في الكلاس
- تعريف الكائن كثابت
- الكلمة
mutable
- الكلمة
مفهوم الثوابت
الثابت ( Constant ) عبارة عن أي شيء يتم تعريفه بشكل لا يمكن إعادة تغيير قيمته.
لتعريف المتغير أو الكائن كثابت نستخدم الكلمة const
و عندها نصبح غير قادرين على تغيير قيمته.
الكلمة const
يمكن استخدامها مع الأشياء التالية:
- المتغيرات (Variables).
- المؤشرات (Pointers).
- باراميترات لدوال (Functions Parameters).
- خصائص الكلاس (Class Member Variables).
- دوال الكلاس (Class Member Functions).
- الكائنات (Objects).
تعريف متغير ثابت
إذا قمت بتعريف متغير ثابت فلا يمكنك تغيير قيمته لاحقاً و لا بد لك من إعطاؤه قيمة مباشرةً عند تعريفه.
القيمة الموضوعة بداخل المتغير يمكن الحصول عليها بشكل عادي مثل القيمة التي يتم وضعها بداخل متغير عادي و لكن لا يمكن تغييرها فقط.
في المثال التالي قمنا بتعريف متغير ثابت إسمه x
قيمته 10
و من ثم حاولنا إعادة تغيير قيمته.
المثال الأول
using namespace std; int main() { // قيمته تساوي 10 x هنا قمنا بتعريف متغير ثابت إسمه const int x = 10; // x هنا قمنا بطباعة قيمة المتغير cout << "x = " << x; return 0; }
سنحصل على النتيجة التالية عند التشغيل.
x = 10
في المثال التالي قمنا بتعريف متغير ثابت إسمه x
قيمته 10
و من ثم حاولنا إعادة تغيير قيمته.
المثال الثاني
using namespace std; int main() { const int x = 10; // قيمته تساوي 10 x هنا قمنا بتعريف متغير ثابت إسمه x = 5; // الأمر الذي سيؤدي لحدوث خطأ x هنا حاولنا تغيير قيمة المتغير return 0; }
سيظهر الخطأ التالي عند التشغيل و الذي يعني أنه لا يمكن تغيير القيمة الموجودة في المتغير x
.
تعريف مؤشر كثابت
المؤشر العادي يمكن في أي وقت جعله يشير لأي شيء موجود في الذاكرة كما سبق و شاهدنا.
تعريف المؤشر نفسه كثابت يجعل المؤشر مخصص للشيء الذي تم جعله يشير إليه عند تعريفه مع عدم القدرة على جعله يشير لشيء آخر لاحقاً.
في المثال التالي قمنا بتعريف متغير إسمه x
قيمته 5
و متغير إسمه y
قيمته 7
.
بعدها قمنا بإنشاء مؤشر ثابت إسمه ptr
يشير لقيمة المتغير x
و من ثم حاولنا جعله يشير لقيمة المتغير y
مما سيؤدي لحدوث مشكلة عند التشغيل.
مثال
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
.
تعريف مؤشر لثابت
إذا كنت تملك متغير ثابت يمكنك إنشاء مؤشر له و لكن عندها يجب أن تنشئ المؤشر كثابت أيضاً مما يجعلك غير قادر على جعله يشير لشيء آخر لاحقاً.
في المثال التالي قمنا بتعريف متغير ثابت إسمه x
قيمته 10
و مؤشر ثابت إسمه ptr
يشير لقيمته.
في الأخير قمنا بعرض القيمة التي يشير إليها المؤشر ptr
في الذاكرة.
مثال
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;
تعريف باراميتر الدالة كثابت
في حال كنت تنوي تمرير قيمة متغير ثابت لدالة و تريد أن يتم معاملة القيمة التي يتم تمريرها كأنها قيمة ثابتة أيضاً, يجب أن تعرف نوع البارميتر في الدالة كثابت.
في المثال التالي قمنا بتعريف دالة إسمها addOne
عند استدعاءها نمرر لها عدد فتقوم بإضافة 1
عليه و ترجع قيمته.
المثال الأول
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
في المثال التالي قمنا بإعادة المثال السابق و لكننا هذه المرة جعلنا القيمة التي يتم تمريرها للدالة يتم إعتبارها قيمة ثابتة مما سيؤدي لحدوث خطأ عند التشغيل.
ملاحظة: قمنا بتعليم السطر الذي أجرينا عليه التعديل باللون الأصفر.
المثال الثاني
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
.
أمثلة إضافية على التعامل مع القيم الثابتة التي يتم تمريرها للدوال.
تعريف خصائص ثابتة في الكلاس
إذا أردت تعريف خصائص ثابتة في الكلاس, بمعنى أنه لا يمكن تغيير القيم التي نعطيهم إياها لاحقاً, فلا بد لك من تمرير قيم لكل خاصية ثابتة قمت بتعريفها في الكائن لحظة إنشاء الكائن و نفعل ذلك من خلال كونستركتور الكائن كما سنرى الآن.
في المثال التالي قمنا بتعريف كلاس إسمه Player
يحتوي على متغير ثابت إسمه name
بالإضافة إلى كونستركتور يسمح لنا بتمرير قيمة للثابت name
.
مثال
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) { }
تعريف الكائن كثابت
إذا أردت إنشاء كائن ثابت بمعنى أنه لا يمكن تغيير قيمة أي خاصية موجودة فيه فيمكنك إنشاء الكائن ككائن ثابت.
عند إنشاء كائن ثابت من كلاس لا يمكنك استدعاء أي دالة في الكلاس تتعامل مع أحد الخصائص الموجودة فيه إلا إذا قمت بجعل الدالة من الأساس ثابتة أيضاً.
في المثال التالي قمنا بتعريف كلاس إسمه Player
يحتوي على متغيرين إسمهما name
و age
كخصائص عادية.
كما أننا قمنا بتعريف كونستركتور يسمح لنا بتمرير قيم للخصائص name
و age
و دالة ثابتة يمكن استخدامها لعرض قيمهما بشكل مرتب.
مثال
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
في حال كنت تنوي إنشاء كائن ثابت من الكلاس و لكنك تريد جعل بعض الخصائص الموجودة فيه غير ثابتة بمعنى أنه يمكن تغيير قيمها لاحقاً بعد إنشاء الكائن, يجب أن تعرف الخاصية التي تريد جعل قيمتها غير ثابتة mutable
حتى يفهم المترجم أنك تريد ذلك.
في المثال التالي قمنا بتعريف كلاس إسمه Player
يحتوي على ثلاث متغيرات إسمهم name
و age
و job
كخصائص في الكلاس.
الخاصية job
جعلنا نوعها mutable
حتى نستطيع تغيير قيمتها متى أردنا من الكائن الذي ننشئه من الكلاس حتى و إن قمنا بإنشائه ككائن ثابت.
كما أننا قمنا بتعريف كونستركتور يسمح لنا بتمرير قيم للخصائص name
و age
و دالة ثابتة يمكن استخدامها لعرض قيم كل الخصائص بشكل مرتب.
مثال
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
الكلمة
الكلمة يمكن أن تستخدم لتعريف متغير قيمته ثابتة مثل الكلمة
const
و لكن بأسلوب مختلف.
في المثال التالي قمنا بتعريف متغير ثابت إسمها MAX_VALUE
قيمته تساوي 1000
.
مثال
using namespace std; int main() { // MAX_VALUE قيمته تساوي قيمة الثابت x هنا قمنا بتعريف متغير إسمه int x = MAX_VALUE; // x هنا قمنا بطباعة قيمة المتغير cout << "x = " << x; return 0; }
سنحصل على النتيجة التالية عند التشغيل.
x = 1000
الكلمة قد تسبب مشاكل غير متوقعة في البرنامج لأن نوع القيمة التي يتم وضعها فيها ليس محدداً, لذلك يفضّل تعريف المتغير الثابت دائماً بواسطة الكلمة
const
بدلاً من الكلمة .