Menu
flag Ar iconالعربيةdown icon

حلقة for المعتمدة على النطاق في C++: الصياغة وauto والمراجع

شرح حلقة for المعتمدة على النطاق في C++ - تكرار نظيف على المصفوفات وvector وstring وmap، ولماذا ينبغي استخدام auto& وconst auto&، ومزالق النسخ وإبطال المُكرِّرات التي يجب تجنبها.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

"لكل عنصر، افعل هذا"

حلقة for العدّية التقليدية رائعة عندما يكون لديك فهرس تقوده. لكنك في معظم الأحيان لا تهتم فعلاً بالفهرس، بل تريد فقط لمس كل عنصر في حاوية. كتابة for (int i = 0; i < v.size(); i++) لهذا الغرض مزعجة، وi على بُعد خطأ واحد بمقدار واحد من القراءة خارج الحدود.

أضافت C++11 حلقة for المعتمدة على النطاق لهذا الغرض تحديداً. تُسمّي متغيراً، وتشير إلى حاوية، فتمر الحلقة على كل عنصر نيابة عنك:

لا فهرس، ولا .size()، ولا حدود يمكن الخطأ فيها. اقرأها كـ "لكل s في scores." تعمل على المصفوفات الخام وstd::vector وstd::string وstd::map وأي شيء آخر يوفّر begin() وend().

دع auto يختار النوع

كتابة نوع العنصر صراحةً تنجح لكنها هشّة، فإذا غيّرت نوع الحاوية وجب تغيير كل حلقة أيضاً. اقرن حلقة for المعتمدة على النطاق بـ auto ليستنتج المترجم نوع العنصر نيابة عنك:

غير أن هناك تكلفة خفية هنا. auto name المجرد يستنتج string وينسخ كل عنصر إلى name في كل مرور. بالنسبة لـ int يكون ذلك مجانياً، أما بالنسبة لـ string أو بنية كبيرة فهو تخصيص مهدور في كل تكرار. الحل هو المراجع، وهو الأمر التالي الذي ينبغي فهمه.

التعديل في المكان باستخدام auto&

إذا كتبت auto x فستحصل على نسخة، لذا فإن الإسناد إلى x يغيّر النسخة لا الحاوية. انتبه إلى هذا المزلق:

عملية الضرب لا تفعل شيئاً بصمت لأن n نسخة قابلة للإهمال. ولتعديل العناصر فعلاً، خذها بالمرجع عبر auto&:

الرمز & الوحيد هو كل الفرق بين "انظر ولا تلمس" و"عدّل في المكان." إذا تساءلت يوماً عن سبب اختفاء تغييراتك، فهذا هو السبب في الغالب الأعم.

القراءة دون نسخ: const auto&

عندما تحتاج فقط إلى قراءة العناصر لكن نسخها مُكلف، استخدم const auto&. المرجع يتجنّب النسخة، وconst يوثّق (ويفرض) أنك لن تعدّل أي شيء:

قاعدة عملية جيدة:

for (auto x : c)         // نسخة - الأنواع الرخيصة (int, char, المؤشرات)
for (auto& x : c)        // تعديل - تريد تغيير العناصر
for (const auto& x : c)  // قراءة - أنواع ثقيلة تكتفي بفحصها

اجعل const auto& الخيار الافتراضي عند القراءة وauto& عند الكتابة. ولا تلجأ إلى auto المجرد إلا للأنواع الصغيرة فعلاً والرخيصة في النسخ.

التكرار على map وpair

حلقة for معتمدة على النطاق على std::map تسلّمك std::pair لكل مدخلة، مع .first (المفتاح) و.second (القيمة). ومنذ C++17، تتيح لك structured bindings تفكيك ذلك الـ pair إلى متغيرين مُسمّيين مباشرةً في ترويسة الحلقة:

[name, age] أوضح بكثير من تكرار entry.first وentry.second في كل مكان. وحافظ على const auto& هنا أيضاً، فمفتاح مدخلة map هو string، لذا فإن نسخ كل pair سيكون إهداراً.

المزلق: لا تُغيّر الحجم أثناء التكرار

أكبر مزلق هو تغيير حجم الحاوية بينما تمرّ عليها حلقة for معتمدة على النطاق. استدعاء push_back أو erase أو insert أو clear قد يُعيد تخصيص التخزين الأساسي ويُبطل المُكرِّرات الداخلية للحلقة، والنتيجة سلوك غير مُعرَّف، أي تعطّل أو بيانات تالفة، لا خطأ ودود:

vector<int> v = {1, 2, 3};
for (int x : v) {
    v.push_back(x);   // سلوك غير مُعرَّف - إعادة التخصيص تُبطل النطاق
}

إذا احتجت إلى إضافة عناصر أو إزالتها أثناء المعالجة، فانتقل إلى حلقة for معتمدة على فهرس أو على مُكرِّر وأدِر الحدود بنفسك، أو ابنِ حاوية نتائج منفصلة واستبدلها لاحقاً. ومزلقان أصغر من العائلة نفسها: لا تربط أبداً حلقة for معتمدة على النطاق بكائن مؤقت يفنى فوراً (for (auto x : makeVector()) لا بأس به، لكن for (auto& x : someObj.getTempVector()) قد يصبح مرجعاً معلّقاً)، وتذكّر أن for (auto& c : myString) يتيح لك تعديل الأحرف الفردية في مكانها.

التالي: الدوال

حلقة for المعتمدة على النطاق ترتّب التكرار، وخيارات auto / auto& / const auto& التي تعلّمتها للتو تنتقل مباشرةً إلى واحدة من أهم الأدوات في C++. سنغلّف بعد ذلك المنطق في دوال قابلة لإعادة الاستخدام، بمنح الشيفرة اسماً ومعاملات وقيمة إرجاع لتستطيع استدعاءها من أي مكان بدلاً من تكرار نفسك.

الأسئلة الشائعة

ما هي حلقة for المعتمدة على النطاق في C++؟

حلقة for المعتمدة على النطاق تمر على كل عنصر في حاوية (مصفوفة أو vector أو string أو map وغيرها) دون أن تضطر إلى إدارة فهرس أو مُكرِّر. الصياغة هي for (auto x : container) { ... }. أُضيفت في C++11 وهي أنظف طريقة للقول "افعل هذا لكل عنصر".

متى ينبغي استخدام auto& بدلاً من auto في حلقة for المعتمدة على النطاق؟

استخدم auto& x عندما تريد تعديل العناصر في مكانها، وconst auto& x عندما تكتفي بقراءتها لكنك تريد تجنّب النسخ (وهذا مهم لـ string أو vector أو الكائنات الكبيرة). أما auto x المجرد فينشئ نسخة في كل تكرار، وهو مقبول للأنواع الرخيصة مثل int لكنه إهدار في غير ذلك.

هل يمكن تغيير حجم vector داخل حلقة for المعتمدة على النطاق في C++؟

لا. استدعاء push_back أو erase أو insert أو clear على الحاوية التي تكررها يُبطل المُكرِّرات الداخلية للحلقة وهو سلوك غير مُعرَّف، فقد يتعطّل البرنامج أو يُفسد البيانات بصمت. إذا احتجت إلى إضافة عناصر أو إزالتها أثناء التكرار، فاستخدم بدلاً من ذلك حلقة for معتمدة على فهرس أو على مُكرِّر.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن