مقابلة C++: 7 فخاخ تسقط فيها 90% من المرشحين

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

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

ابدأ الدورة

لماذا لا يزال C++ يخيف حتى كبار المطورين؟

\n

C++ هي لغة تمنحك تحكمًا كاملاً في الذاكرة، ولكن فقط حتى تنسى وضع فاصلة منقوطة أو تستدعي دالة افتراضية من المُنشئ عن طريق الخطأ. تشتهر مقابلات C++ بقسوتها: قد يُسأل المرء عن الحقول البتية، وعن بنية الجدول الافتراضي، وعن تفاصيل std::move. إذا كنت تستعد لمقابلة تقنية، فهذه المقالة هي قائمة التحقق الخاصة بك. سنستعرض أكثر المواضيع غدرًا التي ستظهر بشكل شبه مؤكد في المقابلة، وسنوضح كيفية الإجابة عليها لتجنب الفشل.

\n\n

في بوابة التعليم PythonLib، نناقش غالبًا كيفية الانتقال من Python إلى C++ والعكس. لكن اليوم هو C++ الخالص. لا حلول وسط.

\n\n

1. الدوال الافتراضية وجداول VTable: كيف تتجنب الفخ

\n

السؤال: "ماذا يحدث إذا استدعيت دالة افتراضية من مُنشئ الفئة الأساسية؟" هو سؤال كلاسيكي. 70% من المرشحين يجيبون بشكل خاطئ. دعنا نفهم الأمر.

\n\n

لماذا هذا خطير؟

\n

عند استدعاء دالة افتراضية في مُنشئ الفئة الأساسية، لم يتم إنشاء كائن الفئة المشتقة بعد. يشير الجدول الافتراضي (VTable) في هذه اللحظة إلى دوال الفئة الأساسية، وليس الفئة المشتقة. ونتيجة لذلك، لا يعمل تعدد الأشكال.

\n\n
#include <iostream>\n\nclass Base {\npublic:\n    Base() { callFoo(); }  // استدعاء في المُنشئ\n    virtual void foo() { std::cout << "Base::foo\\n"; }\n    void callFoo() { foo(); }\n};\n\nclass Derived : public Base {\npublic:\n    Derived() {}\n    virtual void foo() override { std::cout << "Derived::foo\\n"; }\n};\n\nint main() {\n    Derived d;  // سيُطبع: Base::foo، وليس Derived::foo\n    return 0;\n}
\n\n

الخلاصة: لا تستدعِ أبدًا الدوال الافتراضية في المُنشئات والمُدمّرات. إذا سألك المحاور، اشرح بثقة آلية VTable.

\n\n

2. المؤشرات الذكية: RAII وإدارة الذاكرة

\n

لا تخلو أي مقابلة C++ جادة من هذا الموضوع. الأسئلة حول std::unique_ptr و std::shared_ptr و std::weak_ptr هي الأساس. لكن غالبًا ما يُسأل عن المراجع الدائرية و std::enable_shared_from_this.

\n\n

كيف تتجنب تسرب الذاكرة مع shared_ptr؟

\n

تخيل أن لديك كائنين يشير كل منهما إلى الآخر عبر shared_ptr. لن يصبح عداد المراجع صفرًا أبدًا - ستتسرب الذاكرة. الحل: يجب أن يكون أحد المؤشرات من نوع weak_ptr.

\n\n
#include <memory>\n#include <iostream>\n\nstruct Node {\n    std::shared_ptr<Node> next;\n    std::weak_ptr<Node> prev;  // مرجع ضعيف لتجنب الدورة\n    ~Node() { std::cout << "Node destroyed\\n"; }\n};\n\nint main() {\n    auto a = std::make_shared<Node>();\n    auto b = std::make_shared<Node>();\n    a->next = b;\n    b->prev = a;  // weak_ptr لا يزيد العداد\n    // عند الخروج من النطاق، سيتم حذف كلا العقدتين بشكل صحيح\n    return 0;\n}
\n\n

في المقابلة، تأكد من ذكر مفهوم RAII (اكتساب الموارد هو التهيئة) - وهي فلسفة C++ التي تربط عمر المورد بعمر الكائن.

\n\n

3. دلالات النقل و std::move: سحر أم خدعة؟

\n

سؤال نموذجي: "ماذا يفعل std::move؟" الإجابة "ينقل الكائن" غير صحيحة. std::move فقط يحول النوع إلى مرجع rvalue. النقل الفعلي يحدث في مُنشئ النقل أو عامل الإسناد بالنقل.

\n\n

مثال للمقابلة

\n
#include <string>\n#include <vector>\n#include <iostream>\n\nclass MyString {\n    std::string data;\npublic:\n    MyString(const std::string& s) : data(s) {}\n    // مُنشئ النقل\n    MyString(MyString&& other) noexcept : data(std::move(other.data)) {\n        std::cout << "Move constructor c                

المدونات

توصيات الكتب