سؤال

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

هل تستخدم قوائم تهيئة الأعضاء في المنشئين لديك؟إذا كان الأمر كذلك لماذا؟إذا لم يكن الأمر كذلك، لماذا لا؟

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

المحلول

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

class A
{
public:
    A() { x = 0; }
    A(int x_) { x = x_; }
    int x;
};

class B
{
public:
    B()
    {
        a.x = 3;
    }
private:
    A a;
};

في هذه الحالة، المنشئ ل B سوف ندعو المنشئ الافتراضي ل A, ثم تهيئة a.x إلى 3. وسيلة أفضل سيكون ل Bمنشئ لاستدعاء مباشرة Aمنشئ في قائمة المهنة:

B()
  : a(3)
{
}

هذا سوف يدعو فقط AA(int) منشئ وليس منشئه الافتراضي. في هذا المثال، الفرق لا يكاد يذكر، ولكن تخيل لو كنت بذلك Aفعل المنشئ الافتراضي أكثر، مثل تخصيص الذاكرة أو فتح الملفات. أنت لا تريد أن تفعل ذلك دون داع.

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

class A
{
public:
    A(int x_) { x = x_; }
    int x;
};

class B
{
public:
    B() : a(3), y(2)  // 'a' and 'y' MUST be initialized in an initializer list;
    {                 // it is an error not to do so
    }
private:
    A a;
    const int y;
};

نصائح أخرى

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

  1. تهيئة الطبقة الأساسية

أحد الأسباب المهمة لاستخدام قائمة المهيئة المنشئ غير المذكورة في الإجابات هنا هو تهيئة الطبقة الأساسية.

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

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

  1. تهيئة subobjects التي لديها فقط منشئين معلمات

  2. كفاءة

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

  1. تهيئة أعضاء البيانات غير الثابتة

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

  1. تهيئة أعضاء البيانات المرجعية

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

بجانب مشكلات الأداء، هناك مشكلة أخرى مهمة جدًا والتي يمكن أن أسميها قابلية صيانة التعليمات البرمجية وقابلية التوسيع.

إذا كان T هو POD وبدأت في تفضيل قائمة التهيئة، فإذا تغير T مرة واحدة إلى نوع غير POD، فلن تحتاج إلى تغيير أي شيء حول التهيئة لتجنب استدعاءات المنشئ غير الضرورية لأنه تم تحسينه بالفعل.

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

نفس الشيء مع الأعضاء الثابتين أو الأعضاء المرجعيين، لنفترض أنه تم تعريف T في البداية على النحو التالي:

struct T
{
    T() { a = 5; }
private:
    int a;
};

بعد ذلك، قررت تأهيل a كـ const، إذا كنت ستستخدم قائمة التهيئة من البداية، فهذا كان تغييرًا في سطر واحد، ولكن بعد تعريف T على النحو الوارد أعلاه، فإنه يتطلب أيضًا حفر تعريف المنشئ لإزالة المهمة:

struct T
{
    T() : a(5) {} // 2. that requires changes here too
private:
    const int a; // 1. one line change
};

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

قبل تشغيل جسم المنشئ، يتم الاحتجاج بجميع المنشئين في فئة الوالدين ثم لحقولها. بشكل افتراضي، يتم استدعاء منشئون عدم الحجة. تتيح لك قوائم التهيئة أن تختار ما يسمى المنشئ وما هي الحجج التي يستقبلها المنشئ.

إذا كان لديك مرجع أو حقل ثابت، أو إذا لم يكن لدى أحد الفئات المستخدمة منشئ افتراضي، فيجب عليك استخدام قائمة التهيئة.

// Without Initializer List
class MyClass {
    Type variable;
public:
    MyClass(Type a) {  // Assume that Type is an already
                     // declared class and it has appropriate 
                     // constructors and operators
        variable = a;
    }
};

يتبع برنامج التحويل البرمجي هنا بعد الخطوات لإنشاء كائن من النوع MyClass
1. يسمى منشئ الكتابة أولا ل "A".
2. يسمى عامل التعيين من "النوع" داخل Body of MyClass () منشئ لتعيينه

variable = a;
  1. ثم يتم استدعاء Destructor أخيرا من "النوع" "A" لأنه يخرج من النطاق.

    الآن النظر في نفس الرمز مع myclass () منشئ مع قائمة المهنة

    // With Initializer List
     class MyClass {
    Type variable;
    public:
    MyClass(Type a):variable(a) {   // Assume that Type is an already
                     // declared class and it has appropriate
                     // constructors and operators
    }
    };
    

    مع قائمة التهيئة، تتابع الخطوات التالية بواسطة برنامج التحويل البرمجي:

    1. نسخ منشئ الفئة "النوع" يسمى لتهيئة: متغير (أ). يتم استخدام الوسائط في قائمة التهيئة لنسخ بناء "المتغير" مباشرة.
    2. يسمى Destructor من "النوع" ل "A" لأنه يخرج من النطاق.

فقط لإضافة بعض المعلومات الإضافية لإظهار مقدار الاختلاف الذي يمكن أن تصنع قائمة تهيئة الأعضاء Mak. وبعد في leetcode 303 المدى مجموع الاستعلام - غير قابل للتغيير، https://leetcode.com/problems/range-sum-query-querable/, ، حيث تحتاج إلى بناء وتهيئة إلى الصفر متجه مع حجم معين. هنا هو اثنين من التنفيذ وسرعة مختلفة.

بدون تهيئة الأعضاء قائمة، للحصول على AC كلفني 212 السيدة.

class NumArray {
public:
vector<int> preSum;
NumArray(vector<int> nums) {
    preSum = vector<int>(nums.size()+1, 0);
    int ps = 0;
    for (int i = 0; i < nums.size(); i++)
    {
        ps += nums[i];
        preSum[i+1] = ps;
    }
}

int sumRange(int i, int j) {
    return preSum[j+1] - preSum[i];
}
};

حاليا باستخدام قائمة تهيئة الأعضاء, ، الوقت المناسب للحصول على التيار المتردد 108 السيدة. وبعد مع هذا المثال البسيط، فمن الواضح تماما ذلك، قائمة تهيئة الأعضاء هي الطريقة الأكثر كفاءة. وبعد كل القياس هو من وقت التشغيل من LC.

class NumArray {
public:
vector<int> preSum;
NumArray(vector<int> nums) : preSum(nums.size()+1, 0) { 
    int ps = 0;
    for (int i = 0; i < nums.size(); i++)
    {
        ps += nums[i];
        preSum[i+1] = ps;
    }
}

int sumRange(int i, int j) {
    return preSum[j+1] - preSum[i];
}
};

بناء الجملة:

  class Sample
  {
     public:
         int Sam_x;
         int Sam_y;

     Sample(): Sam_x(1), Sam_y(2)     /* Classname: Initialization List */
     {
           // Constructor body
     }
  };

حاجة إلى قائمة التهيئة:

 class Sample
 {
     public:
         int Sam_x;
         int Sam_y;

     Sample()     */* Object and variables are created - i.e.:declaration of variables */*
     { // Constructor body starts 

         Sam_x = 1;      */* Defining a value to the variable */* 
         Sam_y = 2;

     } // Constructor body ends
  };

في البرنامج أعلاه، عند تنفيذ منشئ الفصل، sam_x. و SAM_Y. تم انشاؤها. ثم في هيئة المنشئ، يتم تعريف متغيرات البيانات الأعضاء هذه.

استخدم حالات:

  1. المتغيرات المرجعية والمرجعية في الفصل

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

  1. الكائنات الأعضاء من فئة Sample1 (Base) التي لا تملك منشئ افتراضي

     class Sample1 
     {
         int i;
         public:
         Sample1 (int temp)
         {
            i = temp;
         }
     };
    
      // Class Sample2 contains object of Sample1 
     class Sample2
     {
      Sample1  a;
      public:
      Sample2 (int x): a(x)      /* Initializer list must be used */
      {
    
      }
     };
    

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

 1. Default constructor of Sample1 class
 2. Initialization list in Sample2 class which will call the parametric constructor of Sample1 class (as per above program)
  1. اسم المعلمة منشئ الطبقة وعضو البيانات في الفصل نفسه:

     class Sample3 {
        int i;         /* Member variable name : i */  
        public:
        Sample3 (int i)    /* Local variable name : i */ 
        {
            i = i;
            print(i);   /* Local variable: Prints the correct value which we passed in constructor */
        }
        int getI() const 
        { 
             print(i);    /*global variable: Garbage value is assigned to i. the expected value should be which we passed in constructor*/
             return i; 
        }
     };
    

كما نعلم جميعا، المتغير المحلي لديه أولوية قصوى ثم المتغير العالمي إذا كان كلا المتغيرين لديهم نفس الاسم. في هذه الحالة، يعتبر البرنامج "أنا" قيمة متغير الجانب الأيسر والأيمن. IE: I = I} كمتغير محلي في Sample3 () منشئ ومتغير عضو فئة (I) حصلت على تجاوز. لتجنب، يجب أن نستخدم إما

  1. Initialization list 
  2. this operator.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top