1. 程式人生 > >正確理解Widget::Widget(QWidget *parent) :QWidget(parent)

正確理解Widget::Widget(QWidget *parent) :QWidget(parent)

對話方塊是GUI程式和使用者進行簡短互動的頂層視窗,所謂頂層視窗即始終在主視窗之上顯示。QDialog是Qt所有型別的對話方塊視窗的基類,它繼承於QWidget,是一種容器型別元件。

這裡寫圖片描述

  QWidget是所有視窗類的抽象,它也可以生成對話方塊,但是對話方塊是常見的視窗元件,若每次要使用對話方塊,都利用QWidget來生成並設定相關引數,顯然十分繁瑣。所以Qt為我們封裝了另外一個子類QDialog,專門用於生成對話方塊

       QDialog類是對話方塊視窗的基類。對話方塊視窗是主要用於短時期任務以及使用者進行簡要通訊的頂級視窗。QDialog可以是模態對話方塊也可以是非模態對話方塊。QDialog支援擴充套件性並且可以提供返回值。他們可以有預設按鈕。QDialog也可以有一個QSizeGrip在它的右下方,使用setSizeGripEnable()。

注意:QDialog使用父視窗部件的方法和Qt中其他類不同。對話方塊總是頂級視窗部件,但是如果它有一個父物件,它的預設位置就是父物件的中間。他也將和父物件共享工具條條目。

QDialog 是最普通的頂級視窗(一個不會被嵌入到父視窗部件的視窗部件叫頂級視窗部件)。通常情況下,頂級視窗部件是有框架和標題欄的視窗(儘管使用了一定的視窗部件標記,建立頂級視窗部件時也可以沒有這個修飾)在Qt中。QMainWindow和不同的QDialog的子類是最普通的頂級視窗。

非頂級視窗部件就是子視窗部件。他們是他們的父視窗部件中的子視窗。你通常不能在視覺角度從它們的父視窗部件辨別一個子視窗部件。在Qt中的絕大多數其他視窗部件僅僅作為子視窗部件才是有用的。(當然把一個按鈕作為或者叫做頂級視窗部件也是有可能的,但是絕大多數人喜歡把它們的按鈕放到其他部件當中)

   如果是頂級對話方塊,那就是基於QDialog建立,如果是主窗體,就基於QMainWindow,如果不確定,或有可能作為頂級窗體,或有可能嵌入到其他窗體中,則基於QWidget建立。

該如何理解下面段程式碼的第二行QWidget(parent)

1 Widget::Widget(QWidget *parent) :

2 QWidget(parent)

3 {

4 }

在講解原因之前,先請大家看下面的一個例子

#include <iostream>

using namespace std;

class Base

{

public:

Base() :m_num(0){

cout << "this is Base()" << endl;

}

Base(int val):m_num(val){

cout << "this is Base(int val)" << endl;

}

private:

int m_num;

};

1 上方程式碼定義了一個基類Base,並且有兩個建構函式,一個是預設建構函式,一個是有一個整型引數的建構函式。

class BaseChild: public Base

{

public:

BaseChild(){

cout << "this is BaseChild()" << endl;

}

BaseChild(int val): Base(val){

cout << "this is BaseChild(val)" << endl;

}

private:

int m_num;

};

2 上方程式碼定義了一個BaseChild類,並繼承Base類,同樣的,它也定義了兩個建構函式,一個預設,一個有整型引數。

int main(int argc, char *argv[])

{

BaseChild child1;

BaseChild child2(5);


return 0;

}

3 main函式例項化了兩個子類例項,child1,child2。child1呼叫預設建構函式。child2呼叫有整型引數的建構函式。

現在,我們執行程式,會有如下列印:

  this is Base()!

     this is Base(int Val)!

     this is BaseChild()!

     this is BaseChild(int Val)!

看到了嗎,我們發現:

  • 建立child1時,是先呼叫了Base的預設建構函式,再呼叫自己的預設建構函式
  • 建立child2時,是先呼叫了Base(int)這個建構函式,再呼叫自己的整型引數建構函式。

所以我們回頭看BaseChild的建構函式

BaseChild(int val): Base(val){

cout << "this is BaseChild(val)" << endl;

}

細心的同學,可能早就發現了,初始化列表中的Base(val)正是呼叫了我們Base基類的有參建構函式,而這樣的寫法就剛好是我們開頭程式碼中的那段

Widget::Widget(QWidget *parent) :QWidget(parent)

所以Widget是呼叫了QWidget下面的建構函式

QWidget(QWidget* parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());

所以得出如下總結:

總結: · 如果不指定建構函式,則派生類會呼叫基類的預設建構函式 · 派生類建構函式的初始化列表只能初始化派生類成員,不能直接初始化繼承成員,如果想 要呼叫基類的有參建構函式,則可以在派生類的初始化列表中顯示指定

以上總結,也告訴我們,當定義一個類時,最好為該類定義預設建構函式。

至此,我們明白了這個寫法為什麼會這樣寫。

好的,那麼我們又提出一個問題,“呼叫QWidget(parent)這個建構函式,QWidget父類都做了哪些動作呢?”

下面是QWidget原始碼中的一部分節選:

QWidget::QWidget( QWidget *parent, const char *name, WFlags f )

: QObject( parent, name ), QPaintDevice( PDT_WIDGET ),

pal( parent ? parent->palette() // use parent's palette

: *qApp->palette() ) // use application palette

{

if ( parent ) {

QChildEvent *e = new QChildEvent( Event_ChildInserted, this );

QApplication::postEvent( parent, e );

}

}

大家從上面可以看出,如果parent引數非空的話,那麼該建構函式使用了其父視窗的調色盤,並且傳送了QChildEvent事件,這會讓新的視窗成為parent所指視窗的子視窗,那麼當父視窗被刪除時,子視窗也會自動的被刪除。

初始化列表中的Base(Vale)正好呼叫了Base基類的有參建構函式。

    同理,可以理解Widget::Widget(QWidget *parent) :QWidget(parent)。Widget::Widget(QWidget *parent) :QWidget(parent)呼叫了QWidget基類的下述建構函式:

QWidget(QWidget*parent =Q_NULLPTR,Qt::WindowFlagsf =Qt::WindowFlags());

     如果不指定建構函式,則派生類會呼叫基類的預設建構函式 。派生類建構函式的初始化列表只能初始化派生類成員,不能直接初始化繼承成員,如果想 要呼叫基類的有參建構函式,則可以在派生類的初始化列表中顯示指定。

    當定義一個類時,最好為該類定義一個預設建構函式。

對應語法:

派生類::派生類建構函式(總引數列表):基類建構函式(引數列表)  //基類建構函式的引數列表是實參。 { 派生類中的資料成員初始化;