Kurucularımla üye başlatma listelerini kullanma konusunda kısmi görev yapıyorum ... ama uzun zamandır bunun nedenlerini unuttum ...
Kurucularınızda üye başlatma listeleri kullanıyor musunuz? Öyleyse neden? Değilse, neden olmasın?
Kurucularımla üye başlatma listelerini kullanma konusunda kısmi görev yapıyorum ... ama uzun zamandır bunun nedenlerini unuttum ...
Kurucularınızda üye başlatma listeleri kullanıyor musunuz? Öyleyse neden? Değilse, neden olmasın?
Yanıtlar:
İç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 B
varsayılan yapıcısı arayacak A
ve sonra, başlat a.x
3. Daha iyi bir yol olurdu B
'doğrudan çağırmak lar yapıcısı A
baş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 A
varsayı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;
};
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.
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.
Yalnızca parametrelenmiş kurucuları olan Alt Nesnelerin Başlatılması
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.
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.
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.
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.
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.
// 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;
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:
Ü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];
}
};
C ++ Temel Yönergeleri C.49'da açıklandığı gibi: Yapıcılarda atama başlatmayı tercih edin , varsayılan kuruculara gereksiz çağrı yapılmasını önler.
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:
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.
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)
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.