constructor شرح - هل يمكنني استدعاء مُنشئ من مُنشئ آخر(هل يتشابك منشئ)في C++؟




معنى java (13)

أعتقد أنه يمكنك استدعاء مُنشئ من مُنشئ. سيتم تجميعها وتشغيلها. لقد رأيت شخصًا ما يقوم بذلك مؤخرًا وركض على نظامي التشغيل Windows و Linux.

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

المرجع: https://isocpp.org/wiki/faq/ctors#init-methods

كمطور C# اعتدت على العمل من خلال المنشئين:

class Test {
    public Test() {
        DoSomething();
    }

    public Test(int count) : this() {
        DoSomethingWithCount(count);
    }

    public Test(int count, string name) : this(count) {
        DoSomethingWithName(name);
    }
}

هل هناك طريقة للقيام بذلك في C ++؟

حاولت استدعاء اسم الفئة واستخدام الكلمة الأساسية 'this' هذه ، لكن كلاهما فشل.


C ++ 11: نعم!

لدى C ++ 11 وما بعده هذه الميزة نفسها (تسمى مندوبين مفوضين ).

الصيغة تختلف قليلاً عن C #:

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {}
};

C ++ 03: لا

للأسف ، لا توجد طريقة للقيام بذلك في C ++ 03 ، ولكن هناك طريقتان لمحاكاة هذا:

  1. يمكنك الجمع بين اثنين (أو أكثر) من المنشئات عبر المعلمات الافتراضية:

    class Foo {
    public:
      Foo(char x, int y=0);  // combines two constructors (char) and (char, int)
      // ...
    };
    
  2. استخدم طريقة init لمشاركة الشفرة الشائعة:

    class Foo {
    public:
      Foo(char x);
      Foo(char x, int y);
      // ...
    private:
      void init(char x, int y);
    };
    
    Foo::Foo(char x)
    {
      init(x, int(x) + 7);
      // ...
    }
    
    Foo::Foo(char x, int y)
    {
      init(x, y);
      // ...
    }
    
    void Foo::init(char x, int y)
    {
      // ...
    }
    

راجع إدخال الأسئلة الشائعة C ++ للرجوع إليها.


في Visual C ++ يمكنك أيضاً استخدام هذا الترميز داخل مُنشئ: this-> Classname :: Classname (معلمات منشئ آخر). انظر على سبيل المثال أدناه:

class Vertex
{
 private:
  int x, y;
 public:
  Vertex(int xCoo, int yCoo): x(xCoo), y(yCoo) {}
  Vertex()
  {
   this->Vertex::Vertex(-1, -1);
  }
};

لا أعرف ما إذا كان يعمل في مكان آخر ، لقد قمت باختباره فقط في Visual C ++ 2003 و 2008. قد تقوم أيضًا بالاتصال بعدة منشئي بهذه الطريقة ، كما أفترض في Java و C #.

ملاحظة: بصراحة ، فوجئت أن هذا لم يرد ذكره في وقت سابق.


في C++11 ، Wikipedia :

class Foo  {
     int d;         
public:
    Foo  (int i) : d(i) {}
    Foo  () : Foo(42) {} //New to C++11
};

بالإضافة إلى ذلك ، يمكن تهيئة الأعضاء على هذا النحو أيضًا.

class Foo  {
     int d = 5;         
public:
    Foo  (int i) : d(i) {}
};

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


قد يعمل هذا الأسلوب لبعض أنواع الفئات (عندما يتصرف مشغل التعيين بشكل جيد):

Foo::Foo()
{
    // do what every Foo is needing
    ...
}

Foo::Foo(char x)
{
    *this = Foo();

    // do the special things for a Foo with char
    ...
}

ببساطة ، لا يمكنك قبل C ++ 11.

يقدم C ++ 11 المفوضين المفوضين :

تفويض منشئ

إذا ظهر اسم الفئة نفسها على هيئة فئة أو معرّف في قائمة مُهيئ الأعضاء ، فيجب أن تتكون القائمة من مُهيئ عضو واحد فقط ؛ يُعرف مُنشئ مثل منشئ المفوض ومنشئ المحدد بواسطة العضو الوحيد من قائمة المُهيئ هو المنشئ الهدف

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

لا يمكن أن يكون تفويض المنشئين عوديًا.

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char,int)
};

لاحظ أن المُنشئ المفوض هو اقتراح الكل أو لا شيء؛ إذا كان من المفوض منشئ لمنشئ آخر ، لا يسمح للمنشئ استدعاء لأية أعضاء آخرين في قائمة التهيئة. هذا منطقي إذا كنت تفكر في تهيئة أعضاء const / المرجعية مرة واحدة وفقط مرة واحدة.


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


سيكون من السهل أكثر اختبار ، من يقرر :) حاول هذا:

#include <iostream>

class A {
public:
    A( int a) : m_a(a) {
        std::cout << "A::Ctor" << std::endl;    
    }
    ~A() {
        std::cout << "A::dtor" << std::endl;    
    }
public:
    int m_a;
};

class B : public A {
public:
    B( int a, int b) : m_b(b), A(a) {}
public:
    int m_b;
};

int main() {
    B b(9, 6);
    std::cout << "Test constructor delegation a = " << b.m_a << "; b = " << b.m_b << std::endl;    
    return 0;
}

وتجميعها مع 98 std: g ++ main.cpp -std = c ++ 98 -o test_1

سترى:

A::Ctor
Test constructor delegation a = 9; b = 6
A::dtor

وبالتالي :)


إذا فهمت سؤالك بشكل صحيح ، فأنت تسأل عما إذا كان بإمكانك الاتصال بمنشئين متعددين في C ++؟

إذا كان هذا هو ما تبحث عنه ، فعندئذ لا - هذا غير ممكن.

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

يمكنك حتى الحصول على منشئ واحد مع الوسيطات الافتراضية على النهاية.

ولكن قد لا يكون لديك العديد من المنشئات ، ومن ثم الاتصال بكل منها على حدة.


تجدر الإشارة إلى أنه يمكنك استدعاء منشئ لفئة الأم في المنشئ الخاص بك على سبيل المثال:

class A { /* ... */ };

class B : public A
{
    B() : A()
    {
        // ...
    }
};

ولكن ، لا ، لا يمكنك استدعاء مُنشئ آخر من نفس الفئة.


إذا كنت تريد أن تكون شريرة ، يمكنك استخدام عامل التشغيل "الجديد" في الموقع:

class Foo() {
    Foo() { /* default constructor deliciousness */ }
    Foo(Bar myParam) {
      new (this) Foo();
      /* bar your param all night long */
    } 
};

يبدو أن العمل بالنسبة لي.

تصحيح

كما يشيرElvedinHamzagic ، إذا احتوى فو على كائن خصص الذاكرة ، فقد لا يتم تحرير هذا الكائن. هذا يعقد الامور اكثر.

مثال أكثر عمومية:

class Foo() {
private:
  std::vector<int> Stuff;
public:
    Foo()
      : Stuff(42)
    {
      /* default constructor deliciousness */
    }

    Foo(Bar myParam)
    {
      this->~Foo();
      new (this) Foo();
      /* bar your param all night long */
    } 
};

تبدو أقل قليلا أنيقة ، على وجه اليقين. @ حل JohnIdol هو أفضل من ذلك بكثير.


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

class Test_Base {
    public Test_Base() {
        DoSomething();
    }
};

class Test : public Test_Base {
    public Test() : Test_Base() {
    }

    public Test(int count) : Test_Base() {
        DoSomethingWithCount(count);
    }
};

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


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





c++ constructor