C++النطاقات ( Namespaces )
- مفهوم النطاق
- تضارب الأسماء
- الكلمة المفتاحية namespace
- الكلمة المفتاحية using
- وضع إسم مختصر للنطاق
مفهوم النطاق
النطاق ( Namespace ) أسلوب مفيد جداً في تنظيم كود المشروع نتبعه لضمان عدم حدوث أي مشكلة سببها إستخدام نفس أسماء الأشياء التي تم تعريفها أكثر من مرة, أي لتجنب حدوث تضارب في الأسماء.
إذا كنت أنت من يبني كل كود المشروع فإنك على الأغلب لن تواجه مشكلة سببها تضارب الأسماء لأنك أنت من تحدد أسماء الأشياء التي تقوم بتعريفها و بالتالي فإنك تعرف ما هي كل الأسماء التي وضعتها للمتغيرات و الدوال و الكلاسات في مشروعك. بينما حين تبدأ بتضمين مكتبات و ملفات فيها كود جاهز فهنا يصبح هناك احتمال كبير لحدوث تضارب بالأسماء.
لاستخدام أي أسماء تريد في مشروعك بدون القلق ما إن كان تضمين أي كود جاهز فيه قد يسبب مشكلة تضارب الأسماء يمكنك من البداية وضع الكود الخاص بك بداخل نطاق. عندها حتى إن كان كودك فيه نفس أسماء المتغيرات و الدوال و الكلاسات الموجودة في الكود الجاهز فإن هذا الأمر لن يسبب أي مشكلة بالنسبة للمترجم لأنه قادر على التفريق بينهم بكل سهولة.
تضارب الأسماء
تضارب الأسماء يعني محاولة تعريف نفس الشيء أكثر من مرة في نفس المكان.
فيما يلي ستلاحظ أننا تعمدنا وضع أمثلة فيها مشكلة تضارب بالأسماء حتى تعرف ما هو المقصود بهذه المشكلة و كيف سنقوم لاحقاً بتجنب حدوثها.
في المثال التالي قمنا حاولنا تعريف متغيرين إسمهما x في نفس الدالة مما سيؤدي لظهور مشكلة تضارب الأسماء عند التشغيل.
ملاحظة: الخطأ سيحدث عند محاولة إعادة تعريف المتغير للمرة الثانية في نفس المكان.
المثال الأول
سيظهر الخطأ التالي عند التشغيل و الذي يعني أنه لا يمكن إعادة تعريف المتغير x كما حاولنا أن نفعل في السطر رقم 6
.
في المثال التالي قمنا بإنشاء دالتين إسمهما printMsg() في نفس المكان مما سيؤدي لظهور مشكلة تضارب الأسماء عند التشغيل.
ملاحظة: الخطأ سيحدث عند محاولة إعادة تعريف الدالة للمرة الثانية في نفس المكان.
المثال الثاني
سيظهر الخطأ التالي عند التشغيل و الذي يعني أنه لا يمكن إعادة تعريف الدالة printMsg() كما حاولنا أن نفعل في السطر رقم 8
.
الكلمة المفتاحية namespace
تستخدم الكلمة namespace لتعريف نطاق جديد و وضع الكود فيه أو لتعريف إسم مختصر لنطاق موجود سابقاً.
وضع الكود بداخل نطاق يتيح لك إعادة استخدام نفس أسماء الأشياء المعرّفة سابقاً في نفس المكان.
الشكل العام لتعريف نطاق
- مكان الكلمة
namespace_name
تقوم بوضع الإسم الذي تريد وضعه للنطاق. - بين أقواس البداية و النهاية
{...}
يمكنك تعريف أي شيء تريده مثل متغيرات, دوال, كلاسات و حتى أنه يمكنك تعريف نطاقات داخلية.
الشكل العام لتعريف نطاق بداخل نطاق
يمكنك تعريف نطاق بداخل نطاق و هذا الأسلوب يسمى Nested Namespaces كالتالي.
- مكان الكلمة
outer_namespace_name
تقوم بوضع إسم النطاق الخارجي و بداخل حدوده يمكنك كتابة أي كود تريده. - مكان الكلمة
innner_namespace_name
تقوم بوضع إسم النطاق الداخلي و بداخل حدوده يمكنك كتابة أي كود تريده أيضاً.
في المثال التالي قمنا بتعريف متغير إسمه x
بشكل مباشر في الملف و بجانبه قمنا بتعريف نطاق إسمه harmash
يحتوي على متغير إسمه x
أيضاً.
المثال الأول
سنحصل على النتيجة التالية عند التشغيل.
x = 1 harmash::x = 2
في المثال التالي قمنا بتعريف دالة إسمه printMsg()
بشكل مباشر في الملف و بجانبها قمنا بتعريف نطاق إسمه harmash
يحتوي على دالة إسمها printMsg()
أيضاً.
المثال الثاني
سنحصل على النتيجة التالية عند التشغيل.
printMsg is called harmash::printMsg is called
في المثال التالي قمنا بتعريف نطاق إسمه outer
يحتوي على دالة إسمها printMsg()
و بداخله نطاق إسمه inner
يحتوي على دالة إسمها printMsg()
أيضاً.
المثال الثالث
سنحصل على النتيجة التالية عند التشغيل.
outer::printMsg is called inner::printMsg is called
الكلمة المفتاحية using
تستخدم الكلمة using تستخدم لتضمين كل الكود الموجود بداخل النطاق الذي نحدده بعدها دفعة واحدة مما يجعلنا قادرين على التعامل مع الأشياء الموجودة فيه بدون الحاجة لذكر إسم النطاق في كل مرة.
استخدام هذا الكلمة مفيد من ناحية أنه يقلل حجم الكود و لكن من ناحية أخرى يجبرك على تضمين كل الكود الموجود في النطاق و الذي قد لا تحتاجه أصلاً و قد يؤدي استخدام هذه الكلمة إلى حدوث مشكلة تضارب الأسماء أيضاً.
معلومة تقنية
في الدروس السابقة كنا نستخدم دائماً الكلمة using حين كنا نكتب using namespace std; بهدف استخدام الكود الموجود فيها بدون الحاجة لكتابة std:: في كل مرة, و لكن تذكر دائماً أنه يفضّل تجنب هذا الأمر لكي لا تقوم بتضمين أي كود لست بحاجة له و لضمان أن لا يحدث أي تضارب مع أسماء الأشياء التي تقوم بتعريفها.
الشكل العام لتضمين كل كود النطاق
- مكان الكلمة
namespace_name
تقوم بوضع إسم النطاق الذي تريد تضمين كل الكود الموجود فيه.
الشكل العام لتضمين كود نطاق موجود بداخل نطاق آخر
- مكان الكلمة
parent_namespace_name
تقوم بوضع إسم النطاق الخارجي. - مكان الكلمة
child_namespace_name
تقوم بوضع إسم النطاق الداخلي الذي تريد كل الكود الموجود فيه.
في المثال التالي قمنا بتعريف نطاق إسمه harmash
يحتوي على دالتين إسمهما func1() و func2().
بعدها قمنا بتضمين كل محتوى النطاق harmash
حتى نتمكن من الوصول إلى الأشياء الموجودة فيه بشكل مباشر, أي بدون الحاجة لكتابة harmash::
في كل مرة.
المثال الأول
سنحصل على النتيجة التالية عند التشغيل.
func1 is called func2 is called
المثال التالي يوضح المشكلة التي قد يسببها تضمين كل محتوى النطاق.
في المثال التالي قمنا بتعريف دالة إسمها printMsg() و نطاق إسمه harmash
يحتوي على دالة إسمها printMsg() أيضاً.
بعدها قمنا بتضمين كل محتوى النطاق harmash
و بالتالي أصبح هناك دالتين printMsg() في نفس المكان مما سيؤدي لمشكلة عند محاولة استدعاء الدالة بهذا الشكل printMsg(); سببها تضارب الأسماء عند التشغيل.
المثال الثاني
سيظهر الخطأ التالي عند التشغيل بسبب السطر رقم 25
لأن المترجم حائر و لا يعرف أي دالة printMsg() تريده أن ينفذ لك.
وضع إسم مختصر للنطاق
في حال كان إسم النطاق كبيراً يمكنك استخدام الكلمة namespace لوضع إسم مختصر له.
ما تفعله ببساطة هو تعريف نطاق جديد تعطيه إسم مختصر و تجعله يساوي النطاق الذي يملك إسم كبير.
الشكل العام لوضع إسم مختصر للنطاق
- مكان الكلمة
very_long_namespace_name
تضع إسم النطاق الذي تنوي وضع إسم مختصر له. - مكان الكلمة
alias_name
تضع الإسم المختصر الذي تريد وضعه للناطق.
في المثال التالي قمنا بتعريف نطاق إسمه harmashcom
يحتوي على دالة printMsg().
بعدها قمنا بتعريف إسم مختصر للنطاق harmashcom
إسمه hc
و من ثم قمنا باستدعاء الدالة من خلاله.
مثال
سنحصل على النتيجة التالية عند التشغيل.
printMsg is called