Quản lý bộ nhớ trong Qt?


96

Tôi khá mới đối với Qt và đang thắc mắc về một số nội dung cơ bản với quản lý bộ nhớ và tuổi thọ của các đối tượng. Khi nào tôi cần xóa và / hoặc hủy các đối tượng của mình? Có bất kỳ điều nào trong số này được xử lý tự động không?

Trong ví dụ dưới đây, tôi cần xóa đối tượng nào trong số các đối tượng tôi tạo? Điều gì xảy ra với biến cá thể myOtherClasskhi myClassbị hủy? Điều gì xảy ra nếu tôi không xóa (hoặc phá hủy) các đối tượng của mình? Đó sẽ là một vấn đề với bộ nhớ?

MyClass.h

class MyClass
{

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

MyClass.cpp

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

    MyOtherClass myOtherClass2;

    QString myString = "Hello";
}

Như bạn có thể thấy đây là công cụ khá dễ dàng cho người mới nhưng tôi có thể tìm hiểu về điều này một cách dễ dàng ở đâu?

Câu trả lời:


100

Nếu bạn xây dựng hệ thống phân cấp của riêng mình với QObjects, nghĩa là bạn khởi tạo tất cả các QObjects mới được tạo với cha mẹ,

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

sau đó nó là đủ để deletecác parent, vì parents destructor sẽ chăm sóc phá hủy child. (Nó thực hiện điều này bằng cách phát ra các tín hiệu, vì vậy nó an toàn ngay cả khi bạn xóa childtheo cách thủ công trước cha mẹ.)

Bạn cũng có thể xóa đứa trẻ trước, thứ tự không quan trọng. Đối với một ví dụ mà thứ tự quan trọng, đây là tài liệu về cây đối tượng .

Nếu bạn MyClasskhông phải là con của QObject, bạn sẽ phải sử dụng cách làm việc đơn giản của C ++.

Ngoài ra, lưu ý rằng phân cấp cha-con của QObjects nói chung độc lập với phân cấp của cây kế thừa / phân cấp lớp C ++. Điều đó có nghĩa là một đứa trẻ được chỉ định không cần phải là một lớp con trực tiếp của nó . Bất kỳ (lớp con của) QObjectsẽ đủ.

Tuy nhiên, có thể có một số ràng buộc do các constructor áp đặt vì những lý do khác; chẳng hạn như trong QWidget(QWidget* parent=0), nơi cha mẹ phải là người khác QWidget, ví dụ như cờ khả năng hiển thị và vì bạn thực hiện một số bố cục cơ bản theo cách đó; nhưng đối với hệ thống phân cấp của Qt nói chung, bạn được phép có bất kỳ ai QObjectlàm cha mẹ.


21
(It does this by issuing signals, so it is safe even when you delete child manually before the parent.)-> Đây không phải là lý do tại sao nó an toàn. Trong Qt 4.7.4, QObject con bị xóa trực tiếp (thông qua delete, xem qobject.cpp, dòng 1955). Lý do tại sao việc xóa các đối tượng con là an toàn trước tiên là QObject yêu cầu cha mẹ của nó quên nó khi nó bị xóa.
Martin Hennings

5
Tôi muốn nói thêm rằng bạn phải đảm bảo rằng các hàm hủy của con cháu là ảo để điều này là đúng. Nếu ClassBkế thừa từ QObjectClassCkế thừa từ ClassB, thì ClassCsẽ chỉ bị phá hủy đúng cách bởi mối quan hệ cha-con của Qt nếu hàm ClassBhủy của là ảo.
Phlucious

1
Liên kết trong câu trả lời hiện đã bị hỏng (không có gì đáng ngạc nhiên sau gần 4 năm ...), có lẽ nó là một cái gì đó như thế này qt-project.org/doc/qt-4.8/objecttrees.html ?
PeterSW

2
Trình hủy của @Phlucious QObject đã là ảo, điều này làm cho mọi trình hủy của lớp con trở nên ảo một cách tự động.
rubenvb 22/09/2016

1
Nếu một lớp ở đâu đó trong cây kế thừa có bộ hủy ảo, thì mọi lớp con bên dưới sẽ có bộ hủy ảo. Bây giờ, nếu có một lớp cha lá bên ngoài một chuỗi hủy ảo như vậy mà không có một trình hủy ảo, tôi tin rằng bạn có thể gặp sự cố nếu xóa một con trỏ đến lớp cụ thể đó khi đối tượng thực ở đâu đó xa hơn trong chuỗi đó. Trong trường hợp lớp con của QObject và xóa con trỏ QObject tới một thể hiện của lớp con đó, không bao giờ có vấn đề, ngay cả khi bạn quên từ khóa ảo trong khai báo hủy của lớp con đó.
rubenvb

47

Tôi muốn mở rộng câu trả lời của Debilski bằng cách chỉ ra rằng khái niệm quyền sở hữu rất quan trọng trong Qt. Khi lớp A đảm nhận quyền sở hữu của lớp B, lớp B sẽ bị xóa khi lớp A bị xóa. Có một số tình huống trong đó một đối tượng trở thành chủ sở hữu của đối tượng khác, không chỉ khi bạn tạo một đối tượng và chỉ định cha mẹ của nó.

Ví dụ:

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.

Một vi dụ khac:

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

Vì vậy, hãy kiểm tra tài liệu thường xuyên, nó thường chỉ định liệu một phương thức có ảnh hưởng đến quyền sở hữu của một đối tượng hay không.

Như Debilski đã nêu, những quy tắc này CHỈ áp dụng cho các đối tượng xuất phát từ QObject. Nếu lớp của bạn không xuất phát từ QObject, bạn sẽ phải tự xử lý việc phá hủy.


Sự khác biệt giữa cách viết: QPushButton * someButton = new QPushButton (); hoặc QPushButton someButton = new QPushButton hoặc chỉ QPushButton someButton;
Martin

3
Ơ, có một sự khác biệt rất lớn giữa QPushButton * someButton = new QPushButton; và QPushButton someButton ;. Cái trước sẽ phân bổ đối tượng trên đống, trong khi cái sau sẽ phân bổ nó trên ngăn xếp. Không có sự khác biệt giữa QPushButton * someButton = new QPushButton (); và QPushButton someButton = new QPushButton ;, cả hai đều sẽ gọi hàm tạo mặc định của đối tượng.
Austin

Tôi rất mới với điều này nên xin lỗi vì đã hỏi nhưng sự khác biệt giữa "phân bổ đối tượng trên đống" và "phân bổ nó trên ngăn xếp" là gì? Khi nào tôi nên sử dụng heap và khi nào tôi nên sử dụng stack? Cảm ơn!
Martin

3
Bạn cần đọc về phân bổ động, phạm vi đối tượng và RAII. Trong trường hợp sử dụng C ++ thuần túy, bạn nên cấp phát các đối tượng trên ngăn xếp bất cứ khi nào có thể vì các đối tượng sẽ tự động bị hủy khi chúng hết phạm vi. Đối với các thành viên trong lớp, tốt hơn là nên phân bổ các đối tượng trên heap do hiệu suất. Và bất cứ khi nào bạn muốn một đối tượng "tồn tại lâu hơn" việc thực thi một hàm / phương thức, bạn nên cấp phát đối tượng trên heap. Một lần nữa, đây là những chủ đề rất quan trọng đòi hỏi một số bài đọc.
Austin

@Austin Tuyên bố chung mà bạn nên phân bổ các thành viên lớp trên heap để đạt hiệu suất là bullocks. Nó thực sự phụ thuộc và bạn nên thích các biến có thời lượng lưu trữ tự động cho đến khi bạn tìm thấy vấn đề với hiệu suất.
rubenvb 24/09/2016

7

Parent (đối tượng QObject hoặc lớp dẫn xuất của nó) có một danh sách con trỏ tới con của nó (QObject / được dẫn xuất của nó). Phần tử cha sẽ xóa tất cả các đối tượng trong danh sách con của nó trong khi phần tử cha bị hủy. Bạn có thể sử dụng thuộc tính này của QObject để làm cho các đối tượng con tự động xóa khi bất kỳ đối tượng nào bị xóa. Mối quan hệ có thể được thiết lập bằng cách sử dụng mã sau

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.

Có một cách khác để quản lý bộ nhớ trong Qt, sử dụng smartpointer. Bài viết sau đây mô tả các con trỏ thông minh khác nhau trong Qt. https://blog.qt.digia.com/blog/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have/


-2

Để bổ sung cho các câu trả lời này, để xác nhận vé, tôi khuyên bạn nên sử dụng Visual Leak Detetorthư viện cho các dự án Visual c ++ của bạn, bao gồm các dự án Qt vì nó dựa trên c ++, thư viện này tương thích với các new, delete, free and malloccâu lệnh, được ghi chép tốt và dễ sử dụng. Đừng quên rằng khi bạn tạo lớp giao diện riêng QDialoghoặc QWidgetlớp giao diện kế thừa và sau đó tạo một đối tượng mới của lớp này, đừng quên thực thi setAttribute(Qt::WA_DeleteOnClose)chức năng của đối tượng của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.