Qt'de bellek yönetimi?


97

Qt konusunda oldukça yeniyim ve bellek yönetimi ve nesnelerin ömrü ile ilgili bazı temel şeyleri merak ediyorum. Nesnelerimi ne zaman silmem ve / veya yok etmem gerekir? Bunlardan herhangi biri otomatik olarak işleniyor mu?

Aşağıdaki örnekte, oluşturduğum nesnelerden hangisini silmem gerekiyor? Örnek değişkene ne olur myOtherClassne zaman myClassyok edilir? Nesnelerimi hiç silmezsem (veya yok etmezsem) ne olur? Bu hafızada bir sorun olur mu?

Sınıfım.h

class MyClass
{

public:
    MyClass();
    ~MyClass();
    MyOtherClass *myOtherClass;
};

Sınıfım.cpp

MyClass::MyClass() {
    myOtherClass = new MyOtherClass();

    MyOtherClass myOtherClass2;

    QString myString = "Hello";
}

Gördüğünüz gibi, bu oldukça yeni başlayanlar için kolay bir şey ama bunu kolay bir şekilde nereden öğrenebilirim?

Yanıtlar:


100

QObjectS ile kendi hiyerarşinizi oluşturursanız , yani yeni oluşturulmuş tüm s'leri QObjectbir ebeveyn ile başlatırsanız ,

QObject* parent = new QObject();
QObject* child = new QObject(parent);

o zaman için yeterlidir , çünkü s yıkıcı tahrip ilgilenir . (Bunu sinyalleri yayınlayarak yapar, böylece üst öğeden önce manuel olarak silseniz bile güvenlidir .)deleteparentparentchildchild

Önce çocuğu da silebilirsiniz, sıra önemli değildir. Sıranın önemli olduğu bir örnek için nesne ağaçlarıyla ilgili dokümantasyon burada .

Eğer MyClassçocuğunuz değilse QObject, işleri yapmak için basit C ++ yolunu kullanmanız gerekir.

Ayrıca, QObjects'nin üst-alt hiyerarşisinin genellikle C ++ sınıf hiyerarşisi / miras ağacının hiyerarşisinden bağımsız olduğuna dikkat edin . Bu, atanan bir çocuğun, ebeveyninin doğrudan bir alt sınıfı olması gerekmediği anlamına gelir . Herhangi bir (alt sınıfı) QObjectyeterli olacaktır.

İnşaatçılar tarafından başka nedenlerle dayatılan bazı kısıtlamalar olabilir, ancak; Örneğin QWidget(QWidget* parent=0), QWidgetgörünürlük bayrakları nedeniyle ve bazı temel düzeni bu şekilde yaptığınız için ebeveynin başka olması gereken yerde gibi ; ancak Qt'nin hiyerarşi sistemi için genel QObjectolarak ebeveyn olarak herhangi birine sahip olmanıza izin verilir .


21
(It does this by issuing signals, so it is safe even when you delete child manually before the parent.)-> Güvenli olmasının nedeni bu değil. Qt 4.7.4'te, QObject çocukları doğrudan silinir (aracılığıyla delete, bkz. Qobject.cpp, satır 1955). Öncelikle alt nesneleri silmenin güvenli olmasının nedeni, bir QObject'in ebeveynine silindiğinde onu unutmasını söylemesidir.
Martin Hennings

5
Bunun gerçek olması için soyundan gelen yıkıcıların sanal olduğundan emin olmanız gerektiğini ekleyeceğim. Eğer ClassBdevralır QObjectve ClassCdevralır ClassBsonra, ClassCyalnızca düzgün QT'ın ebeveyn-çocuk ilişkisine tarafından yok olsun eğer ClassBbireyin yıkıcı sanal olduğunu.
Phlucious

1
Cevaptaki bağlantı artık kesildi (yaklaşık 4 yıl sonra şaşırtıcı değil ...), belki de böyle bir şey qt-project.org/doc/qt-4.8/objecttrees.html ?
PeterSW

2
@Phlucious QObject'in yıkıcısı zaten sanaldır, bu da her alt sınıfın yıkıcısını otomatik olarak sanal hale getirir.
rubenvb

1
Miras ağacının herhangi bir yerindeki bir sınıfın sanal bir yıkıcısı varsa, aşağıdaki her alt sınıfın sanal bir yıkıcısı olacaktır. Şimdi, sanal yıkıcı olmayan bu tür bir sanal yıkıcı zincirinin dışında bir yaprak ebeveyn sınıfı varsa, gerçek nesne o zincirin daha aşağısında bir yerde olduğunda belirli bir sınıfa bir işaretçiyi silerseniz sorun yaşayabileceğinizi düşünüyorum. Bir QObject alt sınıfı olması ve bu alt sınıfın bir örneğine bir QObject işaretçisinin silinmesi durumunda, bu alt sınıfın yıkıcı bildirimindeki virtual anahtar sözcüğünü unutsanız bile hiçbir zaman bir sorun olmaz.
rubenvb

47

Qt'de sahiplik kavramının çok önemli olduğuna işaret ederek Debilski'nin cevabını genişletmek istiyorum. A sınıfı, B sınıfının sahipliğini üstlendiğinde, A sınıfı silindiğinde B sınıfı silinir. Sadece bir nesne oluşturduğunuzda ve onun üstünü belirlediğinizde değil, bir nesnenin diğerinin sahibi olduğu birkaç durum vardır.

Örneğin:

QVBoxLayout* layout = new QVBoxLayout;
QPushButton someButton = new QPushButton; // No owner specified.
layout->addWidget(someButton); // someButton still has no owner.
QWidget* widget = new QWidget;
widget->setLayout(layout); // someButton is "re-parented".
                           // widget now owns someButton.

Başka bir örnek:

QMainWindow* window = new QMainWindow;
QWidget* widget = new QWidget; //widget has no owner
window->setCentralWidget(widget); //widget is now owned by window.

Bu nedenle, belgeleri sık sık kontrol edin, genellikle bir yöntemin bir nesnenin sahipliğini etkileyip etkilemeyeceğini belirtir.

Debilski'nin belirttiği gibi, bu kurallar YALNIZCA QObject'ten türetilen nesneler için geçerlidir. Sınıfınız QObject'ten türetilmiyorsa, yıkımı kendiniz halletmeniz gerekir.


Yazma arasındaki fark nedir: QPushButton * someButton = new QPushButton (); veya QPushButton someButton = yeni QPushButton veya sadece QPushButton someButton;
Martin

3
Ehh, QPushButton * someButton = new QPushButton arasında büyük bir fark var; ve QPushButton someButton ;. İlki nesneyi yığın üzerinde tahsis ederken, ikincisi onu yığına tahsis eder. QPushButton * someButton = new QPushButton () arasında bir fark yoktur; ve QPushButton someButton = new QPushButton; ikisi de nesnenin varsayılan yapıcısını çağıracaktır.
Austin

Bu konuda yeniyim, sorduğum için üzgünüm ama "nesneyi yığın üzerinde tahsis et" ve "onu yığına ayır" arasındaki fark nedir? Ne zaman yığın kullanmalıyım ve ne zaman yığın kullanmalıyım? Teşekkürler!
Martin

3
Dinamik ayırmalar, nesne kapsamı ve RAII hakkında bilgi edinmeniz gerekir. Düz C ++ durumunda, nesneler kapsam dışında kaldıklarında otomatik olarak yok edildiklerinden, mümkün olduğunda yığın üzerindeki nesneleri ayırmalısınız. Sınıf üyeleri için, performans nedeniyle nesneleri öbek üzerinde ayırmak daha iyidir. Ve ne zaman bir nesnenin bir işlevin / yöntemin yürütülmesinden "daha uzun yaşamasını" istediğinizde, nesneyi öbek üzerinde tahsis etmelisiniz. Yine, bunlar biraz okuma gerektiren çok önemli konular.
Austin

@Austin Performans için yığın üzerinde sınıf üyelerini ayırmanız gerektiğine dair genel ifade bullock'lardır. Gerçekten bağlıdır ve performansla ilgili bir sorun bulana kadar otomatik depolama süresi olan değişkenleri tercih etmelisiniz.
rubenvb

7

Üst öğe (QObject nesnesi veya türetilmiş sınıfı) alt öğelerine (QObject / türetilmiş) bir gösterici listesine sahiptir. Ebeveyn yok edilirken, üst öğe listesindeki tüm nesneleri silecektir. QObject'in bu özelliğini, alt nesnelerin üst öğe silindiğinde otomatik olarak silinmesini sağlamak için kullanabilirsiniz. İlişki aşağıdaki kod kullanılarak kurulabilir

QObject* parent = new QObject();
QObject* child = new QObject(parent);
delete parent;//all the child objects will get deleted when parent is deleted, child object which are deleted before the parent object is removed from the parent's child list so those destructor will not get called once again.

Smartpointer kullanarak Qt'de belleği yönetmenin başka yolları da var. Aşağıdaki makale, Qt'deki çeşitli akıllı işaretçileri açıklamaktadır. https://www.qt.io/blog/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have


-2

Bu cevaplara ek olarak, doğrulama için, Visual Leak DetetorQt projeleri de dahil olmak üzere Visual c ++ projetleriniz için kitaplık kullanmanızı tavsiye ederim , çünkü c ++ tabanlı olduğundan, bu kitaplık new, delete, free and mallocifadelerle uyumludur , iyi belgelenmiştir ve kullanımı kolaydır. Kendi QDialogveya QWidgetmiras alınan arayüz sınıfınızı oluşturduğunuzda ve ardından bu sınıfın yeni bir nesnesini oluşturduğunuzda setAttribute(Qt::WA_DeleteOnClose), nesnenizin işlevini çalıştırmayı unutmayın .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.