الشبكة العربية لمطوري الألعاب

خبير  أحمد عبد الغني مشاركة 1

وفي 19/شوال/1429 09:18 ص، أعرب وسام البهنسي عن رأيه بالموقف كالآتي:

* تهيئة المتغيرات تتم في الـ initializer list في الـ constructor، بدلاً من جسم الـ constructor نفسه... مثلاً:
 


class Explorer
{
private:
   int m_iMemberOne;
   float m_fMemberTwo;
 
   Explorer()
   {
      m_iMemberOne = 0;
      m_fMemberTwo = 0.0f;
   }
};
 
يجب أن يكون الكود:
 

class Explorer
{
private:
   int m_iMemberOne;
   float m_fMemberTwo;
 
   Explorer() :
     m_iMemberOne(0),
     m_fMemberTwo(0.0f)
   {
   }
};

 
بصراحة لا أعلم ما الفرق، فكيف تعمل هذه الميزة؟ وهل أحدها أفضل من الأخرى؟ أم أن الأمر مجرد اصطلاح وتنظيم؟
 
ما رأيكم؟ ما الفكرة هنا؟

اللهم انصر أهلنا في فلسطين وآجرنا أن نكون عوناً لهم

محترف مشرف عبد اللطيف حاجي علي مشاركة 2

الفرق كبير جداً. إلا أنه قد لا يكون واضحاً في حالة متغير بسيط من نوع int أو float. لفهم الفرق أنظر إلى الكود الآتي:


#include 
class MemberClass
{
public:
      MemberClass()
            // Initialize internal variables here
      {
            std::cout << "MEMBERCLASS: Default constructor called" << std::endl;
      }
      MemberClass(const MemberClass& rhs)
            // Initialize internal variables here
      {
            std::cout << "MEMBERCLASS: Copy-Constructor called" << std::endl;
      }
      ~MemberClass()
      {
            std::cout << "MEMBERCLASS: Destructor called" << std::endl;
      }
      const MemberClass& operator =(const MemberClass& op)
      {
            // Assign internal variables here
            std::cout << "MEMBERCLASS: Assignment operator called" << std::endl;
            return *this;
      }
};
class InitListTest
{
public:
      InitListTest(const MemberClass &init)
            :m_Internal(init)
      { }
protected:
      MemberClass m_Internal;
};
class AssignTest
{
public:
      AssignTest(const MemberClass &init)
      { m_Internal = init;}
protected:
      MemberClass m_Internal;
};
int main()
{
      std::cout << "Create local copy of MemberClass" << std::endl; 
      MemberClass local;
      std::cout << "Begin test:" << std::endl; 
      std::cout << "Initializer list" << std::endl; 
      InitListTest testInitList(local);
      std::cout << "Simple Assignment" << std::endl; 
      AssignTest testAssign(local);
      std::cout << "---End test" << std::endl; 
}
 
إن تشغيل البرنامج السابق سيظهر:



Create local copy of MemberClass
MEMBERCLASS: Default constructor called
Begin test:
Initializer list
MEMBERCLASS: Copy-Constructor called
Simple Assignment
MEMBERCLASS: Default constructor called
MEMBERCLASS: Assignment operator called
---End test
MEMBERCLASS: Destructor called
MEMBERCLASS: Destructor called
MEMBERCLASS: Destructor called
Press any key to continue . . .
 
الآن لنقم بالتحليل:
في البدء وعند تنفيذ 'MemberClass local' فإن الـ DefaultConstructor سيستدعى وهذا طبيعي.
الآن عند تنفيذ 'InitListTest testInitList(local)' فإن الـ Copy-Contructor سيتدعى فقط وذلك لتهيئة المتغير m_Internal داخل الـ class المسمى InitListTest. وينتهي الأمر هنا
أما عند تنفيذ AssignTest testAssign(local) فإن الـ Default-Contructor يستدعى أولاً وذلك بعد الدخول إلى الـ constructor داخل الـ class المسمى AssignTest ثم عند تنفيذ 'm_Internal = init' فإن الـ Assignment operator تستدعى للـ class المسمى MemberClass
ثم ينتهي الاختبار هنا ويستدعى الـ Destructor لكل نسخة من MemberClass بشكل طبيعي
 
إذاً للتلخيص فإن الـ Copy Contructor يستدعى فقط إذا ما استخدمنا الـ Initializer-list
أما إذا استخدمنا assignments بسيطة فإن الـ default-contructor يستدعى أولاً ثم الـ assignment operator. طبعاً فرق الـ performance واضح.
 
أيضاً فإن الـ Initializer-list تستدعي وجود الـ copy-constructor بشكل public فإن لم يوجد لم يكن بامكاننا استخدامه
أما الطريقة الثانية فتستدعي وجود الـ default-contructor و الـ assignment operator بشكل public
 
كما قلت فإن الطريقتين قد لا يكون لهما نفس التأثير في حالة متغيرات بسيطة كـ int و float.  إلا أن استخدام الـ initializer-list في الحالتين يعتير من العادات البرمجية الجيدة

عبد اللطيف حاجي علي
مبرمج
In|Framez

خبير  سعيد بسيوني مشاركة 3

بحب أضيف ان طريقة الـ initializer list هي الطريقة الوحيدة اللي بتسمحلك تمرر بارامترات للكلاس الأب في حالة الوراثة...
 
يعني مثلا.. عندنا كلاس أب بياخد في الـ constructor بتاعو بارامترين..
 



// الكلاس الأب
class Parent
{
public:
  Parent(const char* Name)
  {
    std::cout << Name << std::endl; // اطبع البارامتر
  }
};
 
 
// الكلاس الابن
class Child : public Parent
{
public:
  Child() : Parent("I am a child") // نادي الباني بتاع الأب، ومررله أي حاجة
  {
  }
};
 
 
بينما لو بتحاول تسوي الكلام ده ضمن البلوك بتاعت الـ constructor، فمش حيمشي الحال
 
 
😋