Neden üye başlatma listesini kullanmayı tercih etmeliyim?


Yanıtlar:


278

İçin POD sınıf üyelerine, bu tarz meselesi, hiç fark etmez. Sınıf olan sınıf üyeleri için, varsayılan bir kurucuya gereksiz bir çağrı yapılmasını önler. Düşünmek:

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

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

Bu durumda, yapıcı için Bvarsayılan yapıcısı arayacak Ave sonra, başlat a.x3. Daha iyi bir yol olurdu B'doğrudan çağırmak lar yapıcısı Abaşlatıcısı listesinde yapıcı s':

B()
  : a(3)
{
}

Bu yalnızca çağırır A'ın A(int)yapıcı değil varsayılan yapıcısı. Bu örnekte, fark göz ardı edilebilir, ancak Avarsayılan kurucunun bellek ayırma veya dosyaları açma gibi daha fazlasını yapıp yapmayacağınızı hayal edin . Bunu gereksiz yere yapmak istemezsiniz.

Bir sınıf varsayılan bir kurucu yoktur veya bir varsa Dahası, constüye değişkeni, sen gerekir bir başlatıcı listesini kullanmak:

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

5
bir zorunluluk da referans için önemli bir durumdur
4pie0

5
Neden "a (3);" veya "a = A (3);" B'nin varsayılan yapıcısının gövdesinde?
Sergey

1
POD ile ne demek istediğinizi açıklar mısınız?
Jonas Stein

2
@JonasStein POD, basit veri yapılarına (tam sınıflardan ziyade) ilişkin iyi tanımlanmış bir kurallar kümesidir. Daha fazla bilgi için SSS
bölümünü

2
@Sergey, A'nın varsayılan kurucusu yine de çağrılır.
Vassilis

44

Yukarıda belirtilen performans nedenlerinin yanı sıra, sınıfınız yapıcı parametreleri olarak iletilen nesnelere başvuru saklarsa veya sınıfınızda const değişkenleri varsa, başlatıcı listelerini kullanmak dışında herhangi bir seçeneğiniz yoktur.


7
Aynı şey inandığım const üyeleri için de geçerli.
Richard Corden

evet, const değişkenlerini değiştirmek için atama kullanılamaz, bu nedenle başlatılması gerekir.
Hareen Laks

23
  1. Temel sınıfın başlatılması

Burada cevaplarda belirtilmeyen yapıcı başlatıcı listesinin kullanılmasının önemli bir nedeni temel sınıfın başlatılmasıdır.

İnşaat sırasına göre, taban sınıf çocuk sınıfından önce inşa edilmelidir. Yapıcı başlatıcısı listesi olmadan, temel sınıfınızda alt sınıfın yapıcısına girmeden hemen önce çağrılacak varsayılan yapıcı varsa bu mümkündür.

Ancak, temel sınıfınızda yalnızca parametrelenmiş kurucu varsa, temel sınıfınızın alt sınıftan önce başlatılmasını sağlamak için kurucu başlatıcı listesini kullanmanız gerekir.

  1. Yalnızca parametrelenmiş kurucuları olan Alt Nesnelerin Başlatılması

  2. verim

Yapıcı başlatıcısı listesini kullanarak, veri üyelerinizi önce varsayılan durumlarına başlatmak ve ardından durumlarını kodunuzda ihtiyaç duyduğunuz duruma değiştirmek yerine, kodunuzda tam olarak gereken duruma getirin.

  1. Statik olmayan sabit veri üyelerini başlatma

Sınıfınızdaki statik olmayan sabit veri üyelerinin varsayılan kurucuları varsa ve kurucu başlatıcısı listesini kullanmıyorsanız, varsayılan durumlarına başlatılacakları için onları istenen duruma başlatamazsınız.

  1. Referans veri üyelerinin başlatılması

Kaynaklar derleyici kurucuya girdiğinde referans verisi kullanılmalıdır, çünkü referanslar daha sonra ilan edilemez ve başlatılamaz. Bu yalnızca kurucu başlatıcısı listesiyle mümkündür.


10

Performans sorunlarının yanında, kodun sürdürülebilirliğini ve genişletilebilirliğini aradığım çok önemli bir tane daha var.

Bir T POD ise ve başlatma listesini tercih etmeye başlıyorsanız, bir kez T POD olmayan bir türe dönüşecekse, zaten optimize edilmiş olduğundan gereksiz kurucu çağrılarını önlemek için başlatma etrafında herhangi bir şeyi değiştirmeniz gerekmez.

T türünde varsayılan kurucu ve bir veya daha fazla kullanıcı tanımlı kurucu varsa ve bir kez varsayılanı kaldırmaya veya gizlemeye karar verdiyseniz, başlatma listesi kullanıldıysa, kullanıcı tanımlı kurucularınız varsa kodu güncellemeniz gerekmez. bunlar zaten doğru şekilde uygulanmış.

Const üyeleri veya referans üyeleriyle aynı şekilde, başlangıçta T'nin aşağıdaki gibi tanımlandığını varsayalım:

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

Sonra, bir const olarak nitelendirmeye karar verirsiniz, başlangıç ​​listesini başlangıçtan itibaren kullanırsanız, bu tek bir satır değişikliğiydi, ancak T'nin yukarıdaki gibi tanımlanması, atamayı kaldırmak için yapıcı tanımını kazmayı gerektirir:

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

Kod bir "kod maymunu" tarafından değil, ne yaptığı hakkında daha derin düşünülerek kararlar veren bir mühendis tarafından yazılmışsa, bakımın çok daha kolay ve daha az hataya açık olduğu bir sır değildir.


5

Yapıcı gövdesi çalıştırılmadan önce, üst sınıf ve sonra alanları için tüm yapıcılar çağrılır. Varsayılan olarak, bağımsız değişken yapıcıları çağrılır. Başlatma listeleri, hangi kurucunun çağrıldığını ve kurucunun hangi bağımsız değişkenleri alacağını seçmenizi sağlar.

Bir başvuru veya const alanınız varsa veya kullanılan sınıflardan birinin varsayılan bir yapıcısı yoksa, bir başlatma listesi kullanmanız gerekir.


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

Burada derleyici, MyClass
1 türünde bir nesne oluşturmak için aşağıdaki adımları izler. Türün yapıcısı ilk olarak “a” için çağrılır.
2. “Tür” atama işleci, atamak üzere MyClass () yapıcısının gövdesi içinde çağrılır

variable = a;
  1. Ve son olarak kapsam dışında olduğu için “Tip” yıkıcı “a” olarak adlandırılır.

    Şimdi Initializer List'e sahip MyClass () yapıcısı ile aynı kodu düşünün

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

    Başlatıcı Listesi ile aşağıdaki adımları derleyici takip eder:

    1. “Type” sınıfının kopya yapıcısı başlatmak için çağrılır: değişken (a). Başlatıcı listesindeki bağımsız değişkenler, "değişken" yapısını doğrudan kopyalamak için kullanılır.
    2. “Tip” imha edici, kapsam dışında kaldığı için “a” olarak adlandırılır.

2
Bu kod snippet'i soruyu çözebilir, ancak kodun dışında bir açıklama da dahil olmak üzere, yayınınızın kalitesini artırmaya yardımcı olur. Gelecekte okuyucular için soruyu cevapladığınızı ve bu kişilerin kod önerinizin nedenlerini bilmeyebileceğini unutmayın. Lütfen kodunuzu açıklayıcı yorumlarla doldurmamaya çalışın, bu hem kodun hem de açıklamaların okunabilirliğini azaltır! meta.stackexchange.com/q/114762/308249
davejal

2
Lütfen, kendi fikrinizi yazın veya sadece kopya yapıştırmak yerine orijinal kaynağa (burada, geeksforgeeks.com) bağlantıyı paylaşın.
yuvi

1

Üye başlatma listesinin ne kadar fark yaratabileceğini göstermek için bazı ek bilgiler eklemek için . Leetcode 303 Aralık Toplamı Sorgusu - Değişmez, https://leetcode.com/problems/range-sum-query-immutable/ , burada belirli bir boyutta bir vektör oluşturmak ve sıfırlamak için başlangıç ​​yapmanız gerekir. İşte iki farklı uygulama ve hız karşılaştırması.

Üye başlatma listesi olmadan , AC almak bana yaklaşık 212 ms mal oldu .

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

Şimdi üye başlatma listesini kullanarak , AC alma süresi yaklaşık 108 ms'dir . Bu basit örnekle, üye başlatma listesinin çok daha verimli olduğu oldukça açıktır . Tüm ölçüm LC'den çalışma süresidir.

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


0

Sözdizimi:

  class Sample
  {
     public:
         int Sam_x;
         int Sam_y;

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

Başlatma İhtiyacı listesi:

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

Yukarıdaki programda, sınıfın yapıcısı yürütüldüğünde Sam_x ve Sam_y oluşturulur. Daha sonra yapıcı gövdesinde bu üye veri değişkenleri tanımlanır.

Kullanım örnekleri:

  1. Sınıftaki Sabit ve Referans değişkenler

C, değişkenler gerekir oluşturulması sırasında tanımlanabilir. C ++ ile aynı şekilde, Nesne oluşturma sırasında Başlatma listesini kullanarak Const ve Reference değişkenini başlatmamız gerekir. nesne oluşturulduktan sonra başlatmayı yaparsak (Oluşturucu gövdesi içinde), derleme zamanı hatası alırız.

  1. Varsayılan yapıcısı olmayan Sample1 (base) sınıfının üye nesneleri

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

Türetilmiş sınıf yapıcısını dahili olarak çağıran ve temel sınıf yapıcısını çağıran türetilmiş sınıf için nesne oluştururken (varsayılan). temel sınıf varsayılan kurucuya sahip değilse, kullanıcı derleme zamanı hatası alır. Bundan kaçınmak için,

 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. Sınıf yapıcısının parametre adı ve bir Sınıfın Veri üyesi aynı:

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

Hepimizin bildiği gibi, her iki değişkenin de aynı ada sahip olması durumunda global değişken, global değişkenden daha yüksek önceliğe sahiptir. Bu durumda, program "i" değerini {hem sol hem de sağ taraf değişkenini dikkate alır. ie: i = i} Sample3 () yapıcısında ve Class üye değişkeni (i) 'de yerel değişken olarak geçersiz kılındı. Bundan kaçınmak için,

  1. Initialization list 
  2. this operator.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.