مقدمة في معالجة الأخطاء في C++
تعد معالجة الأخطاء إحدى المهام الأساسية عند تطوير برمجيات موثوقة بلغة C++. على عكس بعض اللغات (مثل Python أو Java) حيث الاستثناءات هي الآلية القياسية الوحيدة، توفر C++ للمطور عدة طرق. يعتمد استقرار الكود وأداؤه وقابليته للقراءة على اختيار الاستراتيجية الصحيحة. في هذه المقالة، سنشرح البنية التقليدية try/catch، والإمكانيات الحديثة لمعيار C++17/20، بالإضافة إلى الطرق البديلة لمعالجة الأخطاء.
1. الطريقة التقليدية: بنية try/catch
الآلية الأساسية لمعالجة الاستثناءات في C++ تُبنى على ثلاث كلمات مفتاحية: try و catch و throw. الاستثناء هو كائن من أي نوع يتم "رميه" عند حدوث خطأ و"التقاطه" في الكتلة المناسبة.
#include <iostream>#include <stdexcept>
double divide(double a, double b) { if (b == 0) { throw std::runtime_error("القسمة على صفر!"); } return a / b;}
int main() { try { double result = divide(10, 0); std::cout << "النتيجة: " << result << std::endl; } catch (const std::runtime_error& e) { std::cerr << "خطأ: " << e.what() << std::endl; } catch (...) { std::cerr << "خطأ غير معروف!" << std::endl; } return 0;}نقاط مهمة:
- التسلسل الهرمي للاستثناءات: يُوصى برمي كائنات من فئات موروثة من
std::exception(مثلstd::runtime_errorوstd::logic_error). - الالتقاط بالمرجع: التقط الاستثناءات دائمًا بمرجع ثابت (
const std::exception&) لتجنب القص والنسخ الزائد. - المعالج العام:
catch (...)يلتقط أي استثناءات لكنه لا يوفر الوصول إلى كائن الخطأ. استخدمه فقط كحل أخير.
2. الطرق البديلة: أكواد الإرجاع و std::optional
ليست الاستثناءات دائمًا الخيار الأفضل. في الأنظمة عالية التحميل أو البرمجة المضمنة، غالبًا ما تُفضل الآليات الأخف.
2.1. أكواد الإرجاع (error codes)
تُرجع الدالة عددًا صحيحًا أو تعدادًا يشير إلى النجاح أو نوع الخطأ. هذه الطريقة بسيطة وفعالة، لكنها تتطلب فحصًا صريحًا للنتيجة.
enum class ErrorCode { Success, DivisionByZero, Overflow };
ErrorCode safe_divide(double a, double b, double& result) { if (b == 0.0) return ErrorCode::DivisionByZero; result = a / b; return ErrorCode::Success;}
int main() { double res; ErrorCode err = safe_divide(10, 0, res); if (err != ErrorCode::Success) { // معالجة الخطأ }}2.2. std::optional (C++17)
حاوية يمكنها احتواء قيمة أو أن تكون فارغة. مثالية للحالات التي يكون فيها الخطأ هو غياب النتيجة.
#include <optional>
std::optional<double> divide_opt(double a, double b) { if (b == 0.0) return std::nullopt; return a / b;}
int main() { auto result = divide_opt(10, 2); if (result) { std::cout << *result << std::endl; } else { std::cout << "خطأ في القسمة" << std::endl; }}3. الإمكانيات الحديثة: noexcept و std::expected (C++23)
يستمر معيار C++ في التطور، مقدمًا وسائل أكثر تعبيرًا للعمل مع الأخطاء.
3.1. مواصفات noexcept
الكلمة المفتاحية noexcept تشير إلى أن الدالة لا ترمي استثناءات. يساعد ذلك المترجم على تحسين الكود ويجعل الواجهة أكثر وضوحًا.
void l