C++ Hata Düzeneği Güvenliğiacehreli.org/hata_duzenegi.pdf · 1 •Nesne yaşam süreçleri...

Preview:

Citation preview

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

acehreli@yahoo.com

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

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

4

Otomatik ve dinamik nesneler

void foo(){    A a0;

    D * d = new D;  // dinamik

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

} // <­­ a0 burada sonlanır

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

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.

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

   

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

    BirSinif(/* ... */)

        : /* ust siniflarin ve uyelerin ilklenmeleri */

    {        /* kurulumun diger adimlari */

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

   

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”).

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();}

   

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.

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.

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

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

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

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;

}

   

Hata ne zaman atılmalı?

a) her hatada mı?

b) önemli hatalarda mı?

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

   

Hata ne zaman yakalanmalı?

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

19

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

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

20

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

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

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.

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}

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

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());

    

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());

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;    

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;

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

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 &);};

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

31

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

32

boost::weak_ptr(gözlemci)

33

Nesne gider, boost::weak_ptr kalır

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 *);

35

boost::scoped_array

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

36

boost::shared_array

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

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

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?

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

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ı

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

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

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.

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?

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

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

};

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

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.

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

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_);    }};

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)

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)

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

Recommended