8 Soru: Üçün Kuralı Nedir?

tarafından oluşturulan soru Sat, May 26, 2018 12:00 AM
  • bir nesneyi kopyalamanın anlamı nedir?
  • kopya kurucu ve kopya atama işleci nedir?
  • Onları ne zaman kendim ilan etmem gerekir?
  • Nesnelerimin kopyalanmasını nasıl önleyebilirim?
2011
  1. 2010-11-13 14: 06: 31Z
  2. @ İkili: En azından bir oylama yapmadan önce yorum tartışmalarını okumak için zaman ayırın. Metin çok daha basitti, ama Fred'den genişlemesi istendi. Ayrıca, bu dilbilgisi adlı dört soru olsa da, gerçekten birkaç yönden sadece bir soru. (Buna katılmıyorsanız, POV'unuzu her bir soruyu kendi kendine yanıtlayarak ve sonuçlara oy vermemize izin vererek kanıtlayın.)
    2010-11-15 23: 02: 07Z
  3. Fred, işte C ++ 1x ile ilgili cevabınıza ilginç bir ek var: stackoverflow.com/questions/4782757/… . Bununla nasıl başa çıkacağız?
    2011-01-25 14: 18: 41Z
  4. 2011-06-27 16: 39: 02Z
  5. C ++ 11'den itibaren bunun beş kuralına veya bunun gibi bir şeye yükseltildiğini düşünüyorum.
    2015-08-19 10: 31: 45Z
8 Yanıtlar                              8                         

Tanıtım

C ++, kullanıcı tanımlı türlerin değişkenlerini değer semantiklerine sahip olarak ele alır. Bu, nesnelerin dolaylı olarak çeşitli bağlamlarda kopyalandığı anlamına gelir, ve "bir nesneyi kopyalamanın" gerçekte ne anlama geldiğini anlamalıyız.

Basit bir örnek ele alalım:

 
class person
{
    std::string name;
    int age;

public:

    person(const std::string& name, int age) : name(name), age(age)
    {
    }
};

int main()
{
    person a("Bjarne Stroustrup", 60);
    person b(a);   // What happens here?
    b = a;         // And here?
}

(name(name), age(age) bölümünden şaşırıyorsanız, buna üye başlatıcı listesi denir.)

Özel üye işlevleri

person nesnesini kopyalamanın anlamı nedir? main işlevi, iki farklı kopyalama senaryosu gösterir. Sıfırlama person b(a);, kopya kurucu tarafından gerçekleştirilir. Görevi, mevcut bir nesnenin durumuna göre yeni bir nesne inşa etmektir. Atama b = a, kopya atama operatörü tarafından gerçekleştirilir. İşi genellikle biraz daha karmaşık. çünkü hedef nesne zaten ele alınması gereken geçerli bir durumda.

Ne kopya kurucuyu ne de atama operatörünü (ne yıkıcıyı) kendimiz ilan etmediğimizden, bunlar bizim için dolaylı olarak tanımlanmıştır. Standarttan alıntı:

  

[...] kopya yapıcısı ve kopya atama operatörü, [...] ve yıkıcı özel üye işlevleridir.   [ Not : Uygulama bu üye işlevlerini dolaylı olarak bildirir   Bazı sınıflar için, program açıkça ilan edilmediğinde.   Uygulama, kullanılmaları halinde dolaylı olarak tanımlayacaktır. [...] son not ]   [n3126.pdf bölüm 12 §1]

Varsayılan olarak, bir nesneyi kopyalamak, üyelerini kopyalamak anlamına gelir:

  

Sendika olmayan bir sınıf X için dolaylı olarak tanımlanmış kopya yapıcısı, alt nesnelerinin üye olarak bir kopyasını çıkarır.   [n3126.pdf bölüm 12.8 §16]

     

Sendika olmayan bir sınıf X için dolaylı olarak tanımlanmış kopya ataması işleci, üye olarak kopya ataması yapar   onun alt hedeflerinden.   [n3126.pdf bölüm 12.8 §30]

Örtük tanımları

person için dolaylı olarak tanımlanmış özel üye işlevleri şöyle görünür:

 
// 1. copy constructor
person(const person& that) : name(that.name), age(that.age)
{
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    name = that.name;
    age = that.age;
    return *this;
}

// 3. destructor
~person()
{
}

Memberwisekopyalama, tam olarak bu durumda istediğimiz şey:  name ve age kopyalanır, bu sayede bağımsız bir person nesnesini alırız. Örtük tanımlı yıkıcı her zaman boştur. Bu, bu durumda da iyidir, çünkü yapıcıda herhangi bir kaynak edinmedik. Üyelerin yıkıcıları, person yıkıcı bittikten sonra dolaylı olarak çağrıldı:

  

Yıkıcı cismi çalıştırdıktan ve vücutta bulunan otomatik nesneleri yok ettikten sonra,   X sınıfı için bir yıkıcı, X'in doğrudan [...] üyeleri için yıkıcı çağırıyor   [n3126.pdf 12.4 §6]

Kaynakları yönetme

Peki bu özel üye işlevlerini ne zaman açıkça ilan etmeliyiz? sınıfımız bir kaynağı yönettiğinde , yani Sınıfın bir nesnesi bu kaynak için sorumlu olduğunda. Bu genellikle yapıcının yapıcıda edinildiği anlamına gelir. (veya kurucuya geçti) ve yıkıcıda serbest bırakıldı .

Ön standart C ++ 'ya geri dönelim. std::string diye bir şey yoktu ve programcılar işaretçilere âşıktı. person sınıfı şu şekilde görünüyor olabilir:

 
class person
{
    char* name;
    int age;

public:

    // the constructor acquires a resource:
    // in this case, dynamic memory obtained via new[]
    person(const char* the_name, int the_age)
    {
        name = new char[strlen(the_name) + 1];
        strcpy(name, the_name);
        age = the_age;
    }

    // the destructor must release this resource via delete[]
    ~person()
    {
        delete[] name;
    }
};

Bugün bile insanlar hala bu tarzda sınıflar yazıp başlarını belaya sokuyorlar: " Bir kişiyi bir vektöre zorladım ve şimdi çılgın bellek hataları alıyorum! " Varsayılan olarak, bir nesneyi kopyalamanın, üyelerini kopyalamak anlamına geldiğini unutmayın. ancak name üyesinin kopyalanması yalnızca bir işaretçiyi kopyalar, işaret ettiği karakter dizisini değil ! Bunun çok hoş olmayan etkileri var:

  1. a aracılığıyla yapılan değişiklikler b aracılığıyla görülebilir.
  2. b bir kere imha edildiğinde, a.name sarkan bir işaretçidir.
  3. a yok edilirse, sarkan işaretçiyi silmek tanımsız davranış şeklinde sonuçlanır.
  4. Görev, name'un görevden önce neyi gösterdiğini dikkate almadığından, er ya da geç her yerde bellek sızıntıları olacak.

Açık tanımları

Üye olarak kopyalama işlemi istenen etkiye sahip olmadığından, karakter dizisinin derin kopyalarını almak için kopya yapıcısını ve kopyalama atama operatörünü açıkça tanımlamalıyız:

 
// 1. copy constructor
person(const person& that)
{
    name = new char[strlen(that.name) + 1];
    strcpy(name, that.name);
    age = that.age;
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    if (this != &that)
    {
        delete[] name;
        // This is a dangerous point in the flow of execution!
        // We have temporarily invalidated the class invariants,
        // and the next statement might throw an exception,
        // leaving the object in an invalid state :(
        name = new char[strlen(that.name) + 1];
        strcpy(name, that.name);
        age = that.age;
    }
    return *this;
}

Başlatma ve atama arasındaki farkı not edin: bellek sızıntılarını önlemek için name a atamadan önce eski durumu parçalamalıyız. Ayrıca, x = x formunun kendi kendine atanmasına karşı korunmalıyız. Bu onay olmadan delete[] name, kaynak dizesini içeren diziyi silerdi, çünkü x = x yazdığınızda, hem this->name hem de that.name aynı işaretçiyi içerir.

İstisna güvenliği

Ne yazık ki, new char[...] bellek tükenmesi nedeniyle bir istisna atarsa ​​bu çözüm başarısız olur. Olası bir çözüm, yerel bir değişken tanıtmak ve ifadeleri yeniden sıralamaktır:

 
// 2. copy assignment operator
person& operator=(const person& that)
{
    char* local_name = new char[strlen(that.name) + 1];
    // If the above statement throws,
    // the object is still in the same state as before.
    // None of the following statements will throw an exception :)
    strcpy(local_name, that.name);
    delete[] name;
    name = local_name;
    age = that.age;
    return *this;
}

Bu, açık bir kontrol olmadan kendi kendine atamayı da önemser. Bu sorunun daha da sağlam bir çözümü kopyala ve takas yap deyimdir , ama burada istisnai güvenlik detaylarına girmeyeceğim. Yalnızca şu noktaya değinmek için istisnalardan bahsettim: Kaynakları yöneten sınıflar yazmak zor.

Sorumsuz kaynaklar

Dosya tanıtıcıları veya muteksler gibi bazı kaynaklar kopyalanamaz veya kopyalanmamalıdır. Bu durumda, bir tanım yapmadan kopya kurucuyu ve kopya ataması işlecini private olarak bildirmeniz yeterlidir:

 
private:

    person(const person& that);
    person& operator=(const person& that);

Alternatif olarak, boost::noncopyable’dan devralabilir veya silinmiş olarak beyan edebilirsiniz (C ++ 11 ve üstü):

 
person(const person& that) = delete;
person& operator=(const person& that) = delete;

Üçün kuralı

Bazen bir kaynağı yöneten bir sınıf uygulamanız gerekir. (Hiçbir zaman tek bir sınıfta birden fazla kaynağı yönetmeyin, bu sadece acıya yol açacaktır.) Bu durumda, üçün kuralını hatırlayın:

  

Herhangi bir şekilde yıkıcıyı açıklamanız gerekirse,   yapıcıyı kopyala veya ödev operatörünü kendin kopyala,   Muhtemelen üçünü de açıkça ilan etmeniz gerekiyor.

(Maalesef, bu "kural", C ++ standardı veya bildiğim herhangi bir derleyici tarafından uygulanmadı.)

Beş kuralı

C ++ 11'den itibaren, bir nesnenin 2 özel üye işlevi vardır: move yapıcısı ve move atama. Bu işlevleri de uygulamak için beş devletin kuralı.

İmzaları olan bir örnek:

 
class person
{
    std::string name;
    int age;

public:
    person(const std::string& name, int age);        // Ctor
    person(const person &) = default;                // Copy Ctor
    person(person &&) noexcept = default;            // Move Ctor
    person& operator=(const person &) = default;     // Copy Assignment
    person& operator=(person &&) noexcept = default; // Move Assignment
    ~person() noexcept = default;                    // Dtor
};

sıfırın kuralı

3/5 kuralı ayrıca 0/3/5 kuralı olarak da adlandırılır. Kuralın sıfır kısmı, sınıfınızı oluştururken özel üye işlevlerinden hiçbirini yazmamanıza izin verilmediğini belirtir.

Önerileri

Çoğu zaman, bir kaynağı kendiniz yönetmeniz gerekmez, çünkü std::string gibi mevcut bir sınıf zaten sizin için yapıyor. Sadece std::string üye kullanarak basit kodu karşılaştırın char* kullanarak sarsılmış ve hataya eğilimli bir alternatife ve ikna olmalısınız. Uzak durduğun süreceHam işaretçi üyelerinden, üç kural, kendi kodunuzu ilgilendirmesi muhtemel değildir.

    
1657
2018-12-25 18: 07: 46Z
  1. Fred, (A) kodlu kodda kötü uygulanmış ödevi hecelemeyin ve yanlış olduğunu söyleyen bir not ekleyin. ve taslakta başka yere bakın; ya kodda c & ya kullanın ya da sadece tüm bu üyelerin (B) uygulanmasını atlayın; ilk yarıyı kısaltırsınız; bu, ROT ile ilgisi yoktur; (C) hareket semantiğinin girişini ve bunun RoT için ne anlama geldiğini tartışırsınız.
    2010-11-13 14: 00: 40Z
  2. Ancak yazı C /W yapılmalı, bence. Terimleri çoğunlukla doğru tutmanızı seviyorum (yani " kopyala atama operatörü" demeniz ve atamanın bir kopyaya işaret edemediği ortak tuzağa dokunmamanız). div>
    2010-11-13 14: 21: 48Z
  3. @ Prasoon: Cevabın yarısını kesmenin CW dışı bir cevabın "adil düzenlemesi" olarak görüneceğini sanmıyorum.
    2010-11-13 14: 33: 27Z
  4. Ayrıca, bunu aşmış olabilirim, ancak kopyalama değerlendirme operatörünün herhangi bir şey yapmadan önce kimliğini kontrol etmesi gerektiğini söylemiyorsunuz.
    2010-11-13 14: 34: 15Z
  5. C ++ 11 için gönderinizi güncellerseniz çok iyi olurdu (örneğin, yapıcı /atamayı taşı)
    2012-09-13 03: 42: 17Z

Üç Kuralı C ++, temel olarak söyleyerek

  

Sınıfınızın herhangi birine ihtiyacı varsa

     
  • bir kopya oluşturucu ,
  •   
  • bir atama operatörü ,
  •   
  • veya bir yıkıcı ,
  •   

açıkça tanımlandıysa, bunların üçüne de ihtiyaç duyulması olasıdır.

Bunun nedeni, üçünün de genellikle bir kaynağı yönetmek için kullanılmasıdır ve sınıfınız bir kaynağı yönetiyorsa, genellikle kopyalamanın yanı sıra serbest bırakmayı da yönetmesi gerekir.

Sınıfınızın yönettiği kaynağı kopyalamak için iyi bir semantik yoksa, ilan ederek kopyalamayı yasaklamayı düşünün ( tanımlanması ) kopya yapıcı ve atama operatörünü private olarak tanımlamak.

(C ++ standardının yeni çıkacak sürümünün (C ++ 11 olan) C ++ kuralına hareket anlambilimi eklediğini ve bunun da Üç Kural Kuralı'nı değiştireceğini unutmayın. Bununla birlikte, C + yazmak için çok az şey biliyorum. Üçün Kuralı ile ilgili +11 bölümü.)

    
476
2017-05-23 12: 03: 09Z
  1. Kopyalamayı önlemek için başka bir çözüm, kopyalanamayan bir sınıftan (özel olarak) miras almaktır (boost::noncopyable gibi). Aynı zamanda çok daha net olabilir. C ++ 0x ve "silme" işlevlerinin burada yardımcı olabileceğini düşünüyorum, ancak sözdizimini unuttum: /
    2010-11-13 16: 33: 56Z
  2. @ Matthieu: Evet, bu da işe yarıyor. Ancak noncopyable std kütüphanesinin bir parçası değilse, onu bir iyileştirme olarak görmüyorum. (Silme sözdizimini unuttuysanız, bildiğim kadarıyla Ethan'ı unuttun. :))
    2010-11-13 17: 20: 28Z
  3. Üç Kural ve C ++ 1x ile ilgili herhangi bir güncelleme var mı?
    2013-08-30 07: 28: 18Z
  4. @ Daan: Bkz. bu cevabı . Ancak, Martinho adlı kullanıcının Sıfır Kuralı . Bana göre bu, son on yılda oluşturulan C ++ için en önemli kurallardan biri.
    2014-06-04 17: 46: 10Z
  5. Martinho'nun Sıfır Kuralı şimdi daha iyi (görünen adware devralma olmadan) archive.org
    2018-06-27 18: 31: 19Z

Büyük üçün yasası yukarıda belirtildiği gibidir.

Düz İngilizce dilinde, çözdüğü türden bir sorun örneği:

Varsayılan yıkıcı değil

Yapıcınıza bellek tahsis ettiniz ve bu yüzden silmek için bir yıkıcı yazmanız gerekiyor. Aksi takdirde, bellek sızıntısına neden olursunuz.

Bunun bir iş olduğunu düşünebilirsiniz.

Sorun, nesnenizden bir kopya çıkarılmışsa, kopya orijinal nesneyle aynı hafızayı gösterecektir.

Bir kere, bunlardan biri yıkıcıdaki hafızayı silerken, diğeri geçersiz kılmaya işaret eder (bu, sarkan bir işaretçi olarak adlandırılır) kullanmaya çalıştığında işler kıllı hale gelir.

Bu nedenle, yeni nesnelere yok etmek için kendi bellek parçalarını tahsis edecek şekilde bir kopya kurucu yazarsınız.

Atama operatörü ve kopya oluşturucu

Yapıcınızdaki hafızayı, sınıfınızın üye bir göstergesine ayırdınız Bu sınıfın nesnesini kopyaladığınızda, varsayılan atama operatörü ve copy yapıcısı bu üye işaretçisinin değerini yeni nesneye kopyalar.

Bu, yeni nesnenin ve eski nesnenin aynı hafıza parçasını göstereceği anlamına gelir, bu yüzden bir nesnede değiştirdiğinizde diğer nesne için de değişecektir. Bir nesne bu hafızayı silerse, diğeri onu kullanmayı denemeye devam eder - eek.

Bunu çözmek için, kopya kurucu ve atama operatörünün kendi sürümünü yazarsınız. Sürümleriniz yeni nesnelere ayrı bir bellek ayırır ve ilk işaretçinin adres yerine işaret ettiği değerleri kopyalayın.

    
151
2018-01-09 18: 27: 49Z
  1. Öyleyse, eğer bir kopya kurucu kullanıyorsak, o zaman kopya yapılır ancak tamamen farklı bir hafıza konumunda ve kopya kurucu kullanmazsak kopya yapılır, ancak Aynı hafıza yeri. Söylemeye çalıştığın şey bu mu? Bu nedenle, kopya kurucusuz bir kopya, yeni bir işaretçinin orada olacağı ancak aynı hafıza konumuna işaret edeceği anlamına gelir, ancak kullanıcı tarafından açıkça tanımlanmış bir kopya kurucumuz varsa, o zaman farklı bir hafıza konumuna işaret eden fakat verilere sahip olan ayrı bir göstericimiz olacaktır. /div>
    2015-01-04 14: 01: 58Z
  2. Üzgünüm, bu yıllar önce cevapladım, ancak cevabım hala burada görünmüyor :-( Temel olarak, evet - anladınız :-)
    2016-07-27 13: 35: 53Z
  3. Prensip kopya atama operatörüne nasıl yarar sağlar? Üçün Kuralında yer alan 3. maddeden bahsedilirse, bu cevap daha faydalı olacaktır.
    2017-10-31 17: 44: 01Z
  4. @ DBedrenko, "yeni bir nesneye kendi bellek parçalarını ayırması için bir kopya yapıcısı yazıyorsunuz ...", bu kopya atamasını genişleten aynı ilkedir Şebeke. Bunu netleştirdiğimi mi sanıyorsun?
    2017-11-02 09: 07: 10Z
  5. @ DBedrenko, daha fazla bilgi ekledim. Bu daha mı anlaşılır hale getiriyor?
    2017-11-02 12: 07: 51Z

Temel olarak bir yıkıcınız varsa (o varsayılan yıkıcı) Bu, tanımladığınız sınıfın bir miktar bellek ayırmaya sahip olduğu anlamına gelir. Sınıfın bazı müşteri kodları veya sizin tarafınızdan dışında kullanıldığını varsayalım.

 
    MyClass x(a, b);
    MyClass y(c, d);
    x = y; // This is a shallow copy if assignment operator is not provided

Eğer MyClass sadece bazı ilkel tiplenmiş üyelere sahipse, varsayılan bir atama operatörü çalışır, ancak bazı işaretçi üyelerine ve atama operatörlerine sahip olmayan nesneleri varsa, sonuç tahmin edilemez olur. Bu nedenle, bir sınıfın yıkıcısında silinecek bir şey varsa, derin bir kopya operatörüne ihtiyaç duyabileceğimizi söyleyebiliriz, bu da bir kopya oluşturucu ve atama operatörü sağlamamız gerektiği anlamına gelir.

    
42
2015-09-11 11: 39: 15Z

Bir nesneyi kopyalamanın anlamı nedir? Nesneleri kopyalamanın birkaç yolu vardır - en çok atıfta bulunacağınız 2 tür hakkında konuşalım - derin kopya ve sığ kopya.

Nesne yönelimli bir dilde olduğumuzdan (veya en azından öyle olduğunu varsayıyorsak), ayrılan bir belleğiniz olduğunu varsayalım. OO dili olduğu için, tahsis ettiğimiz bellek parçalarına kolaylıkla başvurabiliriz, çünkü bunlar genellikle ilkel değişkenler (ints, chars, bytes) ya da kendi türlerimizden ve ilkellerden oluşan tanımlanmış sınıflardır. Öyleyse şöyle bir Car sınıfımız olduğunu söyleyelim:

 
class Car //A very simple class just to demonstrate what these definitions mean.
//It's pseudocode C++/Javaish, I assume strings do not need to be allocated.
{
private String sPrintColor;
private String sModel;
private String sMake;

public changePaint(String newColor)
{
   this.sPrintColor = newColor;
}

public Car(String model, String make, String color) //Constructor
{
   this.sPrintColor = color;
   this.sModel = model;
   this.sMake = make;
}

public ~Car() //Destructor
{
//Because we did not create any custom types, we aren't adding more code.
//Anytime your object goes out of scope / program collects garbage / etc. this guy gets called + all other related destructors.
//Since we did not use anything but strings, we have nothing additional to handle.
//The assumption is being made that the 3 strings will be handled by string's destructor and that it is being called automatically--if this were not the case you would need to do it here.
}

public Car(const Car &other) // Copy Constructor
{
   this.sPrintColor = other.sPrintColor;
   this.sModel = other.sModel;
   this.sMake = other.sMake;
}
public Car &operator =(const Car &other) // Assignment Operator
{
   if(this != &other)
   {
      this.sPrintColor = other.sPrintColor;
      this.sModel = other.sModel;
      this.sMake = other.sMake;
   }
   return *this;
}

}

Derin bir kopya, eğer bir nesneyi bildirirsek ve o nesnenin tamamen ayrı bir kopyasını yaratırsak ... 2 nesne ile 2 tamamen hafıza setine giriyoruz.

 
Car car1 = new Car("mustang", "ford", "red");
Car car2 = car1; //Call the copy constructor
car2.changePaint("green");
//car2 is now green but car1 is still red.

Şimdi tuhaf bir şey yapalım. Diyelim ki araba2 yanlış programlanmış veya bilerek araba1'in yapıldığı gerçek hafızayı paylaşma amaçlı. (Bunu yapmak genellikle bir hatadır ve sınıflarda genellikle altında tartışılan bir battaniyektir.) Car2 hakkında her ne zaman sorsanız, gerçekten car1'in hafıza alanını gösteren bir işaretçi çözüyorsunuz. olduğu.

 
//Shallow copy example
//Assume we're in C++ because it's standard behavior is to shallow copy objects if you do not have a constructor written for an operation.
//Now let's assume I do not have any code for the assignment or copy operations like I do above...with those now gone, C++ will use the default.

 Car car1 = new Car("ford", "mustang", "red"); 
 Car car2 = car1; 
 car2.changePaint("green");//car1 is also now green 
 delete car2;/*I get rid of my car which is also really your car...I told C++ to resolve 
 the address of where car2 exists and delete the memory...which is also
 the memory associated with your car.*/
 car1.changePaint("red");/*program will likely crash because this area is
 no longer allocated to the program.*/

Bu nedenle, hangi dilde yazdığınıza bakılmaksızın, çoğu zaman derin bir kopya istediğiniz için nesneleri kopyalarken ne demek istediğinizi çok dikkatli olun.

Kopya yapıcısı ve kopya ataması işleci nedir? Onları çoktan yukarıda kullandım. Kopya oluşturucu, Car car2 = car1; gibi bir kod yazdığınızda çağrılır. Esasen bir değişkeni bildirir ve tek bir satıra atarsanız, kopya oluşturucu çağrılır. Atama operatörü, eşit bir işaret kullandığınızda gerçekleşen şeydir - car2 = car1;. Uyarı car2 aynı beyanda bulunmamıştır. Bu işlemler için yazdığınız iki kod parçası çok benzer. Aslında, tipik tasarım deseni, ilk kopya /atama meşru olduğunda memnun kaldığınızda her şeyi ayarlamak için çağırdığınız başka bir işleve sahiptir - yazdığım longhand koduna bakarsanız, işlevler neredeyse aynıdır.

Onları ne zaman kendim ilan etmem gerekir? Eğer paylaşılacak veya bir şekilde üretim için kod yazmıyorsanız, gerçekten sadece ihtiyacınız olduğunda bunları bildirmeniz gerekir. Eğer 'kazayla' kullanmayı seçtiyseniz ve birini yapmadıysanız, program dilinizin ne yaptığının farkında olmanız gerekir. derleyici varsayılanını alırsınız. Örneğin nadiren kopya kurucuları kullanıyorum, ancak atama operatörü geçersiz kılmaları çok yaygın. Ekleme, çıkarma vb. Öğelerinin ne anlama geldiğini geçersiz kılabileceğinizi biliyor muydunuz?

Nesnelerimin kopyalanmasını nasıl önleyebilirim? Özel bir işlevle nesneniz için bellek ayırmanıza izin verilen tüm yolları geçersiz kılmak makul bir başlangıçtır. İnsanların kopyalarını gerçekten istemiyorsanız, herkese açık hale getirebilir ve bir istisna atarak ve ayrıca nesneyi kopyalayarak programcıyı uyarabilirsiniz.

    
35
2013-12-12 13: 09: 19Z
  1. Soru C ++ olarak etiketlendi. Bu sözde kod açıklama, en iyi şekilde iyi tanımlanmış "Üç Kural" hakkında herhangi bir şeyi açıklığa kavuşturmak için çok az şey yapar ve en kötüsü karışıklığa yol açar.
    2014-06-11 22: 55: 37Z
  

Onları ne zaman kendim ilan etmem gerekir?

Üç Kural, herhangi birisini ilan ederseniz,

  1. yapıcıyı kopyala
  2. kopya ataması işleci
  3. yıkıcı

o zaman üçünü de beyan etmelisin. Bir kopyalama işleminin anlamını devralma ihtiyacının neredeyse her zaman sınıftan kaynaklandığı gözleminden doğdu.bir çeşit kaynak yönetimini geliştirmek, ve bu neredeyse her zaman şunu ima ediyordu:

  • Bir kopya işleminde hangi kaynak yönetimi yapılsa, diğer kopya işleminde büyük olasılıkla yapılması gerekiyor ve

  • sınıf yıkıcısı, kaynağın yönetimine de katılacak (genellikle serbest bırakacak). Yönetilmesi gereken klasik kaynak bellekti ve bu yüzden tüm Standart Kütüphaneler belleği yönet (örneğin, dinamik bellek yönetimi yapan STL kapsayıcıları) tümü “büyük üç” olarak tanımlıyor: hem kopyalama işlemleri hem de bir yıkıcı.

Üç Kural'ın bir sonucu , kullanıcı tarafından ilan edilen bir yıkıcının bulunmasının, basit üye bilge kopyaların sınıftaki kopyalama işlemleri için uygun olma ihtimalinin düşük olduğunu göstermesidir. Buna göre, eğer bir sınıf yıkıcı ilan ederse, kopya işlemlerinin muhtemelen otomatik olarak oluşturulmaması gerektiğini, çünkü doğru olanı yapmayacaklarını söylüyor. C ++ 98'in kabul edildiği zaman, bu akıl yürütme sırasının önemi tam olarak anlaşılmamıştır, bu nedenle C ++ 98'de bir kullanıcının imha edici olduğunu beyan eden, derleyicilerin kopya işlemleri oluşturma isteğini etkilememiştir. C ++ 11'de durum böyle olmaya devam ediyor, ancak yalnızca kopya işlemlerinin üretildiği koşulları kısıtlamak çok eski kurallara aykırı olacağı için.

  

Nesnelerimin kopyalanmasını nasıl önleyebilirim?

Kopya yapıcıyı bildir & atama operatörünü özel erişim belirteci olarak kopyala.

 
class MemoryBlock
{
public:

//code here

private:
MemoryBlock(const MemoryBlock& other)
{
   cout<<"copy constructor"<<endl;
}

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other)
{
 return *this;
}
};

int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}

C ++ 11 ve sonrası sürümlerinde kopya yapıcısını & atama operatörü silindi

 
class MemoryBlock
{
public:
MemoryBlock(const MemoryBlock& other) = delete

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other) =delete
};


int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}
    
24
2016-01-12 10: 19: 01Z

Mevcut cevapların çoğu zaten kopya kurucuya, atama operatörüne ve yıkıcıya dokunuyor. Ancak, C ++ 11 sonrası, semantik hareketin tanıtımı bunu 3'ün ötesine genişletebilir.

Son zamanlarda Michael Claisse bu konuya değinen bir konuşma yaptı: http://channel9.msdn.com/events /CPP /CPP-Con-2014 /-Kanonik sınıfı

    
14
2015-01-07 05: 38: 51Z

C ++ 'da üçün kuralı, aşağıdaki üye işlevlerinden birinde net bir tanım varsa, programcının diğer iki üye işlevini birlikte tanımlaması gereken, üç gereksinimin tasarım ve tasarımının temel ilkesidir. Yani aşağıdaki üç üye işlevi vazgeçilmezdir: yıkıcı, kopya kurucu, kopya atama işleci.

C ++ 'ta kopya yapıcı özel bir kurucu. Mevcut bir nesnenin kopyasına eşdeğer yeni nesne olan yeni bir nesne oluşturmak için kullanılır.

Kopyalama ataması işleci, genellikle aynı türdeki nesnelere başkaları için varolan bir nesneyi belirtmek için kullanılan özel bir atama işlecidir.

Hızlı örnekler var:

 
// default constructor
My_Class a;

// copy constructor
My_Class b(a);

// copy constructor
My_Class c = a;

// copy assignment operator
b = a;
    
9
2016-10-16 04: 57: 08Z
  1. Merhaba, cevabınız yeni bir şey eklemiyor. Diğerleri konuyu daha derinlemesine ve daha doğru bir şekilde ele alıyor - cevabınız yaklaşık ve aslında bazı yerlerde yanlıştır (yani burada "zorunluluk" yoktur; bu "büyük olasılıkla" olmalıdır). Zaten iyice cevaplanmış sorulara bu tür bir cevabı gönderirken gerçekten değmezdi. Ekleyeceğiniz yeni şeyler olmadığı sürece.
    2014-08-15 16: 26: 02Z
  2. Ayrıca, iki ile ilgili bir şekilde dört hızlı örnek vardır Üç Kural'ın bahsettiği üç . Çok fazla karışıklık.
    2014-11-03 15: 41: 53Z
kaynak yerleştirildi İşte