constructor etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
constructor etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

11 Nisan 2019 Perşembe

Constructor İçinde Exception Fırlatmak

Giriş
Eğer bir nesnenin costructor metodu içinde exception atılırsa, o nesnenin destructor metodu çağrılmaz. Açıklaması şöyle.
C++11 15.2 Constructors and destructors (2)
An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (12.6.2) has completed execution and the destructor has not yet begun execution.
1. Destructor'ımın Çağrılmaması
Örnek
Elimizde şöyle bir kod olsun. Holder sınıfı ve bu sınıfın içinde yaşayan başka bir sınıf var.
class Object
{
public:
  Object() { std::cout << "Object() ctor" << std::endl; }
  ~Object() { std::cout << "~Object() dtor" << std::endl; }
};

class Holder
{

public:
  Holder() :myObjectP( new Object() )
  {
    throw std::exception();
  }
  ~Holder()
  {
    std::cout << "~Holder()" << std::endl;
    delete myObjectP;
  }
private:
  Object* myObjectP;

};
Bu sınıfı kullanmak için şöyle yaparız.
int main(int argc, char* argv[])
{
  try
  {
    std::cout << "begin try" << std::endl;
    Holder h;
  }
  catch ( ... )
  {
    std::cout << "begin catch" << std::endl;
  }
  return 0;
}
Holder nesnesi constructor metodu içinde exception attığı için destructor metodu çağrılmıyor. Dolayısıyla içindeki pointer Object nesnesi de silinmiyor. Çıktı olarak şunu alırız.
begin try
Object() ctor
begin catch
2. New Kullanırken Constructor İçinden Exception Fırlatmak
new memory leak'e sebep olmaz. Açıklaması şöyle.
Whenever the constructor of MyClass throws, new MyClass(); does not produce any memory leak since the storage allocated for MyClass is automatically freed by new when an exception is thrown.
Açıklaması şöyle.
If the new-expression creates an object or an array of objects of class type, access and ambiguity control are done for the allocation function, the deallocation function, and the constructor. If the new-expression creates an array of objects of class type, the destructor is potentially invoked.
Açıklaması şöyle.
If construction of any object in the buffer throws an exception, the previously constructed objects must be destructed. That requires an available destructor.
Örnek
Elimizde şöyle bir kod olsun. Bu kod memory leak'e sebep olmaz.
MyClass* MyClass::getNewMyClassInstance()
{
    return new MyClass();
}
Örnek
Elimizde şöyle bir kod olsun.
int counter;

class Destruct
{
public:
  Destruct()
  {
    if (counter++ > 5)
      throw counter;
  }

  ~Destruct()
  {
    std::cout << "Dtor called\n";
  }
};

int main()
{
  try
  {
    new Destruct[10];
  }
  catch (...){}
}
Çıktı şunu alırız. Burada 7 nesne yaratılıken exception fırlatılıyor. Fully constucted 6 nesne için destructor çağrılıyor.
Dtor called
Dtor called
Dtor called
Dtor called
Dtor called
Dtor called

12 Mart 2018 Pazartesi

C++ ile Constructor

buit-in tiplerin constructor metodu yoktur
C++'ta built-in tiplerin constructor metodları yoktur.
int a = 42 ve
int a;
a = 42 aynı şeydir. a tipinin constructor metodu olmadığı için a tipini tanımlayan ilk cümle bir şey çalıştırmaz.

constructor'dan virtual metod çağrılmaz
Genel kural olarak bir sınıfın contructor veya destructor metodlarında virtual metodlar çağrılmaz. 
Bu kural çağrılamaz demiyor, çağrılmasa daha iyi diyor. Standart  bu kuralı şöyle açıklıyor.
Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor or from a destructor, including during the construction or destruction of the class’s non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor’s or destructor’s class and not one overriding it in a more-derived class. If the virtual function call uses an explicit class member access (5.2.5) and the object expression refers to the complete object of x or one of that object’s base class subobjects but not x or one of its base class subobjects, the behavior is undefined. 
Constructor için virtual metod çağrısına parasoft şu uyarıyı verir.
"A class's virtual functions shall not be invoked from its destructor or any of its constructors (JSF-71.1-2)"
Eğer derleyicimiz durumu yakalarsa şu hatayı verir.
pure virtual ‘virtual int X::f()’ called from constructor
Bu kural Java için, geçerli olmasa da hataya açık kapı bıraktığı için, yapılmaması bence daha iyi. Örnek de hatalı durum görülebilir.

Örnek
virtual metod çağrılarının, kalıtım yapan sınıfları değil, kendisini veya alt sınıfı tetiklediği görülebilir.
struct V {
  virtual void f();
  virtual void g();
};
struct A : virtual V {
  virtual void f();
};
struct B : virtual V {
  virtual void g();
  B(V*, A*);
};
struct D : A, B {
  virtual void f();
  virtual void g();
  D() : B((A*)this, this) { }
};
B::B(V* v, A* a) {
  f(); // calls V::f, not A::f
  g(); // calls B::g, not D::g
  v->g(); // v is base of B, the call is well-defined, calls B::g
  a->f(); // undefined behavior, a’s type not a base of B
}
Örnek
Şu kod yanlış. Pure virtual bir metod çağrıldığı için uygulama çöker.
struct Base
{
    Base() { method(); }

    virtual void method() = 0;
};

struct Derived : Base
{
    void method() {};
};

int main()
{
    Derived d;
}
Örnek
Şu kod pure virtual metodu dolaylı çağırdığı için yanlış.
#include <iostream>

struct X {
    virtual void foo() = 0;
    void bar() { foo(); }
    X() { bar(); std::cout << "X"; }
};

struct Y : X {
    void foo() override {}
};


int main() {
    Y y;
    return 0;
}

constructor'ın adresi alınamaz
constructor ve destructor metodlarının adresleri alınamaz.
"References and pointers cannot be used on constructors and destructors because their addresses cannot be taken."
Örnek
Şöyle yaparız.

class Sample{
    private: int x;
    public: Sample() { x = 100; };
    public: void* member() { x = 200; };
};

template <class X>
void call_me(Sample s, X function){
    (s.*function)();
};

call_me(s, &Sample::member);   //valid
call_me(s, &Sample::Sample);   //invalid
call_me(s, &Sample::~Sample);  //invalid
Adresinin alınamamasındaki mantık, constructor'ın function pointer haline getirilmesinde bir fayda olmaması. Çünkü her constructor farklı bir imzaya sahip şeklinde açıklanmış.
ilklendirme sırası
Sınıfın değişkenleri constructor'da deklare edildikleri sırada ilklendirilirler. Eğer constructor içinde deklare edildikler sırada kullanılmazlarsa gcc aşağıdakine benzer bir hata verir.
Field1 will be initialized after Field2.

Örnek
Elimizde şöyle bir kod olsun. mSpeed mEntity'den daha önce ilklendirilir.
class Foo {
public:
    Foo(int speed) :
        mSpeed(speed),
        mEntity(speed)
    { }

private:
    int mSpeed;
    Entity mEntity;
}
delegating constructor
C++11 ile Delegating Constructor yazısına taşıdım.

constructor inheritance
C++11 ile Constructor Inheritance yazısına taşıdım.

constructor içinde exception
Constructor İçinde Exception Fırlatmak yazısına taşıdım.

constructor initializer list içinde exception
Ata sınıftan veya member variable'dan kaynaklanan exception'ı durdurmanın yolu yoktur.
#include <iostream>
using namespace std;
class A{
public:
    A() try : i{ 0 }{ throw 5; }
    catch (...){ cout << "Exception" << endl; }
private:
    int i;
};


int main(){
    A obj;
}