سؤال

ما هي قواعد C++ لاستدعاء مُنشئ الطبقة الفائقة من فئة فرعية؟

على سبيل المثال، أعلم أنه في Java، يجب عليك القيام بذلك كسطر أول من مُنشئ الفئة الفرعية (وإذا لم تقم بذلك، فمن المفترض إجراء مكالمة ضمنية لمنشئ فائق no-arg - مما يمنحك خطأ في الترجمة إذا كان ذلك مفقودًا) .

هل كانت مفيدة؟

المحلول

يتم استدعاء منشئي الفئة الأساسية تلقائيًا إذا لم يكن لديهم وسيطة.إذا كنت تريد استدعاء مُنشئ فئة فائقة باستخدام وسيطة، فيجب عليك استخدام قائمة تهيئة مُنشئ الفئة الفرعية.على عكس Java، تدعم لغة C++ الوراثة المتعددة (للأفضل أو للأسوأ)، لذلك يجب الإشارة إلى الفئة الأساسية بالاسم، بدلاً من "super()".

class SuperClass
{
    public:

        SuperClass(int foo)
        {
            // do something with foo
        }
};

class SubClass : public SuperClass
{
    public:

        SubClass(int foo, int bar)
        : SuperClass(foo)    // Call the superclass constructor in the subclass' initialization list.
        {
            // do something with bar
        }
};

مزيد من المعلومات حول قائمة تهيئة المنشئ هنا و هنا.

نصائح أخرى

في C++، يتم استدعاء المنشئات بدون وسيطات لجميع الفئات الفائقة ومتغيرات الأعضاء، قبل الدخول إلى المنشئ الخاص بك.إذا كنت تريد تمرير الوسائط لهم، فهناك صيغة منفصلة لهذا تسمى "تسلسل المنشئ"، والتي تبدو كما يلي:

class Sub : public Base
{
  Sub(int x, int y)
  : Base(x), member(y)
  {
  }
  Type member;
};

إذا تم تشغيل أي شيء عند هذه النقطة، فسيتم استدعاء القواعد/الأعضاء الذين أكملوا البناء سابقًا، ويتم استدعاء المدمرين ويتم إعادة الاستثناء إلى المتصل.إذا كنت تريد اكتشاف الاستثناءات أثناء التسلسل، فيجب عليك استخدام دالة حاول منعها:

class Sub : public Base
{
  Sub(int x, int y)
  try : Base(x), member(y)
  {
    // function body goes here
  } catch(const ExceptionType &e) {
    throw kaboom();
  }
  Type member;
};

في هذا النموذج، لاحظ أن كتلة المحاولة يكون جسد الوظيفة، وليس داخل جسد الوظيفة؛وهذا يسمح لها بالتقاط الاستثناءات التي تم طرحها بواسطة التهيئة الضمنية أو الصريحة للأعضاء والفئة الأساسية، وكذلك أثناء نص الوظيفة.ومع ذلك، إذا لم تطرح كتلة التقاط الوظائف استثناءً مختلفًا، فسيعيد وقت التشغيل طرح الخطأ الأصلي؛الاستثناءات أثناء التهيئة لا تستطيع يتم تجاهلها.

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


class A : public B
{
public:
  A(int a, int b, int c);
private:
  int b_, c_;
};

بعد ذلك، بافتراض أن B لديه مُنشئ يأخذ int، قد يبدو مُنشئ A كما يلي:


A::A(int a, int b, int c) 
  : B(a), b_(b), c_(c) // initialization list
{
  // do something
}

كما ترون، يتم استدعاء منشئ الفئة الأساسية في قائمة التهيئة.بالمناسبة، يُفضل تهيئة أعضاء البيانات في قائمة التهيئة بدلاً من تعيين قيم b_ وc_ داخل نص المنشئ، لأنك توفر التكلفة الإضافية للتخصيص.

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

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

الطريقة الوحيدة لتمرير القيم إلى المُنشئ الأصلي هي من خلال قائمة التهيئة.يتم تنفيذ قائمة التهيئة باستخدام:ثم قائمة الفئات والقيم التي سيتم تمريرها إلى مُنشئ الفئات.

Class2::Class2(string id) : Class1(id) {
....
}

تذكر أيضًا أنه إذا كان لديك مُنشئ لا يأخذ أي معلمات من الفئة الأصلية، فسيتم استدعاؤه تلقائيًا قبل تنفيذ المُنشئ الفرعي.

إذا كان لديك مُنشئ بدون وسائط، فسيتم استدعاؤه قبل تنفيذ مُنشئ الفئة المشتقة.

إذا كنت تريد استدعاء مُنشئ أساسي باستخدام الوسائط، فيجب عليك كتابة ذلك بشكل صريح في المُنشئ المشتق مثل هذا:

class base
{
  public:
  base (int arg)
  {
  }
};

class derived : public base
{
  public:
  derived () : base (number)
  {
  }
};

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

إذا كانت لديك معلمات افتراضية في مُنشئك الأساسي، فسيتم استدعاء الفئة الأساسية تلقائيًا.

using namespace std;

class Base
{
    public:
    Base(int a=1) : _a(a) {}

    protected:
    int _a;
};

class Derived : public Base
{
  public:
  Derived() {}

  void printit() { cout << _a << endl; }
};

int main()
{
   Derived d;
   d.printit();
   return 0;
}

الإخراج هو:1

CDerived::CDerived()
: CBase(...), iCount(0)  //this is the initialisation list. You can initialise member variables here too. (e.g. iCount := 0)
    {
    //construct body
    }

لم يذكر أحد تسلسل استدعاءات المنشئ عندما يتم اشتقاق الفصل من فئات متعددة.التسلسل كما هو مذكور أثناء اشتقاق الفئات.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top