2015-05-20 23 views
8

Hầu như mọi lớp QtWidgets đều có thể có cấp độ gốc. Và thường là tùy chọn để đặt cha mẹ lúc khởi tạo đối tượng. Ví dụ, Nếu tôi có thể tạo một lớp kế thừa QWidget lớp, tôi sẽ làm như sau vào constructor:Phụ huynh trong Qt là gì?

Widget::Widget(QWidget* parent): QWidget(parent) { 
    hbox = new QHBoxLayout(this); 
    yes_button = new QPushButton("&Yes"); 
    no_button = new QPushButton("&No", this); 
    cancel_button = new QPushButton("&Cancel", hbox); 
} 

tôi có thể thiết lập hoặc không đặt phụ huynh. Tôi có thể đặt cancel_button làm con của hbox. Tôi cũng có thể đặt cancel_button làm con của yes_button, nhưng tôi nghĩ rằng đó là điều xấu.

Điểm của vấn đề này là gì? Và, có thực sự cần thiết để đặt cha mẹ cho mọi lớp dựa trên QWidget mà tôi tạo không?

+0

Bạn có nhận thấy rằng 'QWidget' có nguồn gốc từ 'QObject' không? – cmannett85

+0

Vâng, tất nhiên rồi. Nhưng, tôi thực sự không thể hiểu được quan điểm của mối quan hệ cha mẹ này. –

Trả lời

9

Ngoài việc giúp vẽ thứ tự trong các đối tượng GUI, nó cũng giúp quản lý bộ nhớ, để khi bạn hủy QObject, tất cả các con của nó cũng bị hủy. Xem http://doc.qt.io/qt-4.8/objecttrees.html để biết thêm chi tiết. Khi một cái gì đó thay đổi trong cha mẹ (tức là nó được thay đổi kích thước), nó có thể thông báo cho trẻ em của mình để cập nhật bản thân quá.

Để trả lời câu hỏi của bạn, bạn không bắt buộc phải đặt cha mẹ cho mọi thứ (đó là lý do tại sao đây là thông số tùy chọn), nhưng hầu hết thời gian tốt hơn là đặt chính xác.

+0

Có ví dụ nào tôi nên đặt lớp cha dựa trên 'QWidget'. Một trường hợp chương trình sẽ không hoạt động bình thường mà không đặt cha mẹ? –

+2

Nếu bạn không đặt chính xác cấp độ gốc, trình quản lý bố cục sẽ không hoạt động. Một vấn đề khác là trong ví dụ của bạn, nếu bạn phá hủy QWidget của bạn, bạn sẽ phải tiêu diệt yes_button bằng tay, hoặc nó sẽ bị rò rỉ. Nếu cha mẹ được thiết lập, Qt sẽ chăm sóc nó cho bạn – Vitor

+1

Lưu ý rằng có một trường hợp đặc biệt với 'QWidget'. Nếu nó không có cha, nó sẽ được chuyển thành một cửa sổ cấp cao nhất (xem [ở đây] (http://doc.qt.io/qt-4.8/qobject.html#QObject)). – rbaleksandar

3

Thứ nhất, QWidgetQObjectQObject s là các nút trong một cây QObject. Các nút con được bộ nhớ quản lý bởi cha mẹ, trừ khi bạn deallocate chúng trước khi cha mẹ có cơ hội để làm như vậy. Vì vậy, quản lý bộ nhớ là một trong những lý do cho các widget, hoặc bất kỳ một số khác của QObject s, để có một phụ huynh.

Thứ hai, tiện ích con không nhìn thấy được là luôn là cửa sổ cấp cao nhất. Ngược lại, không thể có tiện ích con cấp cao không có cha mẹ. Khi bạn hiển thị một tiện ích không có cha mẹ, nó sẽ có cửa sổ riêng. Điều ngược lại không nhất thiết phải đúng - có thể cung cấp cho tiện ích con một cờ Qt::Window và nó cũng trở thành một cửa sổ cấp cao nhất.

Hệ quả là bất kỳ tiện ích nào chứa trong các tiện ích khác cha mẹ - nếu không nó sẽ là cửa sổ cấp cao nhất. Phụ huynh này có thể không được thiết lập rõ ràng bởi bạn, nhưng nó đã được thiết lập.

Tôi nghĩ rằng câu hỏi của bạn có thể được lặp lại như sau: Khi nào tôi cần phải cung cấp một cách rõ ràng cho hàm tạo phụ tùng? Câu trả lời là:

  1. Bất cứ khi nào tiện ích là cửa sổ cấp cao nhất bạn định có cha/mẹ. Các cửa sổ như vậy không chịu sự quản lý bố cục, do đó không có cơ chế để đặt bố mẹ đó cho bạn. Các hộp thoại tạm thời cấp cao nhất cần phải có các bậc cha mẹ để chúng được định vị chính xác liên quan đến cửa sổ chính.

  2. Bất cứ khi nào bạn có tiện ích con không phải quản lý bố cục.

Widgets chịu sự quản lý bố trí được độ gốc khi chèn vào một bố cục:

int main(int argc, char ** argv) { 
    QApplication app(argc, argv); 
    QWidget window; 
    QVBoxLayout layout(&window); 
    QLabel label("Hello"); 
    QPushButton button("Goodbye"); 
    layout.addWidget(&label); 
    layout.addWidget(&button); 
    QObject::connect(&button, &QPushButton::clicked, [&app]{ app.quit(); }); 
    window.show(); 
    return app.exec(); 
} 

Cuối cùng, không phải tất cả các widget hoặc QObject s cần phải được tạo ra một cách rõ ràng trên heap. Vì tất cả các lớp học QObject đã tham gia trong Qt (và nhiều lớp khác nữa!) sử dụng thành ngữ PIMPL, khi bạn phân bổ chúng riêng lẻ trên heap, bạn thực sự đang thực hiện phân bổ đống hai lần. Trước tiên, bạn cấp phát cá thể của lớp - đôi khi cá thể là nhỏ như một con trỏ hoặc hai - và sau đó hàm tạo của lớp phân bổ PIMPL của nó. Phân bổ heap rõ ràng là một trường hợp bi quan sớm.

Để tránh pessimization này, Widget của bạn sẽ giống như sau:

class Widget : public QWidget { 
    Q_OBJECT 
    QHBoxLayout m_layout; 
    QPushButton m_yesButton, m_noButton, m_cancelButton; 
public: 
    Widget(QWidget * parent = 0); 
}; 

Widget::Widget(QWidget * parent) : 
    QWidget(parent), 
    m_layout(this), 
    m_yesButton("&Yes"), 
    m_noButton("&No"), 
    m_cancelButton("&Cancel") 
{ 
    m_layout.addWidget(&m_yesButton); 
    m_layout.addWidget(&m_noButton); 
    m_layout.addWidget(&m_cancelButton); 
} 

Nếu bạn muốn sử dụng the PIMPL idiom, bạn có thể làm điều đó, quá:

// Widget.h - Interface 
class WidgetPrivate; 
class Widget : public QWidget { 
{ 
    Q_OBJECT 
    Q_DECLARE_PRIVATE(Widget) 
    QScopedPointer<WidgetPrivate> const d_ptr; 
public: 
    Widget(QWidget * parent = 0); 
    ~Widget(); 
}; 

// Widget.cpp - Implementation 
class WidgetPrivate { 
    Q_DISABLE_COPY(WidgetPrivate) 
    Q_DECLARE_PUBLIC(Widget) 
    Widget * const q_ptr; 
    QHBoxLayout layout; 
    QPushButton yesButton, noButton, cancelButton; 
public: 
    WidgetPrivate(Widget * q); 
}; 

WidgetPrivate::WidgetPrivate(Widget * q) { 
    q_ptr(q), 
    layout(q), 
    yesButton("&Yes"), 
    noButton("&No"), 
    cancelButton("&Cancel") 
{ 
    layout.addWidget(&yesButton); 
    layout.addWidget(&noButton); 
    layout.addWidget(&cancelButton); 
} 

Widget::Widget(QWidget * parent) : 
    QWidget(parent), 
    d_ptr(new WidgetPrivate(this)) 
{} 

Widget::~Widget() {} 
// necessary, since WidgetPrivate is unknown to the interface! 

Tất nhiên, bạn nên có sử dụng QDialogButtonBox thay vì tất cả điều này :)

Các vấn đề liên quan