C ++ Programlama Dili: Sıfır veya Altı Kural
Sıfır kuralı veya altı modern C ++ 'da çok önemli bir kuraldır. Mevcut kitabımda “C ++ Çekirdek Yönergeler: Modern C ++ için En İyi Uygulamalar” onları daha kesin olarak sunuyorum. Bu makalede İngilizce kitabımın ilgili alanlarından bahsetmek istiyorum.
Rainer Grimm yıllardır yazılım mimarı, ekip ve eğitim müdürü olarak çalıştı. C ++ programlama dilleri, Python ve Haskell hakkında makaleler yazmayı seviyor, ancak uzman konferanslarla konuşmayı da seviyor. Modern C ++ blogunda, C ++ tutkusuyla yoğun bir şekilde ilgileniyor.
Varsayılan olarak, derleyici gerekirse altı büyük üretebilir. Üyeler için altı özel işlevi tanımlayabilirsiniz, ancak bu nedenle derleyiciden bunları sağlamasını isteyebilirsiniz. = default veya onları silin = delete.
C.20: Önceden tanımlanmış herhangi bir işlemi zayıflatmaktan kaçınmak mümkünse, zaten
Bu kural “Sıfır kuralı“Bu, bir kopya/hareketin anlambilimini destekleyen türleri kullanarak kişiselleştirilmiş üretici, kopyala/yerinden edici üreticiler, ödev operatörleri veya muhripler yazmaktan kaçınmanın mümkün olduğu anlamına gelir. std::vector VEYA std::string.
class Named_map {
public:
// ... no default operations declared ...
private:
std::string name;
std::map<int, int> rep;
};
Named_map nm; // default construct
Named_map nm2{nm}; // copy construct
Zaten tanımlandıkları kopyaların inşaatının varsayılan yapısı ve ayrılması std::string VE std::map. Derleyici bir sınıf için kopya üreticisini otomatik olarak oluşturduğunda, tüm üyeler ve sınıfın tüm temelleri için kopya üreticisini çağırır.
C.21: OR = = herhangi bir varsayılan işlemi silmiyorsa, her şeyi tanımlar veya = siler
Big Six yakından ilişkilidir. Bu ilişki nedeniyle tanımlamalısınız veya =delete Altı hepsi. Sonuç olarak, bu kurala “Altı Kuralı“Bazen duyuyorsun”Beş Kural“, Çünkü varsayılan üretici özel ve bu nedenle bazen hariç tutulur.
Özel üyelerin işlevleri arasındaki çalışanlar
Howard Hinnant, otomatik olarak oluşturulan özel üyelerin işlevlerine genel bakış için 2014 ACCU Konferansı'ndaki konuşmasında gelişti.
Howard tablosu derin bir açıklama gerektirir.
// doubleFree.cpp
#include <cstddef>
class BigArray {
public:
BigArray(std::size_t len): len_(len), data_(new int[len]) {}
~BigArray(){
delete[] data_;
}
private:
size_t len_;
int* data_;
};
int main(){
BigArray bigArray1(1000);
BigArray bigArray2(1000);
bigArray2 = bigArray1; // (1)
} // (2)
Bu programın neden belirsiz davranışı var? Varsayılan kopyanın atanması için işlem bigArray2 = bigArray1 (1) Örneğin tüm üyeleri kopyalar bigArray2. Özellikle, işaretçi verilerinin kopyalandığını ancak verilerin kopyalandığını ortalamaların kopyası. Yani, muhrip bigArray1 VE bigArray2 Buna (2) denir ve çifte serbest bırakılmış belirsiz bir davranışımız var.
Örneğin sezgisel olmayan davranışı BigArray Yüzeysel bir kopyasını yapar BigArrayAncak açık olanı yok ediciyi uyguladı BigArray Verilerin sahipliğini varsayar.
Addizzosanitizer belirsiz davranışı görünür kılar.
C.22 Varsayılan işlemleri tutarlı hale getirin
Bu kural önceki kuralla ilgilidir. Farklı anlambilimli varsayılan işlemler uygulanırsa, sınıf kullanıcıları çok karışık olabilir. Bu garip davranış, üyenin işlevleri kısmen uygulanırsa ve bunlardan kısmi talep ederseniz, öyle görünebilir. =default. Derleyici tarafından üretilen özel üyelerin işlevlerinin sizinkiyle aynı anlamsal olduğunu varsayamazsınız.
Garip davranış örneği olarak, işte sınıf Strange. Strange Bir işaretçi içerir int.
// strange.cpp
#include <iostream>
struct Strange {
Strange(): p(new int(2011)) {}
// deep copy
Strange(const Strange& a) : p(new int(*a.p)) {} // (1)
// shallow copy
// equivalent to Strange& operator
// = (const Strange&) = default;
Strange& operator = (const Strange& a) { // (2)
p = a.p;
return *this;
}
int* p;
};
int main() {
std::cout << 'n';
std::cout << "Deep copy" << 'n';
Strange s1;
Strange s2(s1); // (3)
std::cout << "s1.p: " << s1.p << "; *s1.p: " << *s1.p << 'n';
std::cout << "s2.p: " << s2.p << "; *s2.p: " << *s2.p << 'n';
std::cout << "*s2.p = 2017" << 'n';
*s2.p = 2017; // (4)
std::cout << "s1.p: " << s1.p << "; *s1.p: " << *s1.p << 'n';
std::cout << "s2.p: " << s2.p << "; *s2.p: " << *s2.p << 'n';
std::cout << 'n';
std::cout << "Shallow copy" << 'n';
Strange s3;
s3 = s1; // (5)
std::cout << "s1.p: " << s1.p << "; *s1.p: " << *s1.p << 'n';
std::cout << "s3.p: " << s3.p << "; *s3.p: " << *s3.p << 'n';
std::cout << "*s3.p = 2017" << 'n';
*s3.p = 2017; // (6)
std::cout << "s1.p: " << s1.p << "; *s1.p: " << *s1.p << 'n';
std::cout << "s3.p: " << s3.p << "; *s3.p: " << *s3.p << 'n';
std::cout << 'n';
std::cout << "delete s1.p" << 'n'; // (7)
delete s1.p;
std::cout << "s2.p: " << s2.p << "; *s2.p: "
<< *s2.p << 'n'; // (8)
std::cout << "s3.p: " << s3.p << "; *s3.p: " << *s3.p << 'n';
std::cout << 'n';
}
Sınıf Strange Bir kopya üreticisi (1) ve bir kopya atama operatörü (2) vardır. Kopya üreticisi derin bir kopya uygular ve atama operatörü bir yüzey kopyası uygular. Bu arada, derleyici veya kopya atama operatörü tarafından oluşturulan kopyalar üreticisi, yüzey kopyasını uyguladı. Çoğu zaman, türleriniz için derin bir kopya semantiği (semantik değerler) istersiniz, ancak muhtemelen yağma işlemleri için farklı bir anlambilim sahibi olmak istemeyeceksiniz. Fark, derin kopyanın anlambiliminin iki yeni ayrı depolama oluşturmasıdır. p(new int(*a.p)) Yüzey kopyasının semantiği sadece işaretçiyi kopyalarken p = a.p. İle oynayalım Strange Türler. Aşağıdaki şekilde program çıktısını göstermektedir.
(3) oluşturmak için kopya üreticisini kullanın s2. İşaretçi adreslerinin görüntülenmesi ve işaretçinin değerini değiştirin s2.p (4) onu gösterir s1 VE s2 Bunlar iki farklı nesne. Bu durum böyle değil s1 VE s3. (5) 'deki bir kopyanın kopyası yüzeysel bir kopya gerçekleştirir. Sonuçlar, işaretçiyi değiştirmesidir s3.p (6) böylece işaretçiyi etkiler s1.p Çünkü her iki bölüm de aynı değeri ifade eder.
İşaretçiyi silirsem eğlence başlar s1.p (7). Derin kopya sayesinde yanlış bir şey olmaz s2.p, Ama değeri s3.p Valid olmayan işaretçide bombalar. Daha kesin olmak gerekirse: valid olmayan işaretçide derefning *s3.p (8) Bu belirsiz bir davranıştır.
Sırada ne var?
Normal tip kavramı, standart modeller kütüphanesine (STL) sıkıca sabitlenmiştir. Fikir, STL'nin yaratıcısı Alexander Stephanov'a dayanıyor. Bir sonraki yazımda normal türler hakkında daha fazla yazacağım.
(RME)
Ne yazık ki, bu bağlantı artık geçerli değil.
Boşa harcanan eşyalara olan bağlantılar, 7 günlük daha büyükse veya çok sık çağrılmışsa gerçekleşmez.
Bu makaleyi okumak için bir Haberler+ paketine ihtiyacınız var. Şimdi yükümlülük olmadan bir hafta deneyin – yükümlülük olmadan!
C ++ Programlama Dili: Sıfır veya Altı Kural
Sıfır kuralı veya altı modern C ++ 'da çok önemli bir kuraldır. Mevcut kitabımda “C ++ Çekirdek Yönergeler: Modern C ++ için En İyi Uygulamalar” onları daha kesin olarak sunuyorum. Bu makalede İngilizce kitabımın ilgili alanlarından bahsetmek istiyorum.

Rainer Grimm yıllardır yazılım mimarı, ekip ve eğitim müdürü olarak çalıştı. C ++ programlama dilleri, Python ve Haskell hakkında makaleler yazmayı seviyor, ancak uzman konferanslarla konuşmayı da seviyor. Modern C ++ blogunda, C ++ tutkusuyla yoğun bir şekilde ilgileniyor.

Varsayılan olarak, derleyici gerekirse altı büyük üretebilir. Üyeler için altı özel işlevi tanımlayabilirsiniz, ancak bu nedenle derleyiciden bunları sağlamasını isteyebilirsiniz. = default veya onları silin = delete.
C.20: Önceden tanımlanmış herhangi bir işlemi zayıflatmaktan kaçınmak mümkünse, zaten
Bu kural “Sıfır kuralı“Bu, bir kopya/hareketin anlambilimini destekleyen türleri kullanarak kişiselleştirilmiş üretici, kopyala/yerinden edici üreticiler, ödev operatörleri veya muhripler yazmaktan kaçınmanın mümkün olduğu anlamına gelir. std::vector VEYA std::string.
class Named_map {
public:
// ... no default operations declared ...
private:
std::string name;
std::map<int, int> rep;
};
Named_map nm; // default construct
Named_map nm2{nm}; // copy construct
Zaten tanımlandıkları kopyaların inşaatının varsayılan yapısı ve ayrılması std::string VE std::map. Derleyici bir sınıf için kopya üreticisini otomatik olarak oluşturduğunda, tüm üyeler ve sınıfın tüm temelleri için kopya üreticisini çağırır.
C.21: OR = = herhangi bir varsayılan işlemi silmiyorsa, her şeyi tanımlar veya = siler
Big Six yakından ilişkilidir. Bu ilişki nedeniyle tanımlamalısınız veya =delete Altı hepsi. Sonuç olarak, bu kurala “Altı Kuralı“Bazen duyuyorsun”Beş Kural“, Çünkü varsayılan üretici özel ve bu nedenle bazen hariç tutulur.
Özel üyelerin işlevleri arasındaki çalışanlar
Howard Hinnant, otomatik olarak oluşturulan özel üyelerin işlevlerine genel bakış için 2014 ACCU Konferansı'ndaki konuşmasında gelişti.

Howard tablosu derin bir açıklama gerektirir.
- Her şeyden önce, kullanıcı tarafından açıkça tanımlayan veya otomatik olarak derleyiciyi gerektiren bu altı özel üye işlevinden biri için bildirilen araçlar =default. Özel üye işlevinin ortadan kaldırılması =delete Bu nedenle kullanıcı olarak kabul edilir. Adı kullanırken, varsayılan üreticinin adını arayın, beyan edilen bir kullanıcı olarak sayılır.
- Herhangi bir üretici tanımlarken, önceden tanımlanmış üretici elde edilmez. Varsayılan üretici, konu olmadan çağrılabilen bir üreticidir.
- Varsayılan bir üreticiyi tanımlarken veya silerken =default VEYA =deleteÜyeler için altı özel şirketten başka bir şey ilgilenmiyor.
- Bir muhrip, bir kopya üreticisi veya bir kopya yardım operatörünü tanımlarken veya silerken =default VEYA =deleteSeyahat ataması derleyicisi ve üreticisi tarafından üretilen hiçbir hareket üreticisi elde edilmez. Bu şey, araştırma operasyonlarını bir hareket olarak taşımak veya bir kopya yapımı veya bir kopyanın atanması olarak kopyalama işlemlerini aramak için ödevi geri taşımak anlamına gelir. Bu geri dönüşün otomatizmi masada kırmızı olarak işaretlenmiştir.
- Tanımlarken veya silerken =default VEYA =delete Bir hareket üreticisi veya bir hareket atama operatörü, yalnızca tanımı alırsınız =default VEYA =delete Üreticiyi taşıyın veya atama operatörünü taşıyın. Sonuç olarak, kopya üreticisi ve kopya atama operatörü =delete. Bir kopya işlem aramasını çağırın, nedenlerin yapımının veya kopyasının bir kopyası, Themefor, derleme hatası.
// doubleFree.cpp
#include <cstddef>
class BigArray {
public:
BigArray(std::size_t len): len_(len), data_(new int[len]) {}
~BigArray(){
delete[] data_;
}
private:
size_t len_;
int* data_;
};
int main(){
BigArray bigArray1(1000);
BigArray bigArray2(1000);
bigArray2 = bigArray1; // (1)
} // (2)
Bu programın neden belirsiz davranışı var? Varsayılan kopyanın atanması için işlem bigArray2 = bigArray1 (1) Örneğin tüm üyeleri kopyalar bigArray2. Özellikle, işaretçi verilerinin kopyalandığını ancak verilerin kopyalandığını ortalamaların kopyası. Yani, muhrip bigArray1 VE bigArray2 Buna (2) denir ve çifte serbest bırakılmış belirsiz bir davranışımız var.
Örneğin sezgisel olmayan davranışı BigArray Yüzeysel bir kopyasını yapar BigArrayAncak açık olanı yok ediciyi uyguladı BigArray Verilerin sahipliğini varsayar.
Addizzosanitizer belirsiz davranışı görünür kılar.

C.22 Varsayılan işlemleri tutarlı hale getirin
Bu kural önceki kuralla ilgilidir. Farklı anlambilimli varsayılan işlemler uygulanırsa, sınıf kullanıcıları çok karışık olabilir. Bu garip davranış, üyenin işlevleri kısmen uygulanırsa ve bunlardan kısmi talep ederseniz, öyle görünebilir. =default. Derleyici tarafından üretilen özel üyelerin işlevlerinin sizinkiyle aynı anlamsal olduğunu varsayamazsınız.
Garip davranış örneği olarak, işte sınıf Strange. Strange Bir işaretçi içerir int.
// strange.cpp
#include <iostream>
struct Strange {
Strange(): p(new int(2011)) {}
// deep copy
Strange(const Strange& a) : p(new int(*a.p)) {} // (1)
// shallow copy
// equivalent to Strange& operator
// = (const Strange&) = default;
Strange& operator = (const Strange& a) { // (2)
p = a.p;
return *this;
}
int* p;
};
int main() {
std::cout << 'n';
std::cout << "Deep copy" << 'n';
Strange s1;
Strange s2(s1); // (3)
std::cout << "s1.p: " << s1.p << "; *s1.p: " << *s1.p << 'n';
std::cout << "s2.p: " << s2.p << "; *s2.p: " << *s2.p << 'n';
std::cout << "*s2.p = 2017" << 'n';
*s2.p = 2017; // (4)
std::cout << "s1.p: " << s1.p << "; *s1.p: " << *s1.p << 'n';
std::cout << "s2.p: " << s2.p << "; *s2.p: " << *s2.p << 'n';
std::cout << 'n';
std::cout << "Shallow copy" << 'n';
Strange s3;
s3 = s1; // (5)
std::cout << "s1.p: " << s1.p << "; *s1.p: " << *s1.p << 'n';
std::cout << "s3.p: " << s3.p << "; *s3.p: " << *s3.p << 'n';
std::cout << "*s3.p = 2017" << 'n';
*s3.p = 2017; // (6)
std::cout << "s1.p: " << s1.p << "; *s1.p: " << *s1.p << 'n';
std::cout << "s3.p: " << s3.p << "; *s3.p: " << *s3.p << 'n';
std::cout << 'n';
std::cout << "delete s1.p" << 'n'; // (7)
delete s1.p;
std::cout << "s2.p: " << s2.p << "; *s2.p: "
<< *s2.p << 'n'; // (8)
std::cout << "s3.p: " << s3.p << "; *s3.p: " << *s3.p << 'n';
std::cout << 'n';
}
Sınıf Strange Bir kopya üreticisi (1) ve bir kopya atama operatörü (2) vardır. Kopya üreticisi derin bir kopya uygular ve atama operatörü bir yüzey kopyası uygular. Bu arada, derleyici veya kopya atama operatörü tarafından oluşturulan kopyalar üreticisi, yüzey kopyasını uyguladı. Çoğu zaman, türleriniz için derin bir kopya semantiği (semantik değerler) istersiniz, ancak muhtemelen yağma işlemleri için farklı bir anlambilim sahibi olmak istemeyeceksiniz. Fark, derin kopyanın anlambiliminin iki yeni ayrı depolama oluşturmasıdır. p(new int(*a.p)) Yüzey kopyasının semantiği sadece işaretçiyi kopyalarken p = a.p. İle oynayalım Strange Türler. Aşağıdaki şekilde program çıktısını göstermektedir.

(3) oluşturmak için kopya üreticisini kullanın s2. İşaretçi adreslerinin görüntülenmesi ve işaretçinin değerini değiştirin s2.p (4) onu gösterir s1 VE s2 Bunlar iki farklı nesne. Bu durum böyle değil s1 VE s3. (5) 'deki bir kopyanın kopyası yüzeysel bir kopya gerçekleştirir. Sonuçlar, işaretçiyi değiştirmesidir s3.p (6) böylece işaretçiyi etkiler s1.p Çünkü her iki bölüm de aynı değeri ifade eder.
İşaretçiyi silirsem eğlence başlar s1.p (7). Derin kopya sayesinde yanlış bir şey olmaz s2.p, Ama değeri s3.p Valid olmayan işaretçide bombalar. Daha kesin olmak gerekirse: valid olmayan işaretçide derefning *s3.p (8) Bu belirsiz bir davranıştır.
Sırada ne var?
Normal tip kavramı, standart modeller kütüphanesine (STL) sıkıca sabitlenmiştir. Fikir, STL'nin yaratıcısı Alexander Stephanov'a dayanıyor. Bir sonraki yazımda normal türler hakkında daha fazla yazacağım.
(RME)
Ne yazık ki, bu bağlantı artık geçerli değil.
Boşa harcanan eşyalara olan bağlantılar, 7 günlük daha büyükse veya çok sık çağrılmışsa gerçekleşmez.
Bu makaleyi okumak için bir Haberler+ paketine ihtiyacınız var. Şimdi yükümlülük olmadan bir hafta deneyin – yükümlülük olmadan!