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




copy constructor (10)

كمطور 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' هذه ، لكن كلاهما فشل.

https://code.i-harness.com


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

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

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


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

افترض لدينا فئة تسمى StreamArrayReader مع بعض الحقول الخاصة:

private:
    istream * in;
      // More private fields

ونريد تعريف المنشئين:

public:
    StreamArrayReader(istream * in_stream);
    StreamArrayReader(char * filepath);
    // More constructors...

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

StreamArrayReader::StreamArrayReader(istream * in_stream){
    // Implementation
}

StreamArrayReader::StreamArrayReader(char * filepath) {
    ifstream instream;
    instream.open(filepath);
    StreamArrayReader(&instream);
    instream.close();
}

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

private:
  friend void init_stream_array_reader(StreamArrayReader *o, istream * is);

الآن هذه الطريقة (لأنه صديق) لديها حق الوصول إلى الحقول الخاصة o . ثم ، يصبح أول منشئ:

StreamArrayReader::StreamArrayReader(istream * is) {
    init_stream_array_reader(this, is);
}

لاحظ أن هذا لا يؤدي إلى إنشاء نسخ متعددة للنسخ التي تم إنشاؤها حديثًا. يصبح الثاني:

StreamArrayReader::StreamArrayReader(char * filepath) {
    ifstream instream;
    instream.open(filepath);
    init_stream_array_reader(this, &instream);
    instream.close();
}

بمعنى ، بدلاً من وجود مُنشئ واحد يدعو آخر ، كل من استدعاء صديق خاص!


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

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 هو أفضل من ذلك بكثير.


ببساطة ، لا يمكنك قبل 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 / المرجعية مرة واحدة وفقط مرة واحدة.


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

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

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

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

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


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

#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++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) {}
};

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


في 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 ++ لا يمكنك استدعاء مُنشئ من مُنشئ. ما يمكنك القيام به ، كما أشار وارين ، هو:

  • الزائد على المنشئ ، وذلك باستخدام التوقيعات المختلفة
  • استخدم القيم الافتراضية على الوسيطات ، لإتاحة إصدار "أبسط"

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


لا ، لا يمكنك استدعاء مُنشئ واحد من آخر في C ++ 03 (يسمى مُنشئ تفويض).

هذا تغير في C ++ 11 (المعروف أيضا بـ C ++ 0x) ، والذي أضاف دعمًا للبناء التالي:
(مثال مأخوذ من Wikipedia )

class SomeType
{
  int number;

public:
  SomeType(int newNumber) : number(newNumber) {}
  SomeType() : SomeType(42) {}
};






constructor