การจัดการหน่วยความจำใน Qt?


97

ฉันค่อนข้างใหม่สำหรับ Qt และฉันสงสัยเกี่ยวกับสิ่งพื้นฐานบางอย่างเกี่ยวกับการจัดการหน่วยความจำและอายุการใช้งานของวัตถุ ฉันต้องลบและ / หรือทำลายวัตถุของฉันเมื่อใด สิ่งนี้ได้รับการจัดการโดยอัตโนมัติหรือไม่?

ในตัวอย่างด้านล่างฉันต้องลบวัตถุใดบ้างที่ฉันสร้าง จะเกิดอะไรขึ้นกับตัวแปรอินสแตนซ์myOtherClassเมื่อmyClassถูกทำลาย จะเกิดอะไรขึ้นถ้าฉันไม่ลบ (หรือทำลาย) วัตถุของฉันเลย? จะเป็นปัญหาต่อหน่วยความจำหรือไม่?

MyClass.h

class MyClass
{

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

MyClass.cpp

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

    MyOtherClass myOtherClass2;

    QString myString = "Hello";
}

อย่างที่คุณเห็นว่านี่เป็นสิ่งที่ค่อนข้างง่ายสำหรับมือใหม่ แต่ฉันจะเรียนรู้เกี่ยวกับเรื่องนี้ด้วยวิธีง่ายๆได้ที่ไหน

คำตอบ:


100

หากคุณสร้างลำดับชั้นของคุณเองด้วยQObjects นั่นคือคุณเริ่มต้นQObjects ที่สร้างขึ้นใหม่ทั้งหมดด้วยพาเรนต์

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

แล้วมันก็เพียงพอที่จะเพราะs destructor จะดูแลในการทำลาย (ทำได้โดยการส่งสัญญาณดังนั้นจึงปลอดภัยแม้ว่าคุณจะลบด้วยตนเองก่อนพาเรนต์ก็ตาม)deleteparentparentchildchild

คุณยังสามารถลบเด็กก่อนลำดับไม่สำคัญ ยกตัวอย่างเช่นที่คำสั่งที่ไม่ว่านี่เป็นเอกสารเกี่ยวกับต้นไม้วัตถุ

หากคุณMyClassไม่ใช่ลูกQObjectคุณจะต้องใช้วิธี C ++ ธรรมดาในการทำสิ่งต่างๆ

นอกจากนี้โปรดทราบว่าลำดับชั้นของพาเรนต์ - ลูกQObjectโดยทั่วไปไม่ขึ้นกับลำดับชั้นของลำดับชั้นของคลาส C ++ / แผนผังการสืบทอด นั่นหมายความว่าเด็กที่ได้รับมอบหมายไม่จำเป็นต้องเป็นคลาสย่อยโดยตรงของพาเรนต์ (คลาสย่อยของ) ใด ๆQObjectก็เพียงพอแล้ว

อย่างไรก็ตามอาจมีข้อ จำกัด บางประการที่กำหนดโดยผู้สร้างด้วยเหตุผลอื่น ๆ เช่นในQWidget(QWidget* parent=0)โดยที่ผู้ปกครองต้องเป็นอีกคนหนึ่งQWidgetเนื่องจากเช่นแฟล็กการมองเห็นและเนื่องจากคุณต้องทำเลย์เอาต์พื้นฐานด้วยวิธีนั้น แต่สำหรับระบบลำดับชั้นของ Qt โดยทั่วไปคุณได้รับอนุญาตให้มีสิ่งใดQObjectสิ่งหนึ่งในฐานะผู้ปกครอง


21
(It does this by issuing signals, so it is safe even when you delete child manually before the parent.)-> นี่ไม่ใช่เหตุผลว่าทำไมจึงปลอดภัย ใน Qt 4.7.4 เด็ก QObject จะถูกลบโดยตรง (ผ่านdeleteดู qobject.cpp บรรทัด 1955) เหตุผลที่ปลอดภัยในการลบออบเจ็กต์ลูกก่อนคือ QObject บอกให้พาเรนต์ลืมเมื่อมันถูกลบ
Martin Hennings

5
ฉันจะเพิ่มว่าคุณต้องตรวจสอบให้แน่ใจว่าผู้ทำลายล้างของผู้สืบเชื้อสายเป็นเสมือนจริง หากClassBสืบทอดจากQObjectและClassCสืบทอดจากClassBนั้นClassCจะถูกทำลายอย่างถูกต้องโดยความสัมพันธ์พ่อแม่ลูกของ Qt หากตัวClassBทำลายเป็นเสมือน
Phlucious

1
ลิงก์ในคำตอบตอนนี้ใช้งานไม่ได้ (ไม่น่าแปลกใจหลังจากผ่านไปเกือบ 4 ปี ... ) อาจเป็นเช่นนี้qt-project.org/doc/qt-4.8/objecttrees.html ?
PeterSW

2
ตัวทำลายของ @Phlucious QObject เป็นเสมือนอยู่แล้วซึ่งทำให้ตัวทำลายล้างของคลาสย่อยทุกตัวเสมือนโดยอัตโนมัติ
rubenvb

1
ถ้าคลาสใดคลาสหนึ่งในแผนผังการสืบทอดมีตัวทำลายเสมือนคลาสย่อยทุกคลาสด้านล่างจะมีตัวทำลายเสมือน ตอนนี้ถ้ามีคลาสพาเรนต์ที่อยู่นอกเครือข่ายตัวทำลายเสมือนโดยไม่มีตัวทำลายเสมือนฉันเชื่อว่าคุณอาจมีปัญหาได้ถ้าคุณลบตัวชี้ไปยังคลาสนั้น ๆ เมื่อวัตถุจริงอยู่ที่ใดที่หนึ่งในห่วงโซ่นั้น ในกรณีของคลาสลูกของ QObject และการลบตัวชี้ QObject ไปยังอินสแตนซ์ของคลาสลูกนั้นจะไม่มีปัญหาแม้ว่าคุณจะลืมคีย์เวิร์ดเสมือนในการประกาศ destructor ของคลาสย่อยนั้นก็ตาม
rubenvb

47

ฉันต้องการขยายคำตอบของ Debilski โดยชี้ให้เห็นว่าแนวคิดเรื่องความเป็นเจ้าของนั้นสำคัญมากใน Qt เมื่อคลาส A ถือว่าเป็นเจ้าของคลาส B คลาส B จะถูกลบเมื่อคลาส A ถูกลบ มีหลายสถานการณ์ที่อ็อบเจ็กต์หนึ่งกลายเป็นเจ้าของของอีกอ็อบเจกต์ไม่ใช่แค่เมื่อคุณสร้างอ็อบเจ็กต์และระบุพาเรนต์

ตัวอย่างเช่น:

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.

ตัวอย่างอื่น:

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

ดังนั้นให้ตรวจสอบเอกสารบ่อยๆโดยทั่วไปจะระบุว่าวิธีการใดจะส่งผลต่อความเป็นเจ้าของวัตถุหรือไม่

ตามที่ระบุโดย Debilski กฎเหล่านี้ใช้กับออบเจ็กต์ที่มาจาก QObject เท่านั้น หากคลาสของคุณไม่ได้มาจาก QObject คุณจะต้องจัดการกับการทำลายล้างด้วยตัวเอง


อะไรคือความแตกต่างระหว่างการเขียน: QPushButton * someButton = QPushButton ใหม่ (); หรือ QPushButton someButton = QPushButton ใหม่หรือ QPushButton someButton;
Martin

3
เอ๊ะมีความแตกต่างอย่างมากระหว่าง QPushButton * someButton = QPushButton ใหม่ และ QPushButton someButton ;. ก่อนหน้านี้จะจัดสรรออบเจ็กต์บนฮีปในขณะที่วัตถุหลังจะจัดสรรบนสแต็ก ไม่มีความแตกต่างระหว่าง QPushButton * someButton = QPushButton ใหม่ (); และ QPushButton someButton = QPushButton ใหม่; ทั้งสองจะเรียกตัวสร้างเริ่มต้นของวัตถุ
Austin

ฉันยังใหม่มากสำหรับเรื่องนี้ขออภัยที่ถามว่า "จัดสรรวัตถุบนฮีป" กับ "จัดสรรวัตถุบนกอง" ต่างกันอย่างไร ฉันควรใช้ฮีปเมื่อใดและควรใช้สแต็กเมื่อใด ขอบคุณ!
Martin

3
คุณต้องอ่านเกี่ยวกับการจัดสรรแบบไดนามิกขอบเขตออบเจ็กต์และ RAII ในกรณีของ C ++ ธรรมดาคุณควรจัดสรรออบเจ็กต์บนสแต็กเมื่อใดก็ตามที่ทำได้เนื่องจากอ็อบเจ็กต์จะถูกทำลายโดยอัตโนมัติเมื่อวัตถุหมดขอบเขต สำหรับสมาชิกชั้นเรียนจะเป็นการดีกว่าที่จะจัดสรรวัตถุบนฮีปเนื่องจากประสิทธิภาพ และเมื่อใดก็ตามที่คุณต้องการให้อ็อบเจ็กต์ "อยู่ได้นานกว่า" ในการดำเนินการของฟังก์ชัน / วิธีการคุณควรจัดสรรอ็อบเจ็กต์บนฮีป อีกครั้งสิ่งเหล่านี้เป็นหัวข้อที่สำคัญมากที่ต้องอ่าน
Austin

@Austin คำแถลงทั่วไปที่คุณควรจัดสรรสมาชิกชั้นเรียนในฮีปสำหรับการแสดงคือ bullocks ขึ้นอยู่กับว่าคุณควรเลือกตัวแปรที่มีระยะเวลาการจัดเก็บอัตโนมัติจนกว่าคุณจะพบปัญหาเกี่ยวกับ purrformance
rubenvb

7

พาเรนต์ (ออบเจ็กต์ QObject หรือคลาสที่ได้รับมา) มีรายการตัวชี้ไปยังลูกของมัน (QObject / มันได้มา) พาเรนต์จะลบอ็อบเจ็กต์ทั้งหมดในรายการย่อยในขณะที่พาเรนต์ถูกทำลาย คุณสามารถใช้คุณสมบัตินี้ของ QObject เพื่อทำให้อ็อบเจ็กต์ลูกลบโดยอัตโนมัติเมื่อใดก็ตามที่พาเรนต์ถูกลบ ความสัมพันธ์สามารถสร้างได้โดยใช้รหัสต่อไปนี้

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.

มีวิธีอื่นในการจัดการหน่วยความจำใน Qt โดยใช้ smartpointer บทความต่อไปนี้อธิบายถึงตัวชี้อัจฉริยะต่างๆใน Qt https://www.qt.io/blog/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have


-2

หากต้องการเพิ่มคำตอบเหล่านี้สำหรับvérificationฉันขอแนะนำให้คุณใช้Visual Leak Detetorไลบรารีสำหรับ Visual c ++ projets ของคุณรวมถึงโปรเจ็กต์ Qt เนื่องจากอิงตาม c ++ ไลบรารีนี้เข้ากันได้กับnew, delete, free and mallocคำสั่งมีการจัดทำเอกสารไว้อย่างดีและใช้งานง่าย อย่าลืมว่าเมื่อคุณสร้างคลาสอินเทอร์เฟซของคุณเองQDialogหรือที่QWidgetสืบทอดมาจากนั้นสร้างอ็อบเจ็กต์ใหม่ของคลาสนี้อย่าลืมเรียกใช้setAttribute(Qt::WA_DeleteOnClose)ฟังก์ชันของอ็อบเจ็กต์ของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.