Mengapa saya lebih suka menggunakan daftar inisialisasi anggota?

Jawaban:

278

Untuk anggota kelas POD , tidak ada bedanya, itu hanya masalah gaya. Untuk anggota kelas yang merupakan kelas, maka itu menghindari panggilan yang tidak perlu ke konstruktor default. Mempertimbangkan:

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

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

Dalam hal ini, konstruktor untuk Bakan memanggil konstruktor default untuk A, dan kemudian menginisialisasi a.xke 3. Cara yang lebih baik bagi Bkonstruktor untuk langsung memanggil Akonstruktor dalam daftar penginisialisasi:

B()
  : a(3)
{
}

Hal ini hanya akan memanggil A's A(int)konstruktor dan tidak konstruktor default. Dalam contoh ini, perbedaannya dapat diabaikan, tetapi bayangkan jika Anda yang Amembangun default akan melakukan lebih, seperti mengalokasikan memori atau membuka file. Anda tidak ingin melakukan itu dengan tidak perlu.

Selain itu, jika kelas tidak memiliki konstruktor default, atau Anda memiliki constvariabel anggota, Anda harus menggunakan daftar penginisialisasi:

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;
};
Adam Rosenfield
sumber
5
suatu keharusan juga untuk kasus penting referensi
4pie0
5
Mengapa tidak menggunakan "a (3);" atau "a = A (3);" dalam tubuh konstruktor default B?
Sergey
1
Bisakah Anda jelaskan, apa yang Anda maksud dengan POD?
Jonas Stein
2
@JonasStein POD adalah seperangkat aturan yang didefinisikan dengan baik berkaitan dengan struktur data sederhana (bukan kelas lengkap). Baca FAQ untuk lebih lanjut: stackoverflow.com/questions/146452/what-are-pod-types-in-c
monkey0506
2
@Sergey, konstruktor default A masih akan dipanggil.
Vassilis
44

Terlepas dari alasan kinerja yang disebutkan di atas, jika kelas Anda menyimpan referensi ke objek yang diteruskan sebagai parameter konstruktor atau kelas Anda memiliki variabel const, maka Anda tidak punya pilihan selain menggunakan daftar penginisialisasi.

Naveen
sumber
7
Hal yang sama berlaku untuk anggota const saya percaya.
Richard Corden
ya, Tidak dapat menggunakan tugas untuk memodifikasi variabel const sehingga harus diinisialisasi.
Hareen Laks
23
  1. Inisialisasi kelas dasar

Salah satu alasan penting untuk menggunakan daftar penginisialisasi konstruktor yang tidak disebutkan dalam jawaban di sini adalah inisialisasi kelas dasar.

Sesuai urutan konstruksi, kelas dasar harus dibangun sebelum kelas anak. Tanpa daftar penginisialisasi konstruktor, ini dimungkinkan jika kelas dasar Anda memiliki konstruktor default yang akan dipanggil sebelum memasuki konstruktor kelas anak.

Tetapi, jika kelas dasar Anda hanya memiliki konstruktor parameter, maka Anda harus menggunakan daftar penginisialisasi konstruktor untuk memastikan bahwa kelas dasar Anda diinisialisasi sebelum kelas anak.

  1. Inisialisasi Subobjects yang hanya memiliki konstruktor berparameter

  2. Efisiensi

Menggunakan daftar penginisialisasi konstruktor, Anda menginisialisasi anggota data Anda untuk menyatakan secara tepat yang Anda butuhkan dalam kode Anda daripada menginisialisasi mereka ke keadaan default mereka & kemudian mengubah negara mereka ke yang Anda butuhkan dalam kode Anda.

  1. Menginisialisasi anggota data const non-statis

Jika anggota data const non-statis di kelas Anda memiliki konstruktor default & Anda tidak menggunakan daftar penginisialisasi konstruktor, Anda tidak akan dapat menginisialisasi mereka ke keadaan yang dimaksudkan karena mereka akan diinisialisasi ke keadaan default mereka.

  1. Inisialisasi anggota data referensi

Anggota data referensi harus diinisialisasi ketika kompiler memasuki konstruktor karena referensi tidak dapat dideklarasikan & diinisialisasi nanti. Ini hanya mungkin dengan daftar penginisialisasi konstruktor.

yuvi
sumber
10

Di samping masalah kinerja, ada satu lagi yang sangat penting yang saya sebut rawatan dan perluasan kode.

Jika T adalah POD dan Anda mulai memilih daftar inisialisasi, maka jika suatu kali T akan berubah menjadi tipe non-POD, Anda tidak perlu mengubah apa pun di sekitar inisialisasi untuk menghindari panggilan konstruktor yang tidak perlu karena sudah dioptimalkan.

Jika tipe T memang memiliki konstruktor default dan satu atau lebih konstruktor yang ditentukan pengguna dan satu kali Anda memutuskan untuk menghapus atau menyembunyikan yang default, maka jika daftar inisialisasi digunakan, Anda tidak perlu memperbarui kode jika konstruktor yang ditentukan pengguna karena mereka sudah diimplementasikan dengan benar.

Sama dengan anggota const atau anggota referensi, katakanlah awalnya T didefinisikan sebagai berikut:

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

Selanjutnya, Anda memutuskan untuk memenuhi syarat sebagai const, jika Anda akan menggunakan daftar inisialisasi dari awal, maka ini adalah perubahan baris tunggal, tetapi memiliki T didefinisikan seperti di atas, itu juga perlu menggali definisi konstruktor untuk menghapus tugas:

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

Bukan rahasia bahwa pemeliharaan jauh lebih mudah dan lebih rentan kesalahan jika kode ditulis bukan oleh "kode monyet" tetapi oleh seorang insinyur yang membuat keputusan berdasarkan pertimbangan yang lebih dalam tentang apa yang dia lakukan.

mloskot
sumber
5

Sebelum badan konstruktor dijalankan, semua konstruktor untuk kelas induknya dan kemudian untuk bidangnya dipanggil. Secara default, konstruktor tanpa argumen dipanggil. Daftar inisialisasi memungkinkan Anda memilih konstruktor mana yang dipanggil dan argumen apa yang diterima konstruktor.

Jika Anda memiliki referensi atau bidang const, atau jika salah satu kelas yang digunakan tidak memiliki konstruktor default, Anda harus menggunakan daftar inisialisasi.

Jamal Zafar
sumber
2
// 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;
    }
};

Di sini kompiler mengikuti langkah-langkah berikut untuk membuat objek bertipe MyClass
1. Konstruktor tipe disebut pertama untuk "a".
2. Operator penugasan "Type" disebut bagian dalam konstruktor MyClass () untuk ditugaskan

variable = a;
  1. Dan akhirnya destruktor "Type" dipanggil untuk "a" karena keluar dari ruang lingkup.

    Sekarang pertimbangkan kode yang sama dengan konstruktor MyClass () dengan Initializer List

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

    Dengan Daftar Initializer, langkah-langkah berikut diikuti oleh kompiler:

    1. Copy constructor dari kelas "Type" dipanggil untuk menginisialisasi: variabel (a). Argumen dalam daftar penginisialisasi digunakan untuk menyalin konstruk "variabel" secara langsung.
    2. Destructor "Type" dipanggil untuk "a" karena keluar dari ruang lingkup.
Rahul Singh
sumber
2
Sementara cuplikan kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan tentang kode sangat membantu meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, dan orang-orang itu mungkin tidak tahu alasan untuk saran kode Anda. Tolong juga cobalah untuk tidak membuat kerumunan kode Anda dengan komentar penjelasan, ini mengurangi keterbacaan kode dan penjelasan! meta.stackexchange.com/q/114762/308249
davejal
2
Silakan tulis pemahaman Anda sendiri atau cukup bagikan tautan ke sumber asli (di sini, geeksforgeeks.com) alih-alih hanya menyalin-menempelkannya.
yuvi
1

Hanya dengan menambahkan beberapa info tambahan untuk menunjukkan seberapa besar perbedaan yang dapat dibuat oleh daftar inisialisasi anggota . Dalam leetcode 303 Range Sum Query - Immutable, https://leetcode.com/problems/range-sum-query-immutable/ , di mana Anda perlu membuat dan menginisialisasi nol vektor dengan ukuran tertentu. Berikut adalah dua implementasi dan perbandingan kecepatan yang berbeda.

Tanpa daftar inisialisasi anggota , untuk mendapatkan AC harganya sekitar 212 ms .

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];
}
};

Sekarang menggunakan daftar inisialisasi anggota , waktu untuk mendapatkan AC adalah sekitar 108 ms . Dengan contoh sederhana ini, sangat jelas bahwa, daftar inisialisasi anggota jauh lebih efisien . Semua pengukuran berasal dari waktu berjalan dari 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];
}
};
Yi Wang
sumber
0

Sintaksis:

  class Sample
  {
     public:
         int Sam_x;
         int Sam_y;

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

Perlu daftar Inisialisasi:

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

dalam program di atas, Ketika konstruktor kelas dieksekusi, Sam_x dan Sam_y dibuat. Kemudian dalam tubuh konstruktor, variabel data anggota tersebut didefinisikan.

Gunakan kasing:

  1. Variabel Const dan Reference dalam suatu Kelas

Dalam C, variabel harus didefinisikan selama pembuatan. dengan cara yang sama di C ++, kita harus menginisialisasi variabel Const dan Reference selama pembuatan objek dengan menggunakan daftar Inisialisasi. jika kita melakukan inisialisasi setelah pembuatan objek (Inside constructor body), kita akan mendapatkan kesalahan waktu kompilasi.

  1. Objek anggota kelas Sample1 (base) yang tidak memiliki konstruktor default

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

Saat membuat objek untuk kelas turunan yang secara internal akan memanggil konstruktor kelas turunan dan memanggil konstruktor kelas dasar (default). jika kelas dasar tidak memiliki konstruktor default, pengguna akan mendapatkan kesalahan waktu kompilasi. Untuk menghindari, kita harus memiliki keduanya

 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. Nama parameter konstruktor kelas dan anggota data Kelas sama:

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

Seperti kita ketahui bersama, variabel lokal memiliki prioritas tertinggi daripada variabel global jika kedua variabel tersebut memiliki nama yang sama. Dalam hal ini, program mempertimbangkan nilai "i" {variabel kiri dan kanan. yaitu: i = i} sebagai variabel lokal di konstruktor Sample3 () dan variabel anggota Kelas (i) ditimpa. Untuk menghindari, kita harus menggunakan keduanya

  1. Initialization list 
  2. this operator.
Eswaran Pandi
sumber