Programming Basics SQL HTML CSS JavaScript React Python C++ Java JavaFX Swing Problem Solving English English Conversations Computer Fundamentals Linux Learn Typing

C++الثوابت

  • مفهوم الثوابت
  • تعريف متغير ثابت
  • تعريف مؤشر كثابت
  • تعريف مؤشر لثابت
  • تعريف باراميتر الدالة كثابت
  • تعريف خصائص ثابتة في الكلاس
  • تعريف الكائن كثابت
  • الكلمة mutable
  • الكلمة #define

مفهوم الثوابت

الثابت ( Constant ) عبارة عن أي شيء يتم تعريفه بشكل لا يمكن إعادة تغيير قيمته.
لتعريف المتغير أو الكائن كثابت نستخدم الكلمة const و عندها نصبح غير قادرين على تغيير قيمته.

الكلمة const يمكن استخدامها مع الأشياء التالية:

  • المتغيرات (Variables).
  • المؤشرات (Pointers).
  • باراميترات لدوال (Functions Parameters).
  • خصائص الكلاس (Class Member Variables).
  • دوال الكلاس (Class Member Functions).
  • الكائنات (Objects).

تعريف متغير ثابت

إذا قمت بتعريف متغير ثابت فلا يمكنك تغيير قيمته لاحقاً و لا بد لك من إعطاؤه قيمة مباشرةً عند تعريفه.
القيمة الموضوعة بداخل المتغير يمكن الحصول عليها بشكل عادي مثل القيمة التي يتم وضعها بداخل متغير عادي و لكن لا يمكن تغييرها فقط.


في المثال التالي قمنا بتعريف متغير ثابت إسمه x قيمته 10 و من ثم حاولنا إعادة تغيير قيمته.

المثال الأول

main.cpp
#include <iostream>

using namespace std;

int main()
{
    // قيمته تساوي 10 x هنا قمنا بتعريف متغير ثابت إسمه
    const int x = 10;
    
    // x هنا قمنا بطباعة قيمة المتغير
    cout << "x = " << x;
    
    return 0;
}

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

x = 10


في المثال التالي قمنا بتعريف متغير ثابت إسمه x قيمته 10 و من ثم حاولنا إعادة تغيير قيمته.

المثال الثاني

main.cpp
#include <iostream>

using namespace std;

int main()
{
    const int x = 10;    // قيمته تساوي 10 x هنا قمنا بتعريف متغير ثابت إسمه
    
    x = 5;               // الأمر الذي سيؤدي لحدوث خطأ x هنا حاولنا تغيير قيمة المتغير
    
    return 0;
}

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

error: assignment of read-only variable 'x'

تعريف مؤشر كثابت

المؤشر العادي يمكن في أي وقت جعله يشير لأي شيء موجود في الذاكرة كما سبق و شاهدنا.
تعريف المؤشر نفسه كثابت يجعل المؤشر مخصص للشيء الذي تم جعله يشير إليه عند تعريفه مع عدم القدرة على جعله يشير لشيء آخر لاحقاً.


في المثال التالي قمنا بتعريف متغير إسمه x قيمته 5 و متغير إسمه y قيمته 7.
بعدها قمنا بإنشاء مؤشر ثابت إسمه ptr يشير لقيمة المتغير x و من ثم حاولنا جعله يشير لقيمة المتغير y مما سيؤدي لحدوث مشكلة عند التشغيل.

مثال

main.cpp
#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.

error: assignment of read-only variable 'ptr'

تعريف مؤشر لثابت

إذا كنت تملك متغير ثابت يمكنك إنشاء مؤشر له و لكن عندها يجب أن تنشئ المؤشر كثابت أيضاً مما يجعلك غير قادر على جعله يشير لشيء آخر لاحقاً.


في المثال التالي قمنا بتعريف متغير ثابت إسمه x قيمته 10 و مؤشر ثابت إسمه ptr يشير لقيمته.
في الأخير قمنا بعرض القيمة التي يشير إليها المؤشر ptr في الذاكرة.

مثال

main.cpp
#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;

تعريف باراميتر الدالة كثابت

في حال كنت تنوي تمرير قيمة متغير ثابت لدالة و تريد أن يتم معاملة القيمة التي يتم تمريرها كأنها قيمة ثابتة أيضاً, يجب أن تعرف نوع البارميتر في الدالة كثابت.


في المثال التالي قمنا بتعريف دالة إسمها addOne عند استدعاءها نمرر لها عدد فتقوم بإضافة 1 عليه و ترجع قيمته.

المثال الأول

main.cpp
#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


في المثال التالي قمنا بإعادة المثال السابق و لكننا هذه المرة جعلنا القيمة التي يتم تمريرها للدالة يتم إعتبارها قيمة ثابتة مما سيؤدي لحدوث خطأ عند التشغيل.
ملاحظة: قمنا بتعليم السطر الذي أجرينا عليه التعديل باللون الأصفر.

المثال الثاني

main.cpp
#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.

error: increment of read-only parameter 'n'


أمثلة إضافية على التعامل مع القيم الثابتة التي يتم تمريرها للدوال.

شاهد الأمثلة

تعريف خصائص ثابتة في الكلاس

إذا أردت تعريف خصائص ثابتة في الكلاس, بمعنى أنه لا يمكن تغيير القيم التي نعطيهم إياها لاحقاً, فلا بد لك من تمرير قيم لكل خاصية ثابتة قمت بتعريفها في الكائن لحظة إنشاء الكائن و نفعل ذلك من خلال كونستركتور الكائن كما سنرى الآن.


في المثال التالي قمنا بتعريف كلاس إسمه Player يحتوي على متغير ثابت إسمه name بالإضافة إلى كونستركتور يسمح لنا بتمرير قيمة للثابت name.

مثال

main.cpp
#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)
{

}

تعريف الكائن كثابت

إذا أردت إنشاء كائن ثابت بمعنى أنه لا يمكن تغيير قيمة أي خاصية موجودة فيه فيمكنك إنشاء الكائن ككائن ثابت.
عند إنشاء كائن ثابت من كلاس لا يمكنك استدعاء أي دالة في الكلاس تتعامل مع أحد الخصائص الموجودة فيه إلا إذا قمت بجعل الدالة من الأساس ثابتة أيضاً.


في المثال التالي قمنا بتعريف كلاس إسمه Player يحتوي على متغيرين إسمهما name و age كخصائص عادية.
كما أننا قمنا بتعريف كونستركتور يسمح لنا بتمرير قيم للخصائص name و age و دالة ثابتة يمكن استخدامها لعرض قيمهما بشكل مرتب.

مثال

main.cpp
#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

في حال كنت تنوي إنشاء كائن ثابت من الكلاس و لكنك تريد جعل بعض الخصائص الموجودة فيه غير ثابتة بمعنى أنه يمكن تغيير قيمها لاحقاً بعد إنشاء الكائن, يجب أن تعرف الخاصية التي تريد جعل قيمتها غير ثابتة mutable حتى يفهم المترجم أنك تريد ذلك.


في المثال التالي قمنا بتعريف كلاس إسمه Player يحتوي على ثلاث متغيرات إسمهم name و age و job كخصائص في الكلاس.
الخاصية job جعلنا نوعها mutable حتى نستطيع تغيير قيمتها متى أردنا من الكائن الذي ننشئه من الكلاس حتى و إن قمنا بإنشائه ككائن ثابت.
كما أننا قمنا بتعريف كونستركتور يسمح لنا بتمرير قيم للخصائص name و age و دالة ثابتة يمكن استخدامها لعرض قيم كل الخصائص بشكل مرتب.

مثال

main.cpp
#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

الكلمة #define يمكن أن تستخدم لتعريف متغير قيمته ثابتة مثل الكلمة const و لكن بأسلوب مختلف.


في المثال التالي قمنا بتعريف متغير ثابت إسمها MAX_VALUE قيمته تساوي 1000.

مثال

main.cpp
#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.