مراجعة الكود في C++: ما الذي يجب التحقق منه حتى لا "ينفجر" الكود

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا مراجعة الكود في C++ ليست لضعاف القلوب

\n\n

C++ هي لغة تمنح المبرمج قوة لا تصدق. ولكن، كما قال العم بن من "الرجل العنكبوت": "مع القوة العظيمة تأتي مسؤولية عظيمة". وإذا كان بإمكانك في Python خلط int و str عن طريق الخطأ والحصول على استثناء فقط، فإن نفس الخطأ في C++ يمكن أن يؤدي إلى سلوك غير محدد (UB)، والذي قد يظهر بعد شهر على خادم العميل في الساعة 3 صباحًا.

\n\n

مراجعة الكود في C++ ليست مجرد علامة اختيار في Jira. إنها درعك ضد أخطاء التجزئة (segmentation fault)، وتسرب الذاكرة، والكود البطيء كالسلحفاة. يعتقد العديد من المطورين (خاصة أولئك الذين أتوا من Java أو Python) أن مراجعة الكود هي مجرد التحقق من الأسلوب وأسماء المتغيرات. في C++، الأمر أعمق. هنا تحتاج إلى النظر في روح البرنامج: في إدارة الذاكرة، في RAII، في صحة الثبات (const-correctness)، وما إذا كان هذا المؤشر الذكي سيطلق النار على قدمك.

\n\n

في هذه المقالة، قمت بتجميع قائمة تحقق حقيقية لـ فحص كود C++. خذها كسلاح لك عندما تقوم بمراجعة الكود التالية. هيا بنا!

\n\n

1. تسرب الذاكرة وإدارة الموارد (RAII)

\n\n

هذا هو قدس الأقداس في C++. إذا رأيت new بدون delete - فهذه علامة حمراء. لكن في C++ الحديثة (C++11 وما بعده)، لا ينبغي لنا كتابة new يدويًا على الإطلاق. استخدم المؤشرات الذكية: std::unique_ptr، std::shared_ptr. إذا كان المطور يستخدم مؤشرات خام (int*) لامتلاك مورد - فهذا سبب لطرح سؤال.

\n\n

مثال على كود سيء (علامة حمراء):

\n\n
void process() {\n    int* data = new int[1000];\n    // ... بعض الكود ...\n    if (error) {\n        return; // تسرب! لم يتم استدعاء delete\n    }\n    delete[] data;\n}\n
\n\n

مثال على كود جيد (RAII):

\n\n
void 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
\n\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
\n\n

مثال على UB (ابحث عن هذا في مراجعة الكود):

\n\n
int& 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() للمقارنة الآمنة.

\n\n

3. صحة الثبات (Const-correctness): اجعل كل شيء cons

المدونات

توصيات الكتب