53
1 Nesne yaşam süreçleri Hatalar (exceptions) RAII yöntemi Akıllı göstergeler (smart pointers) Hata güvenliği (exception safety) C++ Hata Düzeneği Güvenliği (exception safety) Ali Çehreli [email protected]

C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

  • Upload
    others

  • View
    6

  • Download
    0

Embed Size (px)

Citation preview

Page 1: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

1

•Nesne yaşam süreçleri•Hatalar (exceptions)•RAII yöntemi•Akıllı göstergeler (smart pointers)•Hata güvenliği (exception safety)

C++ Hata Düzeneği Güvenliği(exception safety)

Ali Çehreli

[email protected]

Page 2: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

2

Kaynaklar

●Herb Sutter'ın Guru of the Week bilmeceleri (http://gotw.ca/gotw/)●Herb Sutter'ın GotW'lerin genişletilmiş hali olan kitabı “Exceptional C++” (Türkçesi: "Sıradışı C++")●boost.org akıllı göstergeleri●Andrei Alexandrescu'nun Loki kütüphanesinin davranışı ayarlanabilen (policy based) akıllı göstergeleri

Page 3: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

3

Nesne türleri ve yaşam süreçleri

Otomatik nesne: Yaşam süreci derleyici tarafından yönetilir; yerel nesneler, parametreler, sınıf ve yapı üyeleri, geçici nesneler

Dinamik nesne: Yaşam süreci programcı tarafından belirlenir; new ile başlar, delete ile biter

Statik nesne: Yaşam süreci derleyici tarafından yönetilir; bunlar konumuzun dışında

Page 4: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

4

Otomatik ve dinamik nesneler

void foo(){    A a0;

    D * d = new D;  // dinamik

    {        A a1;    }  // <­­ a1 burada sonlanır

} // <­­ a0 burada sonlanır

Page 5: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

5

Nesne kurulum adımları

1) Sanal (virtual) üst sınıfların kurulmaları2) Diğer üst sınıfların kurulmaları3) Üyelerin tanımlandıkları sırada ilklenmeleri4) Kurucu işlevin (constructor) işletilmesi

Page 6: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

6

Nesne sonlanma adımları

1) Sonlandırıcı işlevin (destructor) işletilmesi2) Üyelerin ve üst sınıfların kuruldukları sıranın tersinde sonlandırılmaları

Temel türlerin sonlandırıcıları "boştur". Örneğin int'ler özel bir son değer almazlar, göstergeler (işaretçi (pointer)) gösterdikleri nesneleri sonlandırmazlar, vs.

Page 7: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

7

Kurulum başarısız olduğunda...

1) O ana kadar kurulmuş olan nesneler ters sırada sonlandırılırlar2) Eğer nesne için new ile bellekten yer ayrılmışsa, o yer geri verilir

Kurulmakta olan nesnenin sonlandırıcı işlevi çağrılmaz

Page 8: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

   

Nesne kabul edilebilmek içintam olarak kurulmuş olmak gerekirclass BirSinif : /* ust siniflar */{    /* ... */

    BirSinif(/* ... */)

        : /* ust siniflarin ve uyelerin ilklenmeleri */

    {        /* kurulumun diger adimlari */

    }  // <­­ ancak simdi "nesne"};

Page 9: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

   

Hata düzeneğinin işleyişi

● Bir iş yapmaya çalışılırken (“try edilirken”)● hata atılabilir (“throw edilebilir”),● ve hata yakalanabilir (“catch edilebilir”).

Page 10: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

10

Bir try/catch örneğivoid bir_islev(){    try {        bir_is_yap();        isletilmesi_garanti_degil();

    } catch (const BirHata & hata) {

        /* hata durumu islemleri */    }}

void isletilmesi_garanti_degil(){    if (bir_kosul) {        throw BirHata();    }

    bunun_isletilmesi_de_garanti_degil();}

Page 11: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

   

Program yığıtının temizlenmesi(stack unwinding)

Hata yakalanana kadar çıkılan bütün kapsamlardaki bütün nesneler otomatik olarak sonlandırılırlar.

Page 12: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

12

Ancak tek hata atılı olabilir

Program yığıtının atılmış olan bir hata nedeniyle temizlenmesi sırasında yeni bir hata atılırsa program abort ile hemen sonlanır.

İlke: Sonlandırıcı işlevlerden hata sızmasına izin vermeyin.

Page 13: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

13

C'de güvenli kaynak yönetimiint bir_islev(Kaynak ** sonuc){    int hata = 0;        Kaynak * k0 = NULL;    Kaynak * k1 = NULL;

    hata = kaynak_ayir(&k0);    if (hata) goto cikis;

    hata = kaynak_ayir(&k1);    if (hata) goto cikis;        /* r0 ve r1 burada kullaniliyor olsun  */

    if (hata) goto cikis;

    /* sahipligi cagirana gecir */    *sonuc = k0;    k0 = NULL;

cikis:

    kaynak_geri_ver(&k1);    kaynak_geri_ver(&k0);

    return hata;}

int bar(Resource ** in_out){    int err = 0;        Resource * r0 = NULL;    Resource * r1 = NULL;

    err = allocate_resource(&r0);    if (err) goto finally;

    err = allocate_resource(&r1);    if (err) goto finally;    

    /* use r0 and r1  */

    if (err) goto finally;

    /* transfer ownership */    *in_out = r0;    r0 = NULL;

finally:

    deallocate_resource(&r1);    deallocate_resource(&r0);

    return err;}

Page 14: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

14

C'de güvenli kaynak yönetimi

int bir_islev(Kaynak ** sonuc){    int hata = 0;        Kaynak * k0 = NULL;    Kaynak * k1 = NULL;

    hata = kaynak_ayir(&k0);    if (hata) goto cikis;

    hata = kaynak_ayir(&k1);    if (hata) goto cikis;        /* r0 ve r1 burada kullaniliyor olsun  */

    if (hata) goto cikis;

    /* sahipligi cikis parametresine gecir */    *sonuc = k0;    k0 = NULL;

cikis:

    kaynak_geri_ver(&k1);    kaynak_geri_ver(&k0);

    return hata;}

int bir_islev(Kaynak ** sonuc){    int hata = 0;        Kaynak * k0 = NULL;    Kaynak * k1 = NULL;

    hata = kaynak_ayir(&k0);    if (hata) goto cikis;

    hata = kaynak_ayir(&k1);    if (hata) goto cikis;        /* k0 ve k1 burada kullaniliyor olsun  */

    if (hata) goto cikis;

    /* sahipligi cagirana gecir */    *sonuc = k0;    k0 = NULL;

cikis:

    kaynak_geri_ver(&k1);    kaynak_geri_ver(&k0);

    return hata;}

Page 15: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

15

C'de güvenli kaynak yönetimi

int bir_islev(Kaynak ** sonuc){    int hata = 0;        Kaynak * k0 = NULL;    Kaynak * k1 = NULL;

    hata = kaynak_ayir(&k0);    if (hata) goto cikis;

    hata = kaynak_ayir(&k1);    if (hata) goto cikis;        /* r0 ve r1 burada kullaniliyor olsun  */

    if (hata) goto cikis;

    /* sahipligi cikis parametresine gecir */    *sonuc = k0;    k0 = NULL;

cikis:

    kaynak_geri_ver(&k1);    kaynak_geri_ver(&k0);

    return hata;}

int bir_islev(Kaynak ** sonuc){    int hata = 0;        Kaynak * k0 = NULL;    Kaynak * k1 = NULL;

    hata = kaynak_ayir(&k0);    if (hata) goto cikis;

    hata = kaynak_ayir(&k1);    if (hata) goto cikis;        /* k0 ve k1 burada kullaniliyor olsun  */

    if (hata) goto cikis;

    /* sahipligi cagirana gecir */    *sonuc = k0;    k0 = NULL;

cikis:

    kaynak_geri_ver(&k1);    kaynak_geri_ver(&k0);

    return hata;}

Page 16: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

16

C++'da güvenli kaynak yönetimiKaynak bir_islev(){

        Kaynak k0(/* ... */);    Kaynak k1(/* ... */);

        /* k0 ve k1 burada kullaniliyor olsun  */

    /* sahipligi cagirana gecir */    return k0;

}

Page 17: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

   

Hata ne zaman atılmalı?

a) her hatada mı?

b) önemli hatalarda mı?

c) iş doğru olarak yapılamadığında mı?

Page 18: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

   

Hata ne zaman yakalanmalı?

Çok nadiren ve ancak o hata karşısında yapacak bir şey varsa

Page 19: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

19

Kaynağın açıkça geri verilmesi ...

void foo(){    Kaynak * k = ayir();    /* ... */    geri_ver(k);          // <­­ acikca}

Page 20: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

20

... C++'da işe yaramaz

void foo(){    Kaynak * k = ayir();    /* ... */            // <­­ hata atilabilir    geri_ver(k);         // <­­ isletilmeyebilir}

Page 21: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

21

RAII yöntemi(Resource Acquisition Is Initialization)

  geri verme işlemi sonlanmadır  geri verme işlemi sonlandırıcıya ait olmalıdır 

İlke: Kaynak kod içinde değil, o kaynaktan sorumlu olan sınıfın sonlandırıcı işlevinde geri verilmelidir

  geri verme işlemi, sonlanmadır  geri verme , 

Guideline: No explicit deallocation in code; deallocation happens in the destructor of a managing class.

  geri verme işlemi, sonlanmadır  geri verme , 

Guideline: No explicit deallocation in code; deallocation happens in the destructor of a managing class.

  geri verme işlemi, sonlanmadır  geri verme , 

Guideline: No explicit deallocation in code; deallocation happens in the destructor of a managing class.

  geri verme işlemi, sonlanmadır  geri verme işlemi sonlandırıcıya ait olmalıdr 

İlke: Kaynak kod içinde değil, o kaynaktan sorumlu sınıfın sonlandırıcı işlevinde geri verilir.

  geri verme işlemi, sonlanmadır  geri verme işlemi sonlandırıcıya ait olmalıdr 

İlke: Kaynak kod içinde değil, o kaynaktan sorumlu sınıfın sonlandırıcı işlevinde geri verilir.

Page 22: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

22

RAII örneği

void foo_umutlu(){    Kaynak * k = ayir();    /* ... */            // <­­ hata atilabilir    geri_ver(k);         // <­­ isletilmeyebilir}

void foo_RAII(){    KaynakSorumlusu k(ayir());    /* ... */  // <­­ hata atilabilir; sorun yok}

void foo_dogru(){    KaynakSorumlusu * k = ayir();    /* ... */            // <­­ hata atilabilir    geri_ver(k);         // <­­ isletilmeyebilir}

Page 23: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

23

Akıllı göstergeler

● Sorumlusu oldukları nesneleri delete ile sonlandıran RAII nesneleridir● Sıradan göstergeler kadar rahat kullanılırlar● Daha akıllı da olabilirler

Page 24: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

24

Bir akıllı gösterge gerçekleştirmesiclass AkilliGosterge{    BirTur * ptr;

public:

    explicit AkilliGosterge(BirTur * p = 0)        :        ptr(p)    {}

    ~AkilliGosterge()    {        delete ptr;    }

                            

        };

AkilliGosterge g(new BirTur());

    

Page 25: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

25

Bir akıllı gösterge gerçekleştirmesi

class AkilliGosterge{    BirTur * ptr;

public:

    explicit AkilliGosterge(BirTur * p = 0)        :        ptr(p)    {}

    ~AkilliGosterge()    {        delete ptr;    }

    BirTur * get()        const { return  ptr;      }        BirTur & operator* () const { return *ptr;      }        BirTur * operator­>() const { return  ptr;      }        bool     operator! () const { return  ptr == 0; }

private:

    AkilliGosterge(AkilliGosterge const &);    AkilliGosterge & operator= (AkilliGosterge const &);};

AkilliGosterge g(new BirTur());

foo(g.get());

BirTur & r = *g;r.i = 7;    g­>i = 42;

if (!g) {    /* ... */}

AkilliGosterge g1(g); // HATAg2 = g;               // HATA

class AkilliGosterge{    BirTur * ptr;

public:

    explicit AkilliGosterge(BirTur * p = 0)        :        ptr(p)    {}

    ~AkilliGosterge()    {        delete ptr;    }

    BirTur * get()        const { return  ptr;      }    

    

    

};

AkilliGosterge g(new BirTur());

foo(g.get());

Page 26: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

26

Bir akıllı gösterge gerçekleştirmesi

class AkilliGosterge{    BirTur * ptr;

public:

    explicit AkilliGosterge(BirTur * p = 0)        :        ptr(p)    {}

    ~AkilliGosterge()    {        delete ptr;    }

    BirTur * get()        const { return  ptr;      }        BirTur & operator* () const { return *ptr;      }        BirTur * operator­>() const { return  ptr;      }        bool     operator! () const { return  ptr == 0; }

private:

    AkilliGosterge(AkilliGosterge const &);    AkilliGosterge & operator= (AkilliGosterge const &);};

AkilliGosterge g(new BirTur());

foo(g.get());

BirTur & r = *g;r.i = 7;    g­>i = 42;

if (!g) {    /* ... */}

AkilliGosterge g1(g); // HATAg2 = g;               // HATA

class AkilliGosterge{    BirTur * ptr;

public:

    explicit AkilliGosterge(BirTur * p = 0)        :        ptr(p)    {}

    ~AkilliGosterge()    {        delete ptr;    }

    BirTur * get()        const { return  ptr;      }        BirTur & operator* () const { return *ptr;      }    

    

};

AkilliGosterge g(new BirTur());

foo(g.get());

BirTur & r = *g;r.i = 7;    

Page 27: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

27

Bir akıllı gösterge gerçekleştirmesi

class AkilliGosterge{    BirTur * ptr;

public:

    explicit AkilliGosterge(BirTur * p = 0)        :        ptr(p)    {}

    ~AkilliGosterge()    {        delete ptr;    }

    BirTur * get()        const { return  ptr;      }        BirTur & operator* () const { return *ptr;      }        BirTur * operator­>() const { return  ptr;      }        bool     operator! () const { return  ptr == 0; }

private:

    AkilliGosterge(AkilliGosterge const &);    AkilliGosterge & operator= (AkilliGosterge const &);};

AkilliGosterge g(new BirTur());

foo(g.get());

BirTur & r = *g;r.i = 7;    g­>i = 42;

if (!g) {    /* ... */}

AkilliGosterge g1(g); // HATAg2 = g;               // HATA

class AkilliGosterge{    BirTur * ptr;

public:

    explicit AkilliGosterge(BirTur * p = 0)        :        ptr(p)    {}

    ~AkilliGosterge()    {        delete ptr;    }

    BirTur * get()        const { return  ptr;      }        BirTur & operator* () const { return *ptr;      }        BirTur * operator­>() const { return  ptr;      }    

};

AkilliGosterge g(new BirTur());

foo(g.get());

BirTur & r = *g;r.i = 7;    g­>i = 42;

Page 28: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

28

Bir akıllı gösterge gerçekleştirmesi

class AkilliGosterge{    BirTur * ptr;

public:

    explicit AkilliGosterge(BirTur * p = 0)        :        ptr(p)    {}

    ~AkilliGosterge()    {        delete ptr;    }

    BirTur * get()        const { return  ptr;      }        BirTur & operator* () const { return *ptr;      }        BirTur * operator­>() const { return  ptr;      }        bool     operator! () const { return  ptr == 0; }

private:

    AkilliGosterge(AkilliGosterge const &);    AkilliGosterge & operator= (AkilliGosterge const &);};

AkilliGosterge g(new BirTur());

foo(g.get());

BirTur & r = *g;r.i = 7;    g­>i = 42;

if (!g) {    /* ... */}

AkilliGosterge g1(g); // HATAg2 = g;               // HATA

class AkilliGosterge{    BirTur * ptr;

public:

    explicit AkilliGosterge(BirTur * p = 0)        :        ptr(p)    {}

    ~AkilliGosterge()    {        delete ptr;    }

    BirTur * get()        const { return  ptr;      }        BirTur & operator* () const { return *ptr;      }        BirTur * operator­>() const { return  ptr;      }        bool     operator! () const { return  ptr == 0; }

};

AkilliGosterge g(new BirTur());

foo(g.get());

BirTur & r = *g;r.i = 7;    g­>i = 42;

if (!g) {    /* ... */}

Page 29: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

29

Bir akıllı gösterge gerçekleştirmesiclass AkilliGosterge{    BirTur * ptr;

public:

    explicit AkilliGosterge(BirTur * p = 0)        :        ptr(p)    {}

    ~AkilliGosterge()    {        delete ptr;    }

    BirTur * get()        const { return  ptr;      }        BirTur & operator* () const { return *ptr;      }        BirTur * operator­>() const { return  ptr;      }        bool     operator! () const { return  ptr == 0; }

private:

    AkilliGosterge(AkilliGosterge const &);    AkilliGosterge & operator= (AkilliGosterge const &);};

AkilliGosterge g(new BirTur());

foo(g.get());

BirTur & r = *g;r.i = 7;    g­>i = 42;

if (!g) {    /* ... */}

AkilliGosterge g1(g); // HATAg2 = g;               // HATA

class AkilliGosterge{    BirTur * ptr;

public:

    explicit AkilliGosterge(BirTur * p = 0)        :        ptr(p)    {}

    ~AkilliGosterge()    {        delete ptr;    }

    BirTur * get()        const { return  ptr;      }        MyType & operator* () const { return *ptr;      }        MyType * operator­>() const { return  ptr;      }        bool     operator! () const { return  ptr == 0; }

private:

    AkilliGosterge(AkilliGosterge const &);    AkilliGosterge & operator= (AkilliGosterge const &);};

class SmartPointer{    MyType * ptr;

public:

    explicit SmartPointer(MyType * p = 0)        :        ptr(p)    {}

    ~SmartPointer()    {        delete ptr;    }

    MyType * get()        const { return  ptr;      }        MyType & operator* () const { return *ptr;      }        MyType * operator­>() const { return  ptr;      }        bool     operator! () const { return  ptr == 0; }

private:

    SmartPointer(SmartPointer const &);    SmartPointer & operator= (SmartPointer const &);};

Page 30: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

30

boost::scoped_ptr

#include <boost/scoped_ptr.hpp>

typedef boost::scoped_ptr<Hayvan> HayvanPtr;

void foo(){    HayvanPtr hayvan(new Kedi);

    hayvan­>oyna();

    /* ... */

} // Kedi burada sonlanir

Page 31: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

31

boost::shared_ptr(paylaşımlı sahiplik)

Page 32: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

32

boost::weak_ptr(gözlemci)

Page 33: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

33

Nesne gider, boost::weak_ptr kalır

Page 34: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

34

boost::intrusive_ptr(daha hızlı paylaşımlı sahiplik)

●  Türün daha hızlı bir sayma düzeneği bulunmalı●  Harcanan bellek yalnızca bir T* kadardır●  İki tane yardımcı işlev tanımlanarak kullanılır:

void intrusive_ptr_add_ref(T *);

void intrusive_ptr_release(T *);

Page 35: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

35

boost::scoped_array

● boost::scoped_ptr gibidir ama delete[]'i çağırır● Onun yerine std::vector<BirAkilliGosterge> de düşünülebilir

Page 36: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

36

boost::shared_array

● boost::shared_ptr gibidir ama delete[]'i çağırır● Onun yerine std::vector<BirAkilliGosterge> de düşünülebilir

Page 37: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

37

std::auto_ptr(aslında std::transfer_ptr da denebilirmiş)

● Nesne belirli bir anda en fazla tek auto_ptr tarafından sahiplenilir● auto_ptr kopyalandığında veya atandığında sahiplik el değiştirir: hedef yeni sahip haline gelir, kaynak "NULL olur"● standart topluluklarla (örneğin vector) kullanılamaz

Page 38: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

38

C++ Technical Report 1, Boost'taki akıllı göstergelerin bazılarını standart kütüphaneye 

ekledi

● std::tr1::shared_ptr● std::tr1::weak_ptr● başka?

Page 39: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

39

boost::ptr_vector

● Barındırdığı göstergelerin eriştirdiği nesnelere de sahiptir● vector içindeki göstergeler için teker teker delete'i çağırır

Page 40: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

40

Hata güvenliğinde iki önemli tarih

● 1994: Tom Cargill'in "Exception Handling: A False Sense of Security" makalesinde C++ camiasını uyarması: "Atılabilecek hatalar karşısında tam güvenli olarak işleyen bir Stack şablonu yazılamaz"● 1997: İlk doğru çözümün Herb Sutter'ın    comp.lang.c++.moderated haber grubunda başlattığı    Guru of the Week'lerin 8 numaralı konusunda çıkması

Page 41: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

41

Tom Cargill'in iddiası: T'nin kopyalayıcısının hata atma olasılığı varsa, Stack::pop() tam güvenli olarak yazılamaz

template <class T>class Stack{    int adet_;    T * elemanlar_;        /* ... */

public:        /* Elemani en tepeye ekler */    void push(const T &);

    /* En tepedekini dOndUrUr */    T pop()    {        /* ... */

        T tepedeki = elemanlar_[adet_ ­ 1];        ­­adet_;        return tepedeki;    }};

Page 42: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

42

Güvenli bir Stack arayüzütemplate <class T>class Stack{    int adet_;    T * elemanlar_;        /* ... */

public:        void push(const T &);

    /* Tepedekine erisim saglar */    T & top()     {        return stack_[adet_ ­ 1];    }

    void pop()    {        ­­adet_;    }};

İlke: Arayüzleri hatalara karşı güvenli olacak şekilde tasarlayın

Page 43: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

43

Üç işlevler kuralı

Eğer●  sonlandırıcı (destructor)●  kopyalayıcı (copy constructor)●  atama işleci (operator=)üçlüsünden birisini tanımlamanız gerekmişse, hemen hemen her durumda diğerlerini de en azından tanımsız olarak bildirmeniz gerekir.

Page 44: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

44

class UmutluSorumlu : private boost::noncopyable{    Bir * bir_;    Iki * iki_;

public:

    UmutluSorumlu()        :        bir_(new Bir()),        iki_(new Iki())    {        /* kurulumun geri kalani */    }

    ~UmutluSorumlu()    {        delete iki_;        delete bir_;    }};

Görünürde güvenlikBu sınıf güvenli midir?

Page 45: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

45

Görünürde güvenlikGüvenli değil!

class UmutluSorumlu : private boost::noncopyable{    Bir * bir_;    Iki * iki_;

public:

    UmutluSorumlu()        :        bir_(new Bir()),        iki_(new Iki()) <­ 1: new hata atabilir, <­ 2: Iki() hata atabilir    {        /* kurulumun geri kalani */    <­ 3: baska hata atilabilir    }

    ~UmutluSorumlu()    {        delete iki_;        delete bir_;    }};

class WishfulManager : private boost::noncopyable{    One * one_;    Two * two_;

public:

    WishfulManager()        :        one_(new One()),        two_(new Two())   <­ 1: new may throw, <­ 2: Two() may throw    {        /* further construction */ <­ 3: something else may throw    }

    ~WishfulManager()    {        delete two_;        delete one_;    }};

İlke: Tek sahibin tek nesnesi olsun

Page 46: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

46

Şimdi her sahibin tek nesnesi varBu sefer güvenli mi?

class SupheliSorumlu{    BirSorumlusu bir_;    // Bu nesnelerin kopyalama ve atama islemlerinin    IkiSorumlusu iki_;    // guvenli olduklarini varsayalim                          // (ornegin std::string)

public:

    SupheliSorumlu()        :        bir_(new Bir()),        iki_(new Iki())    {        /* kurulumun geri kalani */    }

    // Sonlandirici isleve artik gerek yok...

};

Page 47: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

47

Eğer üyelerin atama işlemleri "normal" ise,bu sınıf güvenli değildir.

class SupheliSorumlu{    std::string bir_;    std::string iki_;

public:

    SupheliSorumlu()        :        bir_(“bir”),        iki_(“iki”)    {        /* kurulumun geri kalani */    }

    // Sonlandirici isleve artik gerek yok...

// Bu sinif, yarim atanmis durumda kalabilir: iki_'nin atanma islemi// sirasinda hata atilirsa; bir_ degismistir, iki_ eski degerinde kalir};

Page 48: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

48

Tek işlev kuralı

Eğer sınıfın birden fazla üyesi varsa,

● atama işleci (operator=)

hemen hemen her durumda en azından tanımsız olarak bildirilmelidir.

Eğer sınıfın birden fazla üyesi varsa,

● atama işleci (operator=)

private.

Eğer sınıfın birden fazla üyesi varsa,

● atama işleci (operator=)

private.

Eğer sınıfın birden fazla üyesi varsa,

● atama işleci (operator=)

private.

Eğer sınıfın birden fazla üyesi varsa,

● atama işleci (operator=)

private.

Eğer sınıfın birden fazla üyesi varsa,

● atama işleci (operator=)

private.

Eğer sınıfın birden fazla üyesi varsa,

● atama işleci (operator=)

private.

Eğer sınıfın birden fazla üyesi varsa,

● atama işleci (operator=)

private.

Eğer sınıfın birden fazla üyesi varsa,

● atama işleci (operator=)

private.

Page 49: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

49

operator= işlecinin bozuk tanımı

class Sinif{

/* ... */

    Sinif & operator= (const Sinif & sagdaki)    {        if (this != &sagdaki) {            // 1) bu nesneyi sonlandir            // 2) sagdakinden kopyala <­­ hata atabilir!        }

        return *this;    }};

Page 50: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

50

operator= işlecinin modern tanımıclass GuvenliSorumlu{    BirPtr bir_;    IkiPtr iki_;    string uc_;

/* ... */

    GuvenliSorumlu & operator= (const GuvenliSorumlu & sagdaki)    {        GuvenliSorumlu gecici(sagdaki);   // 1) Once kopyalamayi dene        this­>swap(temp);                 // 2) Sonra degistir

        return *this;    }                     // <­­ Eski durum bu noktada sonlanir

    // Degis tokus eder; Hata atmaz!    void swap(GuvenliSorumlu & sagdaki)    {        bir_.swap(sagdaki.bir_);        iki_.swap(sagdaki.iki_);        uc_.swap(sagdaki.uc_);    }};

Page 51: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

51

Hata güvenliği garantileri

Temel garanti: Kaynak sızıntısı yok ve nesneler kullanılabilir (tutarlı ama kestirilemez) durumdalarTam garanti: Programın durumunda hiçbir değişiklik yokHata sızdırmama garantisi: Ne kendisi atar, ne çağırdığı işlevler atar (örneğin std::swap)

Page 52: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

52

Özet: Hata güvenliği ilkeleri

●Sonlandırıcı işlevlerden hata sızdırmayın●Kaynakları kod içinde açıkça geri vermeyin (RAII)●Tek sahibin tek nesnesi olsun●Hata atabilecek işleri önceye alın; değişikleri ondan sonra yapın●Hata güvenliği sonraya bırakılamaz●Bir çok işi birden yapmayın (top() ve pop() gibi)

Page 53: C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri •Hatalar (exceptions) •RAII yöntemi •Akıllı göstergeler (smart pointers) •Hata

53

Hatırlatma: Akıllı göstergelerboost::scoped_ptr: basit kaynak sorumlusu; kopyalanamaz

boost::shared_ptr: paylaşımlı sahiplik (reference counted)

boost::weak_ptr: sahipliğe karışmaz; shared_ptr gözlemcisidir

std::auto_ptr: sahipliği devreder

vs...