Bir "raw" işaretçisi yönetilmez. Yani, şu satır:
SomeKindOfObject *someKindOfObject = new SomeKindOfObject();
... eğer eşlik deleteetme uygun bir zamanda yapılmazsa, hafızaya sızacak.
auto_ptr
Bu davaları en aza indirmek std::auto_ptr<>için tanıtıldı. Bununla birlikte, 2011 standardından önceki C ++ sınırlamaları nedeniyle, auto_ptrbellek sızıntısı yapmak hala çok kolaydır . Ancak bunun gibi sınırlı durumlar için yeterlidir:
void func() {
std::auto_ptr<SomeKindOfObject> sKOO_ptr(new SomeKindOfObject());
// do some work
// will not leak if you do not copy sKOO_ptr.
}
En zayıf kullanım durumlarından biri konteynerlerdedir. Bunun nedeni, bir kopyanın bir kopyasının auto_ptr<>yapılması ve eski kopyanın dikkatlice sıfırlanmaması durumunda kabın işaretçiyi silip veri kaybedebilmesidir.
unique_ptr
Bunun yerine, C ++ 11 tanıtıldı std::unique_ptr<>:
void func2() {
std::unique_ptr<SomeKindofObject> sKOO_unique(new SomeKindOfObject());
func3(sKOO_unique); // now func3() owns the pointer and sKOO_unique is no longer valid
}
Bu tür bir unique_ptr<>fonksiyon, fonksiyonlar arasında geçilse bile doğru bir şekilde temizlenecektir. Bunu, işaretçinin "sahipliğini" anlamsal olarak göstererek yapar - "sahip" temizler. Bu, kaplarda kullanım için idealdir:
std::vector<std::unique_ptr<SomeKindofObject>> sKOO_vector();
Bunun aksine auto_ptr<>, unique_ptr<>burada iyi davranılmakta ve vectoryeniden boyutlandırıldığında, nesnelerden hiçbiri vectordestek deposunu kopyalarken yanlışlıkla silinmeyecektir .
shared_ptr ve weak_ptr
unique_ptr<>faydalı olduğundan emin olun, ancak kod tabanınızın iki bölümünün aynı nesneye başvurabilmesi ve işaretçiyi etrafından kopyalayabilmesini ancak yine de uygun temizliği garanti altına almasını istediğiniz durumlar vardır. Örneğin, bir ağaç kullanırken aşağıdaki gibi görünebilir std::shared_ptr<>:
template<class T>
struct Node {
T value;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
Bu durumda, bir kök düğümünün birden fazla kopyasına bile tutabiliriz ve kök düğümün tüm kopyaları yok edildiğinde ağaç uygun şekilde temizlenir.
Bu, her biri shared_ptr<>yalnızca nesneye ilişkin işaretçiyi değil shared_ptr<>, aynı işaretçiyi ifade eden tüm nesnelerin referans sayısını da tuttuğu için çalışır . Yeni bir tane oluşturulduğunda, sayı artar. Biri yok edildiğinde sayım düşüyor. Sayım sıfıra ulaştığında, işaretçi deleted.
Bu da bir problem ortaya koyuyor: Çift bağlantılı yapılar dairesel referanslarla sonuçlanıyor. Ağacımıza bir parentişaretçi eklemek istediğimizi söyleyin Node:
template<class T>
struct Node {
T value;
std::shared_ptr<Node<T>> parent;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
Şimdi, eğer a kaldırırsak Node, buna döngüsel bir referans var. Asla deleted olmayacak çünkü referans sayısı asla sıfır olmayacak.
Bu sorunu çözmek için, bir kullanın std::weak_ptr<>:
template<class T>
struct Node {
T value;
std::weak_ptr<Node<T>> parent;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
Şimdi, işler doğru şekilde çalışacak ve bir düğümün kaldırılması üst düğüme sıkışmış referanslar bırakmayacak. Ancak, ağacın yürümesini biraz daha karmaşık hale getirir:
std::shared_ptr<Node<T>> parent_of_this = node->parent.lock();
Bu yolla, düğüme bir referansı kilitleyebilirsiniz ve üzerinde çalıştığınız için üzerinde çalışırken kaybolacağınıza dair makul bir garantiniz vardır shared_ptr<>.
make_shared ve make_unique
Şimdi, bazı küçük problemler var shared_ptr<>ve çözülmesi unique_ptr<>gerekenler var. Aşağıdaki iki satırın bir sorunu var:
foo_unique(std::unique_ptr<SomeKindofObject>(new SomeKindOfObject()), thrower());
foo_shared(std::shared_ptr<SomeKindofObject>(new SomeKindOfObject()), thrower());
Bir thrower()istisna atarsanız, her iki satırda da bellek sızıntısı olur. Ve daha da fazlası, shared_ptr<>bu işaret ve bu uzak nesneden başvuru sayısı tutar olabilir ) ikinci tahsisi anlamına gelir. Bu genellikle arzu edilmez.
C ++ 11 sağlar std::make_shared<>()ve C ++ 14 std::make_unique<>()bu sorunu çözmeyi sağlar :
foo_unique(std::make_unique<SomeKindofObject>(), thrower());
foo_shared(std::make_shared<SomeKindofObject>(), thrower());
Şimdi, her iki durumda da, thrower()bir istisna atsa bile , bir bellek sızıntısı olmayacak. Bir bonus olarak, size bir istisna güvenlik garantisi verirken, hem hızlı hem de birkaç byte'lık hafıza tasarrufu sağlayabilen, yönetilen nesnesiyle aynı hafıza alanındamake_shared<>() referans sayısını yaratma şansına sahip !
Qt hakkında notlar
Bununla birlikte, C ++ 11 öncesi derleyicileri desteklemesi gereken Qt'nin kendi çöp toplama modeline QObjectsahip olduğu belirtilmelidir : Birçoğunun , kullanıcının deletekendilerine ihtiyaç duymadan düzgün bir şekilde imha edileceği bir mekanizma vardır.
QObjectC ++ 11 işaretçiler tarafından yönetildiğinde nasıl davranacağını bilmiyorum , bu yüzden shared_ptr<QDialog>bunun iyi bir fikir olduğunu söyleyemem . Qt konusunda kesin olarak söyleyecek kadar tecrübem yok, ancak Qt5'in bu kullanım için ayarlanmış olduğuna inanıyorum .