كيف تجتاز مقابلة C++: أهم الأسئلة مع أمثلة الكود
تعتبر مقابلة C++ واحدة من أصعب الاختبارات للمطور. هذه اللغة تجمع بين قوة البرمجة منخفضة المستوى ونماذج OOP الحديثة والقوالب. لاجتياز المقابلة بنجاح، لا تحتاج فقط إلى معرفة بناء الجملة، بل أيضًا فهم كيفية عمل الذاكرة والمترجم والمكتبة القياسية. في هذه المقالة، سنستعرض أهم 10 أسئلة يتم طرحها غالبًا في مقابلات C++، مع إجابات مفصلة وأمثلة الكود.
1. ما هو RAII ولماذا هو مطلوب؟
RAII (Resource Acquisition Is Initialization) هي واحدة من التعابير الأساسية في C++. الفكرة بسيطة: يتم الاستيلاء على المورد (ذاكرة، ملف، mutex) في المُنشئ ويتم تحريره في المُدمّر. هذا يضمن التنظيف الصحيح حتى في حالة الاستثناءات.
مثال على كود غير صحيح (بدون RAII):
void process() { int* ptr = new int(42); // ... كود قد يرمي استثناء delete ptr; // تسرب إذا حدث استثناء قبل delete}مثال مع RAII (باستخدام std::unique_ptr):
#include <memory>
void process() { std::unique_ptr<int> ptr = std::make_unique<int>(42); // ... كود مع استثناءات // ptr سيتم حذفه تلقائيًا عند الخروج من نطاق الرؤية}لماذا يُطرح هذا السؤال: RAII هو أساس الإدارة الآمنة للموارد في C++. بدونه، من المستحيل كتابة كود موثوق.
2. ما الفرق بين الدالة الافتراضية والدالة الافتراضية البحتة؟
الدالة الافتراضية (virtual) يمكن إعادة تعريفها في الفئة المشتقة، ولكن لها تطبيق في الفئة الأساسية. الدالة الافتراضية البحتة (= 0) ليس لها تطبيق في الفئة الأساسية وتجعل الفئة مجردة — لا يمكن إنشاء كائن من هذه الفئة.
مثال:
class Base {public: virtual void foo() { std::cout << "Base foo"; } // افتراضية virtual void bar() = 0; // افتراضية بحتة};
class Derived : public Base {public: void foo() override { std::cout << "Derived foo"; } void bar() override { std::cout << "Derived bar"; }};سؤال إضافي: كيف يعمل vtable؟ يقوم المترجم بإنشاء جدول دوال افتراضية لكل فئة تحتوي على دوال افتراضية. كل كائن يحتوي على مؤشر لهذا الجدول (vptr). عند استدعاء دالة افتراضية، يتم التوزيع الديناميكي عبر vtable.
3. ما هي المؤشرات الذكية الموجودة في C++ ومتى يتم استخدامها؟
في C++11 ظهرت ثلاثة أنواع من المؤشرات الذكية:
- std::unique_ptr — ملكية فردية. لا يمكن نسخه، فقط نقله. مثالي للموارد ذات المالك الواحد.
- std::shared_ptr — ملكية مشتركة. يستخدم عداد المراجع. مناسب عندما تحتاج عدة كائنات إلى امتلاك مورد واحد.
- std::weak_ptr — لا يمتلك المورد، ولكن يمكنه مراقبة shared_ptr. مطلوب لكسر المراجع الدائرية.
مثال استخدام unique_ptr:
#include <memory>
class MyClass { /* ... */ };
void example() { auto ptr = std::make_unique<MyClass>(); // auto ptr2 = ptr; // خطأ! لا يمكن نسخه auto ptr3 = std::move(ptr); // صحيح، نقل}مثال shared_ptr و weak_ptr لمنع المراجع الدائرية:
struct Node { std::shared_ptr<Node> next; std::weak_ptr<Node> prev; // weak_ptr لتجنب الدائرة};4. ما هي دلالات النقل (move-semantics) و std::move؟
دلالات النقل تسمح بـ "نقل" الموارد من كائن إلى آخر بدلاً من النسخ. هذا مفيد بشكل خاص للكائنات المؤقتة أو عندما يكون النسخ مكلفًا (مثل std::string أو