Üç Kuralı Nedir?

Nov 13 2010
  • Bir nesneyi kopyalamak ne anlama geliyor?
  • Nelerdir kopya yapıcı ve kopya atama operatörü ?
  • Bunları ne zaman kendim beyan etmem gerekir?
  • Nesnelerimin kopyalanmasını nasıl engelleyebilirim?

Yanıtlar

1852 fredoverflow Nov 13 2010 at 20:27

Giriş

C ++, kullanıcı tanımlı türlerin değişkenlerini değer semantiğiyle ele alır . Bu, nesnelerin çeşitli bağlamlarda örtük olarak kopyalandığı anlamına gelir ve "bir nesneyi kopyalamanın" gerçekte ne anlama geldiğini anlamamız gerekir.

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

( name(name), age(age)Kısmen şaşırırsanız , buna üye başlatıcı listesi denir .)

Özel üye işlevleri

Bir personnesneyi kopyalamak ne demektir ? mainFonksiyonu, iki farklı kopyalama senaryoları göstermektedir. Başlatma person b(a);, kopya oluşturucu tarafından gerçekleştirilir . Görevi, mevcut bir nesnenin durumuna göre yeni bir nesne oluşturmaktır. Atama b = a, kopya atama operatörü tarafından gerçekleştirilir . İşi genellikle biraz daha karmaşıktır, çünkü hedef nesne halihazırda ilgilenilmesi gereken geçerli bir durumdadır.

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

[...] kopya yapıcı ve kopya atama operatörü, [...] ve yıkıcı özel üye işlevleridir. [ Not : Uygulama, program bunları açıkça bildirmediğinde, bazı sınıf türleri için bu üye işlevleri dolaylı olarak bildirecektir. Uygulama, kullanılırlarsa bunları 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:

Bir birleşim dışı sınıf X için örtük olarak tanımlanmış kopya yapıcısı, alt nesnelerinin üyeler için bir kopyasını gerçekleştirir. [n3126.pdf bölüm 12.8 §16]

Birleşim dışı X sınıfı için örtük olarak tanımlanmış kopya atama operatörü, alt nesnelerinin üyelere göre kopya atamasını gerçekleştirir. [n3126.pdf bölüm 12.8 §30]

Örtülü tanımlar

Örtük olarak tanımlanmış özel üye işlevleri personşuna benzer:

// 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 bazlı kopyalama, bu durumda tam olarak istediğimiz şeydir: nameve agekopyalanır, böylece kendi kendine yeten, bağımsız bir personnesne elde ederiz . Örtülü olarak tanımlanan yıkıcı her zaman boştur. Yapıcıda herhangi bir kaynak elde etmediğimiz için bu durumda da sorun yok. personYıkıcı bittikten sonra üyelerin yıkıcıları örtük olarak çağrılır :

Yıkıcının gövdesini ç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

Öyleyse bu özel üye fonksiyonları ne zaman açıkça bildirmeliyiz? Sınıfımız bir kaynağı yönettiğinde , yani sınıfın bir nesnesi o kaynaktan sorumlu olduğunda. Bu genellikle kaynağın yapıcıda edinildiği (veya kurucuya aktarıldığı) ve yıkıcıda serbest bırakıldığı anlamına gelir .

Önceden standart C ++ 'ya geri dönelim. Diye bir şey yoktu std::stringve programcılar işaretçilere aşıktı. personSınıf bu benziyordu 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 dersler yazıyorlar ve başları belaya giriyor: " Bir kişiyi bir vektöre ittim ve şimdi çılgın hafıza hataları alıyorum! " Varsayılan olarak, bir nesneyi kopyalamak, üyelerini kopyalamak anlamına gelir, ancak nameüyeyi yalnızca kopyalamak anlamına gelir. işaret ettiği karakter dizisini değil , bir göstericiyi kopyalar ! Bunun birkaç hoş olmayan etkisi vardır:

  1. Üzerinden yapılan değişiklikler üzerinden agözlemlenebilir b.
  2. Bir kez byok edildiğinde, a.namesarkan bir işaretçi.
  3. aYok edilirse , sarkan işaretçinin silinmesi tanımsız davranışa neden olur .
  4. nameÖdev, görevden önce neyi işaret ettiğini hesaba katmadığı için, er ya da geç her yerde bellek sızıntıları yaşayacaksınız.

Açık tanımlar

Üye bazlı kopyalama istenen etkiye sahip olmadığından, karakter dizisinin derin kopyalarını yapmak için kopya yapıcıyı ve kopya atama operatörünü açıkça tanımlamamız gerekir:

// 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 farka dikkat edin: namebellek sızıntılarını önlemek için atamadan önce eski durumu yıkmalıyız. Ayrıca, formun kendi kendine atanmasına karşı korumalıyız x = x. O çek olmadan, delete[] nameiçeren diziyi silmek istiyorsunuz kaynak yazdığınız zaman, çünkü dize x = x, hem this->nameve that.nameaynı işaretçi içerir.

İstisnai güvenlik

Ne yazık ki, new char[...]bellek tükenmesi nedeniyle bir istisna atılırsa bu çözüm başarısız olacaktır . Olası bir çözüm, yerel bir değişken eklemek 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 aynı zamanda açık bir kontrol olmaksızın kendi kendine atamayı da halleder. Bu soruna daha da sağlam bir çözüm kopyala ve değiştir deyimidir , ancak burada istisna güvenliği ayrıntılarına girmeyeceğim. Sadece aşağıdaki noktayı belirtmek için istisnalardan bahsetmiştim: Kaynakları yöneten sınıflar yazmak zordur.

Kopyalanamayan kaynaklar

Dosya tanıtıcıları veya muteksler gibi bazı kaynaklar kopyalanamaz veya kopyalanmamalıdır. Bu durumda, kopya yapıcıyı ve kopya atama işlecini privatebir tanım vermeden ilan edin :

private:

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

Alternatif olarak, bunları devralabilir boost::noncopyableveya silinmiş olarak bildirebilirsiniz (C ++ 11 ve üzeri):

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. (Asla tek bir sınıfta birden fazla kaynağı yönetmeyin, bu yalnızca acıya yol açar.) Bu durumda, üç kuralı unutmayın :

Yıkıcı, kopya yapıcı veya kopya atama işlecini kendiniz açıkça bildirmeniz gerekiyorsa, muhtemelen üçünü de açık bir şekilde bildirmeniz gerekir.

(Ne yazık ki, bu "kural" C ++ standardı veya bildiğim herhangi bir derleyici tarafından uygulanmıyor.)

Beş kuralı

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

İmzalarla bir örnek:

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

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

Sıfır kuralı

3/5 kuralı 0/3/5 kuralı olarak da anılır. Kuralın sıfır bölümü, sınıfınızı oluştururken özel üye işlevlerinden hiçbirini yazmamanıza izin verildiğini belirtir.

Tavsiye

Çoğu zaman bir kaynağı kendiniz yönetmenize gerek yoktur, çünkü std::stringzaten sizin için olduğu gibi mevcut bir sınıf bunu yapmaktadır. Sadece bir std::stringüye kullanarak basit kodu, a kullanarak kıvrımlı ve hataya açık alternatifle karşılaştırın char*ve ikna olmalısınız. Ham işaretçi üyelerinden uzak durduğunuz sürece, üç kuralının kendi kodunuzla ilgili olması pek olası değildir.

521 sbi Nov 13 2010 at 21:22

Üç Kuralı C ++ için pratik bir kuraldır, temelde 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 bir şekilde tanımlanırsa, muhtemelen üçüne de ihtiyaç duyacaktır .

Bunun nedenleri, üçü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 kopyalamayı ve serbest bırakmayı yönetmesi gerekir.

Sınıfınızın yönettiği kaynağı kopyalamak için iyi bir anlambilim yoksa , kopyalama yapıcısını ve atama operatörünü olarak bildirerek ( tanımlayarak ) kopyalamayı yasaklamayı düşünün private.

(C ++ standardının (C ++ 11 olan) yakında çıkacak olan yeni sürümünün, C ++ 'ya hareket semantiğini ekleyeceğini ve bu durumun muhtemelen Üç Kuralını değiştireceğini unutmayın.Ancak, bunun hakkında bir C ++ 11 bölümü yazmak için çok az şey biliyorum Üç Kuralı hakkında.)

162 Stefan May 14 2012 at 21:22

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

Çözdüğü türden bir soruna sade İngilizce ile basit bir örnek:

Varsayılan olmayan yıkıcı

Yapıcınızda bellek ayırdınız ve bu nedenle 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, nesnenizin bir kopyası yapılırsa, kopya orijinal nesneyle aynı belleğe işaret edecektir.

Bunlardan biri yıkıcıdaki hafızayı sildiğinde, diğeri onu kullanmaya çalıştığında geçersiz hafızayı gösteren bir işaretçiye (buna sarkan bir işaretçi denir) sahip olacak.

Bu nedenle, yeni nesneleri yok etmek için kendi bellek parçalarını tahsis edecek bir kopya oluşturucu yazarsınız.

Atama operatörü ve kopya oluşturucu

Oluşturucunuzda, sınıfınızın bir üye göstericisine bellek ayırdınız. Bu sınıfın bir nesnesini kopyaladığınızda, varsayılan atama operatörü ve copy yapıcısı, bu üye işaretçisinin değerini yeni nesneye kopyalayacaktır.

Bu, yeni nesnenin ve eski nesnenin aynı bellek parçasına işaret edeceği anlamına gelir, böylece onu bir nesnede değiştirdiğinizde diğer nesne için de değiştirilir. Bir nesne bu belleği silerse, diğeri onu kullanmaya devam eder - eek.

Bunu çözmek için kopyalama yapıcısı ve atama işlecinin kendi sürümünü yazarsınız. Sürümleriniz yeni nesnelere ayrı bellek ayırır ve adres yerine ilk işaretçinin işaret ettiği değerler boyunca kopyalar.

45 fatma.ekici Jan 01 2013 at 02:29

Temel olarak, bir yıkıcınız varsa (varsayılan yıkıcı değil), bu, tanımladığınız sınıfın bir miktar bellek tahsisine sahip olduğu anlamına gelir. Sınıfın dışarıda bir müşteri kodu tarafından veya sizin tarafınızdan 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

MyClass yalnızca bazı ilkel tipte üyelere sahipse, varsayılan bir atama operatörü çalışacaktır, ancak atama operatörlerine sahip olmayan bazı işaretçi üyeleri ve nesneleri varsa, sonuç tahmin edilemez olacaktır. Bu nedenle, bir sınıfın yıkıcısında silinecek bir şey varsa, derin bir kopya operatörüne ihtiyacımız olabileceğini söyleyebiliriz, bu da bir kopya oluşturucu ve atama operatörü sağlamamız gerektiği anlamına gelir.

37 user1701047 Oct 17 2012 at 23:37

Bir nesneyi kopyalamak ne anlama geliyor? Nesneleri kopyalamanın birkaç yolu vardır - büyük olasılıkla bahsettiğiniz 2 tür hakkında konuşalım - derin kopya ve sığ kopya.

Nesne yönelimli bir dilde olduğumuz için (veya en azından öyle olduğunu varsayıyoruz), diyelim ki ayrılmış bir bellek parçanız var. Bir OO dili olduğu için, ayırdığımız bellek parçalarına kolayca başvurabiliriz çünkü bunlar genellikle ilkel değişkenler (ints, chars, bytes) veya kendi türlerimizden ve ilkellerimizden oluşan tanımladığımız sınıflardır. Diyelim ki aşağıdaki gibi bir Car 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, bir nesneyi açıklarsak ve ardından nesnenin tamamen ayrı bir kopyasını oluşturursak ... 2 tamamen bellek setinde 2 nesne ile 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 tuhaf bir şey yapalım. Diyelim ki araba2 ya yanlış programlanmış ya da kasıtlı olarak araba1'in yapıldığı gerçek belleği paylaşma amaçlı. (Bunu yapmak genellikle bir hatadır ve sınıflarda genellikle altında tartışılan örtüdür.) Araba2 hakkında ne zaman sorsan, gerçekten araba1'in hafıza alanına bir gösterici çözümlüyormuşsun gibi davran ... bu aşağı yukarı ne sığ bir kopya dır-dir.

//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, nesnelerin kopyalanması söz konusu olduğunda ne demek istediğinize çok dikkat edin çünkü çoğu zaman derin bir kopya istiyorsunuz.

Kopya oluşturucu ve kopya atama operatörü nedir? Bunları zaten yukarıda kullandım. Kopya yapıcısı, Car car2 = car1;Esasen bir değişken bildirir ve onu tek satırda atarsanız, kopya yapıcısı çağrıldığında olduğu gibi bir kod yazdığınızda çağrılır. Atama operatörü, eşittir işareti kullandığınızda olan şeydir car2 = car1;. Bildirim car2aynı ifadede beyan edilmemiştir. Bu işlemler için yazdığınız iki kod parçası büyük olasılıkla çok benzer. Aslında, tipik tasarım örüntüsünün, ilk kopyalama / atamanın yasal olduğundan emin olduğunuzda her şeyi ayarlamak için çağırdığınız başka bir işlevi vardır - yazdığım uzun el koda bakarsanız, işlevler neredeyse aynıdır.

Bunları ne zaman kendim beyan etmem gerekir? Eğer paylaşılacak ya da bir şekilde üretim için kod yazmıyorsanız, onları gerçekten sadece ihtiyacınız olduğunda bildirmeniz gerekir. Eğer 'kazara' kullanmayı seçerseniz ve bir tane yapmadıysanız, program dilinizin ne yaptığının farkında olmanız gerekir - yani derleyici varsayılanını alırsınız. Örneğin, kopya oluşturucuları nadiren kullanırım, ancak atama operatörü geçersiz kılmaları çok yaygındır. Toplama, çıkarma vb. Ne anlama geldiğini de geçersiz kılabileceğinizi biliyor muydunuz?

Nesnelerimin kopyalanmasını nasıl engelleyebilirim? Ö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 onları 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 Ajayyadav Jan 12 2016 at 16:54

Bunları ne zaman kendim beyan etmem gerekir?

Üç Kuralı, aşağıdakilerden herhangi birini beyan ederseniz,

  1. yapıcı kopyala
  2. kopya atama operatörü
  3. yıkıcı

o zaman üçünü de ilan etmelisiniz. Bir kopyalama işleminin anlamını devralma ihtiyacının neredeyse her zaman bir tür kaynak yönetimi gerçekleştiren sınıftan kaynaklandığı ve hemen hemen her zaman şunu ima ettiği gözleminden doğmuştur.

  • bir kopyalama işleminde ne yapılıyorsa, diğer kopyalama işleminde muhtemelen yapılması gereken kaynak yönetimi ve

  • sınıf yıkıcı da kaynağın yönetimine katılır (genellikle onu serbest bırakır). Yönetilecek klasik kaynak bellekti ve bu nedenle belleği yöneten tüm Standart Kitaplık sınıflarının (örneğin, dinamik bellek yönetimini gerçekleştiren STL konteynerleri) tümü "büyük üçü" ilan eder: hem kopyalama işlemleri hem de bir yıkıcı.

Üç Kuralı'nın bir sonucu, kullanıcı tarafından beyan edilen bir yıkıcının varlığının, basit üye bazlı kopyanın sınıftaki kopyalama işlemleri için uygun olmayacağını göstermesidir. Bu da, eğer bir sınıf bir yıkıcı ilan ederse, kopyalama işlemlerinin muhtemelen otomatik olarak üretilmemesi gerektiğini, çünkü doğru şeyi yapmayacaklarını gösteriyor. C ++ 98 kabul edildiğinde, bu akıl yürütme çizgisinin önemi tam olarak anlaşılmamıştı, bu nedenle C ++ 98'de, kullanıcı tarafından bildirilen bir yıkıcının varlığının, derleyicilerin kopyalama işlemleri oluşturma istekliliği üzerinde hiçbir etkisi yoktu. C ++ 11'de durum böyle olmaya devam ediyor, ancak yalnızca kopyalama işlemlerinin oluşturulduğu koşulları kısıtlamanın çok fazla eski kodu bozacağı için.

Nesnelerimin kopyalanmasını nasıl engelleyebilirim?

Kopya oluşturucuyu ve kopya atama operatörünü özel erişim belirleyicisi 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, kopya yapıcı ve atama operatörünün silindiğini de 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);
}
17 wei Jan 07 2015 at 12:38

Mevcut yanıtların çoğu zaten kopya yapıcıya, atama işlecine ve yıkıcıya dokunuyor. Bununla birlikte, C ++ 11 sonrası, hareket anlamsallığının tanıtımı bunu 3'ün ötesine genişletebilir.

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

11 MarcusThornton Aug 12 2014 at 11:27

C ++ 'da üç kuralı, aşağıdaki üye işlevlerinden birinde açık bir tanım varsa, programcının diğer iki üye işlevini birlikte tanımlaması gereken üç gereksinimin tasarımı ve geliştirilmesinin temel bir ilkesidir. Yani şu üç üye fonksiyon zorunludur: yıkıcı, kopya yapıcı, kopya atama operatörü.

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

Kopyalama atama operatörü, genellikle mevcut bir nesneyi aynı nesne türünden diğerlerine belirtmek için kullanılan özel bir atama operatörüdür.

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;