Manajemen memori di Qt?

96

Saya cukup baru mengenal Qt dan bertanya-tanya tentang beberapa hal dasar dengan manajemen memori dan kehidupan objek. Kapan saya perlu menghapus dan / atau menghancurkan objek saya? Apakah semua ini ditangani secara otomatis?

Pada contoh di bawah ini, objek mana yang saya buat yang perlu saya hapus? Apa yang terjadi pada variabel contoh myOtherClassketika myClassdimusnahkan? Apa yang terjadi jika saya tidak menghapus (atau menghancurkan) objek saya sama sekali? Akankah itu menjadi masalah bagi ingatan?

MyClass.h

class MyClass
{

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

MyClass.cpp

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

    MyOtherClass myOtherClass2;

    QString myString = "Hello";
}

Seperti yang Anda lihat, ini adalah hal yang sangat mudah bagi pemula, tetapi di mana saya dapat mempelajarinya dengan cara yang mudah?

Martin
sumber

Jawaban:

100

Jika Anda membangun hierarki Anda sendiri dengan QObjects, artinya, Anda menginisialisasi semua yang baru dibuat QObjectdengan induk,

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

maka cukup untuk deleteitu parent, karena parentdestruktor akan mengurus penghancuran child. (Ini dilakukan dengan mengeluarkan sinyal, jadi aman bahkan ketika Anda menghapus childsecara manual sebelum induknya.)

Anda juga dapat menghapus anak tersebut terlebih dahulu, urutannya tidak masalah. Untuk contoh di mana urutan itu penting, inilah dokumentasi tentang pohon objek .

Jika Anda MyClassbukan anak dari QObject, Anda harus menggunakan cara C ++ biasa dalam melakukan sesuatu.

Juga, perhatikan bahwa hierarki induk-anak QObjects umumnya tidak bergantung pada hierarki hierarki kelas / pohon warisan kelas C ++. Artinya, anak yang ditetapkan tidak perlu menjadi subkelas langsung dari induknya . Semua (subclass dari) QObjectsudah cukup.

Mungkin ada beberapa kendala yang dikenakan oleh konstruktor karena alasan lain; seperti di QWidget(QWidget* parent=0), di mana induk harus menjadi yang lain QWidget, karena misalnya bendera visibilitas dan karena Anda akan melakukan beberapa tata letak dasar seperti itu; tetapi untuk sistem hierarki Qt secara umum, Anda diperbolehkan memiliki apapun QObjectsebagai orang tua.

Debilski
sumber
21
(It does this by issuing signals, so it is safe even when you delete child manually before the parent.)-> Ini bukanlah alasan mengapa ini aman. Dalam Qt 4.7.4, anak-anak QObject dihapus secara langsung (melalui delete, lihat qobject.cpp, baris 1955). Alasan mengapa aman untuk menghapus objek turunan terlebih dahulu adalah karena QObject memberitahu orang tuanya untuk melupakannya ketika dihapus.
Martin Hennings
5
Saya akan menambahkan bahwa Anda harus memastikan perusak keturunan adalah virtual agar ini benar. Jika ClassBmewarisi dari QObjectdan ClassCmewarisi dari ClassB, maka ClassChanya akan dihancurkan dengan benar oleh hubungan induk-anak Qt jika ClassBdestruktornya virtual.
Phlucious
1
Tautan di jawaban sekarang rusak (tidak mengherankan setelah hampir 4 tahun ...), mungkinkah itu seperti ini qt-project.org/doc/qt-4.8/objecttrees.html ?
PeterSW
2
Penghancur @Phlucious QObject sudah virtual, yang membuat penghancur setiap subkelas menjadi virtual secara otomatis.
rubenvb
1
Jika satu kelas di suatu tempat di pohon warisan memiliki destruktor virtual, setiap kelas anak di bawah ini akan memiliki destruktor virtual. Sekarang, jika ada kelas induk daun di luar rantai penghancur virtual tanpa penghancur virtual, saya yakin Anda dapat mengalami masalah jika Anda menghapus penunjuk ke kelas tertentu ketika objek sebenarnya berada di suatu tempat lebih jauh di bawah rantai itu. Dalam kasus kelas anak QObject dan menghapus penunjuk QObject ke turunan kelas anak itu, tidak pernah ada masalah, bahkan jika Anda lupa kata kunci virtual dalam deklarasi destruktor subkelas tersebut.
rubenvb
47

Saya ingin memperluas jawaban Debilski dengan menunjukkan bahwa konsep kepemilikan sangat penting di Qt. Ketika kelas A mengasumsikan kepemilikan kelas B, kelas B dihapus ketika kelas A dihapus. Ada beberapa situasi di mana satu objek menjadi pemilik objek lain, tidak hanya saat Anda membuat objek dan menentukan induknya.

Misalnya:

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.

Contoh lain:

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

Jadi, sering-seringlah memeriksa dokumentasinya, umumnya menentukan apakah suatu metode akan memengaruhi kepemilikan suatu objek.

Seperti yang dinyatakan oleh Debilski, aturan ini HANYA berlaku untuk objek yang diturunkan dari QObject. Jika kelas Anda tidak berasal dari QObject, Anda harus menangani sendiri penghancurannya.

Austin
sumber
Apa perbedaan antara penulisan: QPushButton * someButton = new QPushButton (); atau QPushButton someButton = new QPushButton atau hanya QPushButton someButton;
Martin
3
Ehh, ada perbedaan besar antara QPushButton * someButton = new QPushButton; dan QPushButton someButton ;. Yang pertama akan mengalokasikan objek di heap, sedangkan yang terakhir akan mengalokasikannya di stack. Tidak ada perbedaan antara QPushButton * someButton = new QPushButton (); dan QPushButton someButton = new QPushButton ;, keduanya akan memanggil konstruktor default objek.
Austin
Saya sangat baru dalam hal ini jadi maaf karena bertanya, tetapi apa perbedaan antara "mengalokasikan objek di heap" dan "mengalokasikannya di tumpukan"? Kapan saya harus menggunakan heap dan kapan saya harus menggunakan stack? Terima kasih!
Martin
3
Anda perlu membaca tentang alokasi dinamis, cakupan objek, dan RAII. Dalam kasus C ++ biasa, Anda harus mengalokasikan objek pada tumpukan jika memungkinkan karena objek secara otomatis dihancurkan ketika mereka kehabisan ruang lingkup. Untuk anggota kelas, lebih baik mengalokasikan objek di heap karena kinerja. Dan kapan pun Anda ingin objek "hidup lebih lama dari" eksekusi fungsi / metode, Anda harus mengalokasikan objek tersebut di heap. Sekali lagi, ini adalah topik yang sangat penting yang membutuhkan beberapa bacaan.
Austin
@Austin Pernyataan umum bahwa Anda harus mengalokasikan anggota kelas di heap untuk kinerja adalah bullock. Itu benar-benar tergantung dan Anda harus memilih variabel dengan durasi penyimpanan otomatis sampai Anda menemukan masalah dengan performa.
rubenvb
7

Induk (baik objek QObject atau kelas turunannya) memiliki daftar penunjuk ke anak-anaknya (QObject / turunannya). Induk akan menghapus semua objek dalam daftar anaknya sementara induk dimusnahkan. Anda dapat menggunakan properti QObject ini untuk membuat objek anak dihapus secara otomatis ketika induknya dihapus. Relasi dapat dibuat menggunakan kode berikut

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.

Ada cara lain untuk mengelola memori di Qt, menggunakan smartpointer. Artikel berikut menjelaskan berbagai petunjuk cerdas di Qt. https://blog.qt.digia.com/blog/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have/

yesraaj
sumber
-2

Untuk menambahkan jawaban ini, untuk vérifikasi, saya akan merekomendasikan Anda untuk menggunakan Visual Leak Detetorperpustakaan untuk proyek Visual c ++ Anda, termasuk proyek Qt karena berbasis c ++, perpustakaan ini kompatibel dengan new, delete, free and mallocpernyataan, didokumentasikan dengan baik dan mudah digunakan. Jangan lupa bahwa ketika Anda membuat kelas antarmuka sendiri QDialogatau QWidgetmewarisi, lalu membuat objek baru dari kelas ini, jangan lupa untuk menjalankan setAttribute(Qt::WA_DeleteOnClose)fungsi objek Anda.

Khaled
sumber