12 Eylül 2019 Perşembe

Copy Constructor

Copy constructor imzası her zaman reference almalıdır
Parametre const olabilir veya olmayabilir.
C(const C& c){...}
C(C& c){...}
Bu copy constructor değildir.
C(C c){...}
Template Constructor ve Copy Constructor
Elimizde şöyle bir kod olsun. Bu koddaki son satır copy constructor yerine template constructor'ı çalıştıyor. Çünkü non-const nesne'yi const yapmak maliyetli, template constructor ise direkt çağrılabiliyor.
#include <iostream>

struct uct
{
  uct() { std::cerr << "default" << std::endl; }

  uct(const uct &) { std::cerr << "copy" << std::endl; }
  uct(      uct&&) { std::cerr << "move" << std::endl; }

  uct(const int  &) { std::cerr << "int" << std::endl; }
  uct(      int &&) { std::cerr << "int" << std::endl; }

  template <typename T>
  uct(T &&) { std::cerr << "template" << std::endl; }
};

int main()
{
    uct u1    ; // default
    uct u2( 5); // int
    uct u3(u1); // template, why?
}
Copy Constructor ve Assignment Operator Farkı
Copy constructor ve assignment operatör aynı gibi görünse de birisi yeni nesne oluştururken çağrılır. Diğeri ise mevcut nesne için çağrılır. Aşağıdaki örneklerde yeni bir nesne yaratılıyor
f(T o) {
...}

T object;
f(object); // copy construction to pass the argument
T object2(object); // construct a T from another T
Bu örneklerlerde ise mevcut nesne güncelleniyor.
T object;
...
T object2;
...
object = object2;
Assignment ile Yaratma
Bir nesne diğer bir nesneye eşitlenerek yaratılırsa copy constructor çağırılır. Örnek:
class Foo
{
...
};

Foo f;
Foo g = f; // (*)
Assignment operatörünün çağrılıp çağrılmayacağını anlamanın en kolay yolu = işaretinden önce g değişkenin tanımlanıp tanımlanmamış olduğuna bakmak.

Copy Constructor Ne Zaman Üretilmez
Eğer alanlardan bir veya daha fazlasının copy constructor metodu etkinsizleştirişmiş ise üretilmez. Elimizde şöyle bir sınıf olsun.
class MyClass
{
public:
  MyClass(int ID) : ID(ID) { }
  std::ofstream outputstream;
  std::ifstream inputstream;
  std::mutex mymutex;
private:
  int ID;
};
Şöyle yaparsak derleyici hata verir.
std::vector<MyClass> MyVector;
//<-- Error C2280 'MyClass::MyClass(const MyClass &)': 
attempting to reference a deleted function
MyVector.push_back (MyClass(1)); 
Copy Constructor'ın Etkinsizleştirilmesi
Copy Constructor'ın Etkinsizleştirilmesi - Deleted Copy Constructor yazısına taşıdım.

Copy Constructor'ın Etkinsizleştirilmesi Ancak Move Constructor Kullanılması
Copy constructor etkinsizleştirilse bile move constructor kullanmaya devam etmek isteyebiliriz.
Örnek
Bu durumda şöyle yaparız.
C(C&&) = default;
C& operator=(C&&) = default;
Örnek
Şöyle yaparız
struct ExplicitCopy {

  // Implement as usual
  ExplicitCopy() = default;
  ExplicitCopy(ExplicitCopy &&) = default;
  ExplicitCopy &operator = (ExplicitCopy &&) = default;

  // Copying happens with an ADL call to this function
  friend ExplicitCopy copy(ExplicitCopy const &orig) {
    return orig;
  }

private:
  // Copy operations are private and can't be called accidentally
  ExplicitCopy(ExplicitCopy const &) = default;
  ExplicitCopy &operator = (ExplicitCopy const &) = default;
};
Şöyle yaparız.
ExplicitCopy ec;
ExplicitCopy ec2 = ec;       // Nope
ExplicitCopy ec3(ec);        // Nope
ExplicitCopy ec4 = copy(ec); // Yes

Bu duruma ne zaman ihtiyaç olur
Örnek 1 :
Bir nesne kopyalanmasın ancak temporary nesneleri de metodlardan dönebilelim istersek kullanırız. Yani şöyle yapmak istersek kullanırız.
C f() {
    return C();
}
Örnek 2 :
Nesnemizi geçici bir başka nesneden yaratmak istiyoruz.  A nesnesinin copy constructor metodu etkinsizleştirilmiş ancak move constructor metodu var.
class A
{
public:

  A(const A&) = delete;
  A (const A&&) = default;
 
};
Şöyle yapabiliriz.
auto a = A {};
veya
A a = A {};

Rule Of 3
Rule of 3 şudur
If you need to explicitly declare either the destructor, copy constructor or copy assignment operator yourself, you probably need to explicitly declare all three of them.
Eğer sınıf belleği silmek için destructor yazmaya ihtiyaç duyuyorsa, muhtemelen copy constructor ve assignment operator'ünü de yazmaya ihtiyaç duyar der.

Rule of 4 And A Half
Açıklaması şöyle
“The Rule of The Big Four (and a half)" states that if you implement one of
* The copy constructor
* The assignment operator
* The move constructor
* The destructor
* The swap function
then you must have a policy about the others.
Rule of 5
Rule of 5 yeni C++11 ile geldi. Rule of 3 kuralına ek olarak, move constructor ve move  assignment operator'ünü de yazmaya ihtiyaç olur der.

Bence kendi bellek alanını yöneten ve raw pointer kullanan sınıfların artık hiç kullanılmaması lazım.

Clone Örneği
Ata sınıfımızda şöyle bir metod olsun.
virtual Animal* clone () const = 0;
Kalıtan sınıflar şöyle yaparlar.
Rabbit* clone () const {
  return new Rabbit(*this);
}
Copy constructor list initialization gibi de çağırılabilir. Şöyle yaparız.
class Demo {
public:
  int value1;
  Demo(){}
  Demo(Demo& demo) {
    this->value1 = demo.value1;
  }
  Demo Clone() {
    return Demo {*this};
  }
};

Hiç yorum yok:

Yorum Gönder