مقدمة: لماذا مراجعة الكود في C++ ليست لضعاف القلوب
\n\nC++ هي لغة تمنح المبرمج قوة لا تصدق. ولكن، كما قال العم بن من "الرجل العنكبوت": "مع القوة العظيمة تأتي مسؤولية عظيمة". وإذا كان بإمكانك في Python خلط int و str عن طريق الخطأ والحصول على استثناء فقط، فإن نفس الخطأ في C++ يمكن أن يؤدي إلى سلوك غير محدد (UB)، والذي قد يظهر بعد شهر على خادم العميل في الساعة 3 صباحًا.
مراجعة الكود في C++ ليست مجرد علامة اختيار في Jira. إنها درعك ضد أخطاء التجزئة (segmentation fault)، وتسرب الذاكرة، والكود البطيء كالسلحفاة. يعتقد العديد من المطورين (خاصة أولئك الذين أتوا من Java أو Python) أن مراجعة الكود هي مجرد التحقق من الأسلوب وأسماء المتغيرات. في C++، الأمر أعمق. هنا تحتاج إلى النظر في روح البرنامج: في إدارة الذاكرة، في RAII، في صحة الثبات (const-correctness)، وما إذا كان هذا المؤشر الذكي سيطلق النار على قدمك.
\n\nفي هذه المقالة، قمت بتجميع قائمة تحقق حقيقية لـ فحص كود C++. خذها كسلاح لك عندما تقوم بمراجعة الكود التالية. هيا بنا!
\n\n1. تسرب الذاكرة وإدارة الموارد (RAII)
\n\nهذا هو قدس الأقداس في C++. إذا رأيت new بدون delete - فهذه علامة حمراء. لكن في C++ الحديثة (C++11 وما بعده)، لا ينبغي لنا كتابة new يدويًا على الإطلاق. استخدم المؤشرات الذكية: std::unique_ptr، std::shared_ptr. إذا كان المطور يستخدم مؤشرات خام (int*) لامتلاك مورد - فهذا سبب لطرح سؤال.
مثال على كود سيء (علامة حمراء):
\n\nvoid process() {\n int* data = new int[1000];\n // ... بعض الكود ...\n if (error) {\n return; // تسرب! لم يتم استدعاء delete\n }\n delete[] data;\n}\n\n\nمثال على كود جيد (RAII):
\n\nvoid process() {\n std::vector<int> data(1000);\n // ... كود ...\n if (error) {\n return; // كل شيء بخير، المتجه سينظف الذاكرة بنفسه\n }\n}\n\n\nما يجب التحقق منه في مراجعة الكود في C++:
\n- \n
- هل يوجد
new/deleteفي الكود؟ إذا كان الأمر كذلك، لماذا؟ لماذا لا يتم استخدامstd::make_unique؟ \n - هل يتم استخدام
std::unique_ptrللملكية الحصرية؟ \n - هل يتم تمرير
std::shared_ptrبالقيمة حيث يكفي المرجع (هذا مكلف بسبب العداد الذري atomic counter)؟ \n - هل يوجد تسرب عند حدوث الاستثناءات (exception safety)؟ \n
2. السلوك غير المحدد (Undefined Behavior)
\n\nفي C++، يثق المترجم بالمبرمج. إذا كتبت هراءً، لن يوبخك المترجم - بل سيولد كودًا يمكنه فعل أي شيء. UB هو آفة C++. إليك ما يجب اكتشافه في المراجعة:
\n\n- \n
- تجاوز حدود المصفوفة:
arr[10]لمصفوفة من 10 عناصر. \n - استخدام متغير بعد نقله:
std::moveليس سحرًا، الكائن بعد النقل يكون في حالة صالحة ولكن غير محددة. \n - فيضان الأعداد الصحيحة الموقعة:
INT_MAX + 1هو UB! (على عكس Java، حيث هو مجرد التفاف). \n - مرجع معلق (Dangling reference): إرجاع مرجع لمتغير محلي. \n
مثال على UB (ابحث عن هذا في مراجعة الكود):
\n\nint& getValue() {\n int x = 42;\n return x; // UB! تم تدمير x بعد الخروج من الدالة\n}\n\nvoid example() {\n int& ref = getValue();\n std::cout << ref; // قد يطبع 42، وقد يطبع بيانات عشوائية أو يتعطل\n}\n\n\nنصيحة للمراجع: إذا رأيت عمليات حسابية مع int، خاصة في الحلقات أو عند العمل مع الأحجام - تحقق مما إذا كان سيكون هناك فيضان. من الأفضل استخدام size_t للفهارس و std::ssize() للمقارنة الآمنة.