c++ معنى - ما هي قواعد استدعاء منشئ superclass؟




بالعربي كلمة (9)

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

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


Answers

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


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

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

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


يتم استدعاء منشئات الفئة الأساسية تلقائيًا إذا لم يكن لديهم وسيطة. إذا كنت تريد استدعاء منشئ superclass مع وسيطة ، يجب عليك استخدام قائمة التهيئة منشئ فئة فرعية. بخلاف 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
        }
};

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


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

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


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

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 ++ هناك مفهوم قائمة التهيئة الخاص بالمطور ، وهو المكان الذي يمكنك فيه استدعاء منشئ الطبقة الأساسية ومن حيث يجب عليك أيضًا تهيئة أعضاء البيانات. تأتي قائمة التهيئة بعد توقيع المُنشئ بعد النقطتين ، وقبل جسم المُنشئ. لنفترض أن لدينا صف A:


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_ داخل جسم المُنشئ ، لأنك تقوم بحفظ التكلفة الإضافية للتخصيص.

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


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


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

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

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

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

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


تختلف قواعد C # بشكل كبير عن قواعد Java و C ++.

عندما تكون في المُنشئ لكائن ما في C # ، يوجد ذلك الكائن في نموذج مُهيأ بشكل كامل (ليس فقط "تم إنشاؤه") ، كنوع مشتق بالكامل.

namespace Demo
{
    class A 
    {
      public A()
      {
        System.Console.WriteLine("This is a {0},", this.GetType());
      }
    }

    class B : A
    {      
    }

    // . . .

    B b = new B(); // Output: "This is a Demo.B"
}

هذا يعني أنه إذا قمت بالاتصال بوظيفة ظاهرية من مُنشئ A ، فسيتم حلها إلى أي تجاوز في B ، إذا تم توفيرها.

حتى إذا قمت بإعداد A و B عمداً مثل هذا ، وفهم كامل لسلوك النظام ، فقد تكون في حالة صدمة لاحقًا. لنفترض أنك سمّيت الوظائف الافتراضية في منشئ B ، "مع العلم" أنها ستتعامل مع B أو A حسب الاقتضاء. ثم يمر الوقت ، ويقرر شخص آخر أنهم بحاجة إلى تحديد C ، وتجاوز بعض الوظائف الافتراضية هناك. وينتهي كل من مُنشئ B المفاجئ بالشفرة البرمجية في C ، مما قد يؤدي إلى سلوك مثير للدهشة.

ربما تكون فكرة جيدة لتجنب الوظائف الافتراضية في الصانعين على أي حال ، لأن القواعد مختلفة جدا بين C # و C ++ و Java. قد لا يعرف المبرمجون ما يمكن توقعه!







c++ inheritance constructor