Pertanyaan Dapatkah saya memanggil konstruktor dari konstruktor lain (melakukan konstruktor) di C ++?


Sebagai C # pengembang Saya terbiasa menjalankan konstruktor:

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

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

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

Apakah ada cara untuk melakukan ini di C ++?

Saya mencoba memanggil nama Kelas dan menggunakan kata kunci 'ini', tetapi keduanya gagal.


756
2017-11-21 09:43


asal


Jawaban:


C ++ 11: Ya!

C ++ 11 dan seterusnya memiliki fitur yang sama ini (disebut mendelegasikan konstruktor).

Sintaksnya sedikit berbeda dari C #:

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

C + + 03: Tidak

Sayangnya, tidak ada cara untuk melakukannya di C ++ 03, tetapi ada dua cara untuk mensimulasikan ini:

  1. Anda dapat menggabungkan dua (atau lebih) konstruktor melalui parameter default:

    class Foo {
    public:
      Foo(char x, int y=0);  // combines two constructors (char) and (char, int)
      // ...
    };
    
  2. Gunakan metode init untuk berbagi kode umum:

    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)
    {
      // ...
    }
    

Lihat entri C ++ FAQ sebagai referensi.


1020
2017-11-21 10:04



Tidak, Anda tidak dapat memanggil satu konstruktor dari yang lain di C ++ 03 (disebut konstruktor delegasi).

Ini diubah dalam C ++ 11 (alias C ++ 0x), yang menambahkan dukungan untuk sintaks berikut:
(misalnya diambil dari Wikipedia)

class SomeType
{
  int number;

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

101
2017-11-21 10:00



Saya percaya Anda dapat memanggil konstruktor dari konstruktor. Ini akan mengkompilasi dan menjalankan. Saya baru-baru ini melihat seseorang melakukan ini dan itu berjalan pada Windows dan Linux.

Itu tidak melakukan apa yang Anda inginkan. Konstruktor batin akan membangun objek lokal sementara yang akan dihapus setelah konstruktor luar kembali. Mereka harus menjadi konstruktor yang berbeda juga atau Anda akan membuat panggilan rekursif.

Ref: https://isocpp.org/wiki/faq/ctors#init-methods


38
2017-08-12 15:31



Perlu menunjukkan bahwa Anda bisa panggil konstruktor kelas induk di konstruktor Anda misalnya:

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

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

Tapi, tidak, Anda tidak dapat memanggil konstruktor lain dari kelas yang sama.


19
2017-11-22 06:36



Di C ++ 11, Sebuah konstruktor dapat memanggil konstruktor lain yang berlebihan:

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

Selain itu, anggota dapat diinisialisasi seperti ini juga.

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

Ini harus menghilangkan kebutuhan untuk membuat metode pembantu inisialisasi. Dan masih disarankan untuk tidak memanggil fungsi virtual apa pun dalam konstruktor atau destruktor untuk menghindari penggunaan anggota yang mungkin tidak diinisialisasi.


17
2017-09-22 20:07



Jika Anda ingin menjadi jahat, Anda dapat menggunakan operator "baru" di tempat:

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

Sepertinya bekerja untukku.

sunting

Sebagai @ElvedinHamzagic menunjukkan, jika Foo berisi objek yang mengalokasikan memori, objek yang mungkin tidak dibebaskan. Ini memperumit lebih jauh.

Contoh yang lebih umum:

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 */
    } 
};

Terlihat sedikit kurang elegan, pasti. Solusi @ JohnIdol jauh lebih baik.


11
2018-03-24 16:53



Tidak, di C + + Anda tidak dapat memanggil konstruktor dari konstruktor. Apa yang dapat Anda lakukan, seperti ditunjukkan oleh warren, adalah:

  • Membebani konstruktor, menggunakan tanda tangan yang berbeda
  • Gunakan nilai default pada argumen, untuk membuat versi "lebih sederhana" tersedia

Perhatikan bahwa dalam kasus pertama, Anda tidak dapat mengurangi duplikasi kode dengan memanggil satu konstruktor dari yang lain. Anda tentu saja dapat memiliki metode terpisah, pribadi / dilindungi, yang melakukan semua inisialisasi, dan membiarkan konstruktor terutama berurusan dengan penanganan argumen.


7
2017-11-21 09:56



Dalam Visual C ++ Anda juga dapat menggunakan notasi ini di dalam constructor: this-> Classname :: Classname (parameter konstruktor lain). Lihat contoh di bawah ini:

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

Saya tidak tahu apakah itu berhasil di tempat lain, saya hanya mengujinya di Visual C ++ 2003 dan 2008. Anda juga dapat menelepon beberapa konstruktor dengan cara ini, saya kira, seperti di Jawa dan C #.

P.S .: Terus terang, saya terkejut bahwa ini tidak disebutkan sebelumnya.


5
2018-02-20 10:00



Jika saya memahami pertanyaan Anda dengan benar, Anda bertanya apakah Anda dapat memanggil beberapa konstruktor di C ++?

Jika itu yang Anda cari, maka tidak - itu tidak mungkin.

Anda tentu saja dapat memiliki beberapa konstruktor, masing-masing dengan tanda tangan argumen unik, dan kemudian memanggil yang Anda inginkan ketika Anda instantiate objek baru.

Anda bahkan dapat memiliki satu konstruktor dengan argumen default di bagian akhir.

Tetapi Anda mungkin tidak memiliki banyak konstruktor, dan kemudian memanggil masing-masing secara terpisah.


2
2017-11-21 09:49



Pilihan lain yang belum ditunjukkan adalah membagi kelas Anda menjadi dua, membungkus kelas antarmuka ringan di sekitar kelas asli Anda untuk mencapai efek yang Anda cari:

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

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

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

Ini bisa menjadi berantakan jika Anda memiliki banyak konstruktor yang harus memanggil rekan "tingkat atas", tetapi untuk beberapa konstruktor, itu harus bisa diterapkan.


2
2017-11-25 00:54



Saya akan mengusulkan penggunaan a private friend metode yang mengimplementasikan logika aplikasi konstruktor dan disebut oleh berbagai konstruktor. Berikut ini contohnya:

Asumsikan kita memiliki kelas yang disebut StreamArrayReader dengan beberapa bidang pribadi:

private:
    istream * in;
      // More private fields

Dan kami ingin mendefinisikan dua konstruktor:

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

Di mana yang kedua hanya menggunakan yang pertama (dan tentu saja kita tidak ingin menduplikasi implementasi dari yang pertama). Idealnya, seseorang ingin melakukan sesuatu seperti:

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

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

Namun, ini tidak diizinkan di C ++. Untuk alasan itu, kita dapat mendefinisikan metode teman pribadi sebagai berikut yang mengimplementasikan apa yang seharusnya dilakukan oleh konstruktor pertama:

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

Sekarang metode ini (karena ini adalah teman) memiliki akses ke bidang pribadi o. Kemudian, konstruktor pertama menjadi:

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

Perhatikan bahwa ini tidak membuat banyak salinan untuk salinan yang baru dibuat. Yang kedua menjadi:

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

Itu adalah, alih-alih memiliki satu konstruktor yang memanggil yang lain, keduanya memanggil teman pribadi!


2
2017-11-02 00:19