天天看點

派生類的構造函數的初始化清單問題,Widget::Widget(QWidget *parent) :QWidget(parent)

該如何了解下面段代碼的第二行QWidget(parent)?

Widget::Widget(QWidget *parent) :
    QWidget(parent),//????
    ui(new Ui::Widget)
{
    ui->setupUi(this);
}      

一、派生類不能在成員初始化清單中直接初始化基類的成員

初始化基類成員

構造函數是不可繼承的。是以,派生類的構造函數必須通過調用基類的構造函數初始化基類成員,不能夠在派生類初始化清單直接初始化基類的成員,“越級初始化”。派生類的構造函數的一般格式為:

派生類名(形參表):基類名1(形參表1),基類名2(形參表2)···

{···}

注意事項:

(1)在建立派生類對象時,先調用基類的構造函數,然後調用派生類的構造函數;撤銷對象時,析構函數被調用的順序則相反。

(2)若派生類中包含對象成員,則派生類的構造函數初始化成員清單中既要列出基類的構造函數也要列出對象的構造函數。派生類定義對象時,先調用基類的構造函數,再調用對象的構造函數,最後調用派生類的構造函數。

二、派生類的構造函數的初始化清單問題

首先必須清楚一點:初始化與指派是兩碼事!

派生類的構造函數的初始化清單可以包含基類的構造函數、派生類成員的初始化,但是不能有基類成員的初始化!

以下是錯誤的:

class A

{

public:

A(int i = 0):a(i){} //基類初始化成員變量

protected:

int a;

};

class B :public A

B():a(5){}//派生類不能初始化基類成員變量

這樣是正确的:

#include<iostream>

usingnamespace std;

A(int i = 0):a(i) {}

B():A(5){}//基類的構造函數

void printinfo()

cout<< a<< endl;

}

這樣也是可以的:

class B :publicA

public:

B() {a=5;}

第三種方式雖然也可以将B類對象的a設為5,但它的原理是改變a原本的值,而不是初始化!

當定義B類對象時,會去調用B的構造函數,派生類的構造函數會先調用基類A的構造函數。是以成員a是先被初始化為0,再被B的構造函數改為5(B可以通路A的公有及保護成員)。而第二種方式則是在A的構造函數初始化a時就把5傳遞了過去。

effictive C++ :盡量使用初始化清單而不是指派。一個好的原則是,能使用初始化清單的時候盡量使用初始化清單。

三、關于C++初始化清單

C++初始化類的成員有兩種方式,一是使用初始化清單,二是在構造函數體内進行指派操作。

主要是性能問題,對于内置類型,如int, float等,使用初始化類表和在構造函數體内初始化差别不是很大,但是對于類類型來說,最好使用初始化清單,為什麼呢?由測試可知,使用初始化清單少了一次調用預設構造函數的過程,這對于資料密集型的類來說,是非常高效的。

除了性能問題之外,有些時場合初始化清單是不可或缺的,以下幾種情況時必須使用初始化清單

1.常量成員,因為常量隻能初始化不能指派,是以必須放在初始化清單裡面

2.引用類型,引用必須在定義的時候初始化,并且不能重新指派,是以也要寫在初始化清單裡面

3. 沒有預設構造函數的類類型,因為使用初始化清單可以不必調用預設構造函數來初始化,而是直接調用拷貝構造函數初始化

四、書籍與Qt案例

書籍,C++ Primer4中的15.4.2派生類構造函數有提到相關知識。

案例,Qt源碼有應用:

MainWindow::MainWindow(QWidget *parent) :

QMainWindow(parent),//調用基類的構造函數,指派父視窗指針

ui(new ui::MainWindow)

ui->setupUi(this);

繼續閱讀