حجم صفيف ثابت مضاد

تم تحضير ترجمة المقال استعدادًا لبدء الدورة التدريبية "C ++ Developer. محترف " .








أريد أن ألفت انتباهك إلى النمط المضاد ، والذي غالبًا ما أراه في كود الطلاب في Code Review StackExchange وحتى في الكثير من المواد التعليمية (!) لأشخاص آخرين. لديهم مجموعة من ، على سبيل المثال ، 5 عناصر ؛ وبعد ذلك ، نظرًا لأن الأرقام السحرية سيئة ، فإنها تقدم ثابتًا مُسمىًا لتمثيل عدد العناصر الأساسية "5".



void example()
{
    constexpr int myArraySize = 5;
    int myArray[myArraySize] = {2, 7, 1, 8, 2};
    ...




لكن الحل لا بأس به! في الكود أعلاه ، يتكرر الرقم خمسة : أولاً في القيمة myArraySize = 5، ثم مرة أخرى عندما تقوم بتعيين العناصر بالفعل myArray. الكود أعلاه فظيع من وجهة نظر الصيانة مثل:



constexpr int messageLength = 45;
const char message[messageLength] =
    "Invalid input. Please enter a valid number.\n";




- وهذا بالطبع لن يكتبه أحد منا.



الكود المتكرر ليس جيدًا



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



   constexpr int myArraySize = 5;
-   int myArray[myArraySize] = {2, 7, 1, 8, 2};
+   int myArray[myArraySize] = {3, 1, 4};




يبدو أن التصحيح أعلاه يغير محتويات المصفوفة من 2،7،1،8،2 إلى 3،1،4 ، لكنها ليست كذلك! في الواقع ، قام بتغييره إلى 3،1،4،0،0 - مع حشوة الأصفار - لأن المشرف نسي التعديل myArraySizeوفقًا لـ myArray.



نهج موثوق



بالنسبة للعد ، فإن أجهزة الكمبيوتر جيدة جدًا في ذلك. لذا دع الكمبيوتر يعد!



int myArray[] = {2, 7, 1, 8, 2};
constexpr int myArraySize = std::size(myArray);




يمكنك الآن تغيير محتويات المصفوفة ، لنقل من 2،7،1،8،2 إلى 3،1،4 ، عن طريق تغيير سطر واحد فقط من التعليمات البرمجية. لا تحتاج إلى تكرار التغيير في أي مكان.



أكثر من ذلك ، myArrayعادةً ما يستخدم الكود الحقيقي حلقات forو / أو خوارزميات بناءً على نطاق التكرار لمعالجته ، لذلك لا يحتاج إلى متغير مسمى على الإطلاق لتخزين حجم المصفوفة.



for (int elt : myArray) {
    use(elt);
}
std::sort(myArray.begin(), myArray.end());
std::ranges::sort(myArray);

// Warning: Unused variable 'myArraySize'




myArraySizeدائمًا ما يتم استخدام الإصدار "السيئ" من هذا الرمز (في الإعلان myArray) ، وبالتالي من غير المحتمل أن يرى المبرمج أنه يمكن استبعادها. في الإصدار "الجيد" ، من السهل على المترجم أن يكتشف ما myArraySizeلا يتم استخدامه.



كيف تفعل هذا std::array؟



أحيانًا يتخذ المبرمج خطوة أخرى نحو الجانب المظلم ويكتب:



constexpr int myArraySize = 5;
std::array<int, myArraySize> myArray = {2, 7, 1, 8, 2};




يجب إعادة كتابة هذا على الأقل:



std::array<int, 5> myArray = {2, 7, 1, 8, 2};
constexpr int myArraySize = myArray.size();  //  std::size(myArray)




ومع ذلك ، لا توجد طريقة سهلة للتخلص من العد اليدوي في السطر الأول. يتيح لك CTAD C ++ 17 الكتابة



std::array myArray = {2, 7, 1, 8, 2};




لكن هذا لا يعمل إلا إذا كنت بحاجة إلى مصفوفة int- لن تعمل إذا كنت بحاجة إلى مصفوفة short، على سبيل المثال ، أو مصفوفة uint32_t.



يعطينا C ++ 20 الأمراض المنقولة جنسياً :: to_array ، مما يسمح لنا بالكتابة



auto myArray = std::to_array<int>({2, 7, 1, 8, 2});
constexpr int myArraySize = myArray.size();




لاحظ أن هذا ينشئ مصفوفة C ثم ينقل (يبني النقل) عناصرها فيه std::array. تمت تهيئة جميع الأمثلة السابقة الخاصة بنا myArrayبقائمة مُهيئ متعرجة أدت إلى بدء التهيئة المجمعة وإنشاء مثيل لعناصر الصفيف في مكانها.



على أي حال ، تؤدي كل هذه الخيارات إلى عدد كبير من مثيلات القالب الإضافية مقارنة بمصفوفات C القديمة الجيدة (التي لا تتطلب إنشاء مثيل للقالب). لذلك ، أنا أفضل بشدة T[]الإصدار الأحدث std::array<T, N>.



في C ++ 11 و C ++ 14 ، std::arrayكانت هناك ميزة مريحة في القدرة على القول arr.size()؛ لكن هذه الميزة تبخرت عندما زودتنا C ++ 17std::size(arr)وللصفائف المضمنة. لا std::arrayتوجد المزيد من المزايا المريحة. استخدمه إذا كنت بحاجة إلى دلالات متغير الكائن بالكامل (مرر المصفوفة بأكملها إلى دالة! قم بإرجاع مصفوفة من دالة! عيّن المصفوفات بـ =! قارن المصفوفات بـ ==!) ، لكن بخلاف ذلك أوصي بتجنب استخدام std::array.



وبالمثل ، أوصي بتجنبه std::listإلا إذا كنت تريد استقرار مكرره ، ولصق سريع ، وفرز دون استبدال العناصر ، وما إلى ذلك. لا أقول أنه لا يوجد مكان لهذه الأنواع في C ++ ؛ أنا أقول فقط أن لديهم "مجموعة محددة جدًا من المهارات" ، وإذا لم تستخدم هذه المهارات ، فمن المحتمل أنك تدفع مبالغ زائدة.




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





اقرأ أكثر:






All Articles