Bir "raw" işaretçisi yönetilmez. Yani, şu satır:
SomeKindOfObject *someKindOfObject = new SomeKindOfObject();
... eğer eşlik delete
etme 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_ptr
bellek 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 vector
yeniden boyutlandırıldığında, nesnelerden hiçbiri vector
destek 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 delete
d.
Bu da bir problem ortaya koyuyor: Çift bağlantılı yapılar dairesel referanslarla sonuçlanıyor. Ağacımıza bir parent
iş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 delete
d 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 QObject
sahip olduğu belirtilmelidir : Birçoğunun , kullanıcının delete
kendilerine ihtiyaç duymadan düzgün bir şekilde imha edileceği bir mekanizma vardır.
QObject
C ++ 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 .