Soru Üçüncüsü Nedir?


  • Nedir bir nesneyi kopyalama anlamına gelmek?
  • Neler var kopya kurucu ve kopya atama operatörü?
  • Onları ne zaman kendime bildirmeliyim?
  • Nesnelerin kopyalanmasını nasıl engelleyebilirim?

1844
2017-11-13 13:27


Menşei


Lütfen okumak bu bütün konu ve c++-faq wiki etiketi kapatmaya oy vermeden önce. - sbi
@Binary: En azından yorum tartışmalarını okumak için zaman ayırın önce oy verirsin. Metin çok daha basitdi, ama Fred'in üzerine genişletmesi istendi. Ayrıca, bu dört soru olsa da dilbilgisiBu gerçekten birkaç yönüyle sadece bir sorudur. (Eğer buna katılmıyorsanız, bu soruların her birine kendi başına cevap vererek POV'unuzu kanıtlayın ve sonuçlara oy verelim.) - sbi
Fred, işte C ++ 1x ile ilgili cevabınıza ilginç bir katkı: stackoverflow.com/questions/4782757/.... Bununla nasıl baş edebiliriz? - sbi
İlgili: Büyük İki Yasa - Nemanja Trifunovic
C ++ 11'den itibaren bunun beş kuralına veya bunun gibi bir şeye yükseltildiğini unutmayın. - paxdiablo


Cevaplar:


Giriş

C ++, kullanıcı tanımlı türlerdeki değişkenleri ele alır. değer semantiği. Bu, nesnelerin çeşitli bağlamlarda örtülü olarak kopyalandığı anlamına gelir, ve "bir nesneyi kopyalamanın" aslında ne anlama geldiğini anlamalıyız.

Basit bir örneği 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?
}

(Eğer şaşkınsanız name(name), age(age) Bölüm, buna a denir üye başlatıcı listesi.)

Özel üye fonksiyonları

Kopyalamak ne demektir person nesne? main işlev, iki ayrı kopyalama senaryosu gösterir. Başlatma person b(a); tarafından yapılır kopya kurucu. İşi, mevcut bir nesnenin durumuna dayanarak yeni bir nesne inşa etmektir. Görev b = a tarafından yapılır kopya atama operatörü. İşi genellikle biraz daha karmaşıktır. çünkü hedef nesne zaten ele alınması gereken bazı geçerli durumda.

Ne kopya kurucuyu ne de görevlendiren işletmeyi (ne de yıkıcıyı) kendimiz ilan etmediğimizden, bunlar bizim için örtülü olarak tanımlanmıştır. Standarttan alıntı:

[...] kopya kurucu ve kopya atama operatörü, [...] ve destructor özel üye işlevleridir.   [ Not: Uygulama, bu üye işlevlerini dolaylı olarak ilan edecektir.   Program, bunları açıkça bildirmediğinde bazı sınıf türleri için.   Uygulama, kullanıldıklarında örtülü olarak tanımlayacaktır. [...] bitiş notu ]   [n3126.pdf bölüm 12 §1]

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

Bir sendika dışı sınıf X için örtülü olarak tanımlanmış kopya oluşturucu, alt nesnelerinin bir üyeliğini kopyalar.   [n3126.pdf bölüm 12.8 §16]

Bir sendika dışı sınıf X için örtülü olarak tanımlanan kopya atama operatörü, üye olarak kopyalama ataması gerçekleştirir   Alt Nesnelerinin   [n3126.pdf bölüm 12.8 §30]

Örtülü tanımlar

Örtülü olarak tanımlanan özel üye işlevi person Bunun gibi:

// 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()
{
}

Üye olarak kopyalama, bu durumda tam olarak istediğimiz şeydir: name ve age kopyalanır, bu yüzden bağımsız, bağımsız person nesne. Gizli olarak tanımlanmış yıkıcı daima boştur. Bu durum, bu durumda, kurucuda herhangi bir kaynak almadığımız için de geçerlidir. Üyelerin imhacıları örtülü olarak person yıkıcı bitti:

Yıkıcının cesedini çalıştırdıktan ve vücut içinde tahsis edilen herhangi bir otomatik nesneyi yok ettikten sonra,   X sınıfı için bir yıkıcı X'in doğrudan [...] üyeleri için yıkıcıları çağırır   [n3126.pdf 12.4 §6]

Kaynakları yönetme

Peki bu özel üye işlevlerini ne zaman açık bir şekilde ilan etmeliyiz? Sınıfımız ne zaman bir kaynağı yönetir, yani, sınıfın bir nesnesi olduğunda sorumluluk sahibi Bu kaynak için. Bu genellikle kaynak demektir Edinilen kurucuda (veya kurucuya geçirilir) ve yayınlandı yıkıcıda.

Standart C ++ 'ya geri dönelim. Diye bir şey yoktu std::stringve programcılar işaretçilere aşıktı. person sınıf şöyle olabilirdi:

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 dersler yazıyor ve başı belaya giriyor: "Bir kişiyi bir vektöre ittim ve şimdi çılgın hafıza hatalarım var!" Varsayılan olarak, bir nesneyi kopyalamak, üyelerini kopyalamak anlamına gelir. ama kopyalama name üye sadece bir işaretçi kopyalar değil işaret ettiği karakter dizisi! Bunun birkaç hoş olmayan etkisi var:

  1. Üzerinden değişiklikler a üzerinden görülebilir b.
  2. bir Zamanlar b yok edildi, a.name sarkan bir işaretçidir.
  3. Eğer a sarkan işaretçi verim silme, yok edilir tanımlanmamış davranış.
  4. Atama neyi hesaba katmıyorsa name görevden önce işaret etti, er ya da geç her yerde hafıza sızıntısı olacak.

Açık tanımlar

Üye olarak kopyalama istenen etkiye sahip olmadığı için, karakter dizisinin derin kopyalarını yapmak için kopya kurucusu ve kopya 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: atamadan önce eski durumu yıkmalıyız name bellek sızıntılarını önlemek için. Ayrıca, formun kendiliğinden atanmasına karşı da korunmalıyız x = x. Bu kontrol olmadan delete[] name içeren diziyi siler kaynak dize, çünkü yazarken x = x, her ikisi de this->name ve that.name aynı işaretçiyi içerir.

İstisna güvenliği

Ne yazık ki, bu çözüm başarısız olur new char[...] Bellek tükenmesi nedeniyle bir istisna atar. Bir olası çö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 olmaksızın kendini atamayı da ele alır. Bu soruna daha sağlam bir çözüm kopyala ve takas deyim, ama burada istisna güvenliğinin ayrıntılarına girmeyeceğim. Sadece şu noktayı yapmak için istisnalardan bahsetmiştim: Kaynakları yöneten sınıflar yazmak zordur.

Hesaplı olmayan kaynaklar

Dosya tanıtıcıları veya muteksler gibi bazı kaynaklar kopyalanamaz veya kopyalanamaz. Bu durumda, kopya kurucuyu ve kopya atama operatörünü private bir tanım vermeden:

private:

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

Alternatif olarak, boost::noncopyable veya silinmiş olarak bildirin (C ++ 0x):

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. (Birden çok kaynağı tek bir sınıfta yönetmeyin, Bu sadece acıya yol açacaktır.) Bu durumda, üçlü kural:

Eğer ya yıkıcıyı açıkça ilan etmeniz gerekiyorsa,   kurucu veya kopya atama operatörünü kendiniz kopyalayın,   Muhtemelen her üçünü açıkça belirtmeniz gerekir.

(Ne yazık ki, bu "kural" C ++ standardı veya farkında olduğum herhangi bir derleyici tarafından zorlanmaz.)

Tavsiye

Çoğu zaman, bir kaynağı kendiniz yönetmeniz gerekmez. çünkü mevcut bir sınıf gibi std::string zaten sizin için yapar. Sadece kullanarak basit kodu karşılaştırın std::string üye bir kullanarak kıvrımlı ve hata eğilimli alternatif char* ve ikna olmalısınız. İşlenmemiş işaretçilerden uzak kaldığınız sürece, üçün kuralının kendi kodunuzu ilgilendirmesi olası değildir.


1517
2017-11-13 13:27



Fred, oy kullanma hakkımda kendimi daha iyi hissederim: (A) kötü yönetilen kodda kötü bir şekilde uygulanmış ödevi heceleyemezsiniz ve yanlış olduğunu söyleyen bir not ekledikten sonra paragrafta başka bir yere bakın; ya kodda c & s kullanın ya da sadece tüm bu üyeleri (B) uygulayarak atlayın; ilk yarıyı kısaltın; (C) hareket semantiğinin tanıtılmasını ve bunun RoT için ne anlama geldiğini tartışırsınız. - sbi
Ama sonra yazı C / W yapılmalıdır, bence. Ben terimleri çoğunlukla doğru tutmayı sevdim (yani siz diyorsunuz)kopya atama operatörü ", ve bu atama bir kopyasını ima olamazdı ortak tuzağa dokunmazsınız). - Johannes Schaub - litb
@Prasoon: Cevabın yarısını kesmenin CW dışı bir cevabın "adil kurgulanması" olarak görülmeyeceğini düşünüyorum. - sbi
Ayrıca, bunu aşmış olabilirim, ancak kopyalama işleminin operatörünün herhangi bir şey yapmadan önce kimliği kontrol etmesi gerektiğini söylemezsiniz. - Björn Pollex
Gönderinizi C ++ 11 için güncellerseniz harika olur (örn. Kurucu / görevlendirmeyi taşıyın) - Alexander Malakhov


Üç Kuralı temelde C ++ için bir kuraldır

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

  • bir kopya kurucu,
  • bir atama operatörü,
  • ya da çöp yakma fırını,

Açıkça tanımlanmış, o zaman ihtiyaç muhtemelen her üçü.

Bunun nedenleri, üçünün de genellikle bir kaynağı yönetmek için kullanılmalarıdır ve eğer sınıfınız bir kaynağı yönetiyorsa, genellikle kopyalamayı ve serbest bırakmayı yönetmelidir.

Sınıfınızın yöneteceği kaynağı kopyalamak için iyi bir semantik yoksa, o zaman bildirerek kopyalamayı yasaklamayı düşünün ( tanımlarken) kopya kurucusu ve atama operatörü olarak private.

(C ++ standardının gelecek olan yeni versiyonunun (C ++ 11 olan), C ++ 'ya, C ++' ya taşınacağını unutmayın, bu da Üç Kural Kuralı'nı değiştirecektir. Ancak, bu konuda C ++ 11 bölümünün yazılması için çok az şey biliyorum. Üç Kural hakkında.)


451
2017-11-13 14:22



Kopyalamayı önlemek için başka bir çözüm, kopyalanamayan bir sınıftan (özel olarak) miras almaktır ( boost::noncopyable). Ayrıca daha net olabilir. Ben C ++ 0x ve "silme" işlevlerinin burada yardımcı olabileceğini düşünüyorum, ama sözdizimini unuttum: / - Matthieu M.
@Matthieu: Evet, bu da işe yarıyor. Ama değilse noncopyable std lib'in bir parçası, bunu bir iyileştirme olarak görmüyorum. (Oh, ve eğer silme sözdizimini unutursasaydın, bildiğim mor ehanı unuttun. :)) - sbi
Üç ve C ++ 1x Kuralıyla ilgili herhangi bir güncelleme var mı? - Daan Timmer
@Daan: Gör bu cevap. Ancak, Martinho'ler Sıfır Kuralı. Bana göre, bu son on yılda C ++ için en önemli temel kurallardan biridir. - sbi
Martinho'nun Sıfır Kuralı şimdi İşte - Diego


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

Basit İngilizcede, çözdüğü türden bir problem:

Varsayılan olmayan yıkıcı

Hafızanı yapıcınıza tahsis ettiniz ve onu silmek için bir yıkıcı yazmanız gerekiyor. Aksi halde bir bellek sızıntısına neden olacaksınız.

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

Sorun, nesnenizin bir kopyası yapılırsa, kopya, orijinal nesne ile aynı belleğe işaret edecektir.

Bunlardan biri, bir kez onun yok edicideki hafızayı siler, diğeri, bir şey kullanmaya çalıştığında geçersiz bellek (buna sarkan bir işaretçi denir) gösterecektir.

Bu nedenle, bir kopya oluşturucu yazarsınız, böylece yeni nesnelere yok etmek için kendi bellek parçalarını tahsis eder.

Atama operatörü ve kopya kurucusu

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

Bu, yeni nesnenin ve eski nesnenin aynı bellek parçasına işaret edeceği anlamına gelir, böylece bir nesneyi değiştirdiğinizde, diğer obje için de değiştirilir. Bir nesne bu belleği silerse diğeri onu kullanmaya çalışmayı sürdürür - eek.

Bunu çözmek için, kopya oluşturucunun ve atama operatörünün kendi sürümünüzü yazabilirsiniz. Sürümleriniz, yeni nesnelere ayrı bellek ayırır ve ilk işaretçinin adresinden ziyade işaret ettiği değerlere kopyalayın.


134
2018-05-14 14:22



Yani bir kopya kurucuyu kullanırsak, kopya tamamen farklı bir bellek konumunda yapılır ve kopya oluşturucu kullanmazsak, kopyalama yapılır, ancak aynı bellek konumuna işaret eder. söylemeye çalıştığın şey bu mu? Bu nedenle, kopya yapıcısı olmayan bir kopya, yeni bir işaretçinin orada bulunacağını ancak aynı bellek konumuna işaret ettiğini, ancak kullanıcı tarafından açıkça tanımlanmış bir kopya yapıcımız varsa, farklı bir bellek konumuna işaret eden ancak verileri gösteren ayrı bir işaretçiye sahip olacağımız anlamına gelir. - Unbreakable
Üzgünüm, bu çağa cevap verdim ama cevabım hala burada görünmüyor :-( Temelde, evet - anladın :-) - Stefan
İlke, kopya atama işletmecisine nasıl katkıda bulunur? Üçlü Kuralı'ndaki 3. maddeden bahsedilerse, bu cevap daha yararlı olacaktır. - DBedrenko
@DBedrenko, "yeni bir nesneyi kendi bellek parçalarına ayıran bir kopya oluşturucu yazıyorsunuz ..." bu, kopya ataması operatörüne uzanan aynı prensiptir. Bunu açıklığa kavuşturduğumu düşünmüyor musun? - Stefan
@Stefan Evet, çok teşekkürler! - DBedrenko


Temel olarak bir yok ediciniz varsa (varsayılan yıkıcı değil), tanımladığınız sınıfın bazı bellek ayırmalarına sahip olduğu anlamına gelir. Sınıfın bazı istemci kodları veya sizin tarafınızdan dışarıda 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 yazılan üyelere sahipse, bir varsayılan görevlendirme operatörü işe yarardı, ancak bazı işaretçi üyeleri ve görevlendirme operatörü olmayan nesneler varsa, sonuç tahmin edilemez olurdu. Bu nedenle, bir sınıfın yıkıcısı içinde silinecek bir şey varsa, bir kopya kurucu ve görevlendirme operatörü sağlamamız gerektiği anlamına gelen bir derin kopya operatörüne ihtiyaç duyabileceğimizi söyleyebiliriz.


37
2017-12-31 19:29





Bir nesneyi kopyalamak ne anlama geliyor? Nesneleri kopyalamanın birkaç yolu vardır - büyük olasılıkla başvurduğunuz 2 türden bahsedelim - derin ve sığ kopya.

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

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 ilan edip, nesnenin tamamen ayrı bir kopyasını yaratırsak ... 2 nesnede 2 tamamen hafıza kümesinde sonuçlanır.

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 garip bir şey yapalım. Arabanın 2 yanlış programlanmış olduğunu ya da araba1'in yapıldığı gerçek hafızayı paylaşmayı amaçladığını varsayalım. (Bunu yapmak genellikle bir hatadır ve derslerde genellikle tartışılan bir battaniyedir.) Arabanın 2 hakkında ne zaman sorduğunu farz edersiniz, gerçekten araba1'in hafıza alanına bir işaretçiyi çözüyorsunuz ... olduğunu.

//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.*/

Yazdığınız dile bakılmaksızın, nesneleri kopyalamaya gelince ne demek istediğine çok dikkat edin, çünkü çoğu zaman derin bir kopya istersiniz.

Kopya oluşturucu ve kopya atama operatörü nedir? Onları çoktan kullanmıştım. Kod yazarken kopya kurucusu çağrılır. Car car2 = car1;  Aslında bir değişken bildirir ve bir satırda atarsanız, kopya kurucusu çağrıldığında budur. Atama operatörü, eşit bir işaret kullandığınızda ne olur?car2 = car1;. ihbar car2 aynı ifadede beyan edilmez. Bu işlemler için yazdığınız iki kod parçası çok benzerdir. Aslında, tipik tasarım deseni, ilk kopya / atama meşru olduktan sonra her şeyi ayarlamak için çağırdığınız başka bir işleve sahiptir - yazdığım longhand koduna bakarsanız, işlevler hemen hemen aynıdır.

Onları ne zaman kendime bildirmeliyim? Paylaşılacak ya da bir şekilde üretilecek kod yazmıyorsanız, yalnızca onlara ihtiyacınız olduğunda bunları bildirmeniz gerekir. Eğer 'kazayla' kullanmayı seçtiyseniz ve bir tane yapmadıysanız program dilinizin ne yaptığının farkında olmanız gerekir - yani. derleyiciyi varsayılan olarak alırsınız. Örneğin, nadiren kopya oluşturucuları kullanıyorum, ancak görev operatörü geçersiz kılmaları çok yaygındır. Ekleme, çıkarma, vb. Ne anlama geldiğini de bildiğinizi biliyor muydunuz?

Nesnelerin kopyalanmasını nasıl engelleyebilirim? Özel bir işlevle nesneyiniz için bellek ayırmanıza izin verdiğiniz tüm yolları geçersiz kılmak makul bir başlangıçtır. İnsanların bunları kopyalamasını gerçekten istemiyorsanız, bunu herkese açık hale getirebilir ve bir istisna atarak ve aynı zamanda nesneyi kopyalamayarak programcıyı uyarabilirsiniz.


27
2017-10-17 16:37



Soru C ++ olarak etiketlendi. Bu sözde kod açıklamasının, iyi tanımlanmış "Üçün Kuralı" ile ilgili her şeyi açıklığa kavuşturması çok azdır ve sadece karışıklığı en kötü şekilde yaymaktadır. - sehe


Onları ne zaman kendime bildirmeliyim?

Üç Kural, herhangi bir

  1. kopya kurucu
  2. kopya atama operatörü
  3. çöp yakma fırını

o zaman üçünü de ilan etmelisiniz. Bir kopya operasyonunun anlamını devralma ihtiyacının hemen hemen her zaman bir tür kaynak yönetimini gerçekleştiren sınıftan kaynaklandığının ve neredeyse her zaman bunun ima edildiği gözleminden doğmuştur.

  • Bir kopyalama işleminde hangi kaynak yönetiminin yapıldığı, muhtemelen diğer kopyalama işleminde yapılması gerekmiştir.

  • Sınıf yıkıcısı da kaynak yönetimine katılıyordu (genellikle serbest bırakıyordu). Yönetilecek klasik kaynak hafızaydı ve bu yüzden tüm Standart Kitaplık sınıfları budur. belleği yönetmek (ör. dinamik bellek yönetimini gerçekleştiren STL kapsayıcılar) hepsi “büyük üç” olarak nitelendirir: hem kopyalama işlemleri hem de bir yıkıcı.

Üç Kuralın bir sonucu Kullanıcı tarafından bildirilen bir yıkıcı varlığının, basit üye bilge kopyalarının, sınıftaki kopyalama işlemleri için uygun olmayacağını göstermesidir. Bu da, bir sınıf bir yıkıcı ilan ederse, kopyalama işlemlerinin muhtemelen otomatik olarak üretilmemesi gerektiğini çünkü doğru şeyi yapamayacağını öne sürmektedir. C ++ 98'in kabul edildiği tarihte, bu akıl yürütme çizgisinin önemi tam olarak anlaşılamamıştır, bu yüzden C ++ 98'de, kullanıcı tarafından tahsis edilmiş bir imha edicinin varlığının, derleyicilerin kopya operasyonları üretme konusundaki istekleri üzerinde hiçbir etkisi olmamıştır. Bu, C ++ 11'de geçerli olmaya devam ediyor, ancak sadece kopyalama işlemlerinin oluşturulduğu koşulların kısıtlanması çok fazla eski kod kırılacağından.

Nesnelerin kopyalanmasını nasıl engelleyebilirim?

Kopya oluşturucu ve kopya atama operatörünü özel erişim belirteci olarak bildirin.

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’den itibaren, kopyalama kurucusu ve atama operatörünün silinmiş olduğunu da bildirebilirsiniz.

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

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


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

19
2018-01-12 09:54





Mevcut cevapların çoğu zaten kopya oluşturucuya, görev operatörüne ve yıkıcıya dokunuyor. Ancak, C ++ 11 sonrası, hareket anlamının tanıtımı 3'ün ötesine geçebilir.

Son zamanlarda Michael Claisse bu konuya değinen bir konuşma yaptı: http://channel9.msdn.com/events/CPP/C-PP-Con-2014/The-Canonical-Class


9
2018-01-07 05:38





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

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

Kopyalama atama operatörü, genellikle varolan bir nesneyi aynı nesne türündeki başkalarına 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;

5
2017-08-12 04:27



Merhaba, cevabınız yeni bir şey eklemiyor. Diğerleri konuyu çok daha derinlerde ve daha doğru bir şekilde ele alır - cevabınız yaklaşık olarak doğrudur ve aslında bazı yerlerde yanlıştır (burada "zorunlu" yoktur, "çok büyük ihtimalle"). Bu tür bir yanıtı, zaten iyice yanıtlanmış olan sorulara yollarken gerçekten değmezsiniz. Eklenecek yeni şeyleriniz yoksa. - Mat
Ayrıca, dört hızlı örnekler bir şekilde ile ilgili iki arasında üç Üçüncüsü hakkında konuşuyor. Çok fazla kafa karışıklığı. - anatolyg