1. QAbstractTableModel 例子中有很多定義的函數都并未看到被調用,我注意到了這一句話“這個函數在用戶編輯數據時會自動調用”說的是 setData() 函數,但是其他的難道也都是?可是這些都是自己定義的函數?系統(tǒng)怎么會知道?
2. 像void MyTableWidget::mouseMoveEvent(QMouseEvent *event) 這類的事件到底是誰調用它的?就是說我不明白那個event的參數是誰傳給它的?
為了說明這個問題,我們先來看這個例子:
class CityModel : public QAbstractTableModel
{ Q_OBJECT public: CityModel(QObject *parent = 0);
void setCities(const QStringList &cityNames);
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
private: int offsetOf(int row, int column) const;
QStringList cities; QVector
};
CityModel 繼承自 QAbstractTableModel。下面我們去看看 QAbstractTableModel 的代碼,位于 src/corelib/kernel/qabstractitemmodel.h。我們發(fā)現,除去第一個 setCities(const QStringList &) 函數,其他的函數在其基類中都標有 virtual 關鍵字。
在面向對象設計中有一個概念是多態(tài)。多態(tài)的實現可以有很多種。例如,我們可以以父類的指針去指向一個子類的對象。為什么呢?因為子類和父類是 is-a 的關系,也就是說,如果 B 是 A 的子類,那么可以看成,B 是一個 A。我們就可以用父類的指針去指向子類的對象,例如下面的代碼:
class Parent { public: virtual void func() { cout << "parent"; } void func2() { cout << "parent"; } }; class Child : public Parent { public: virtual void func() { cout << "child"; } void func2() { cout << "child"; } }; Parent *p = new Child; p->func(); p->func2(); 最后一行,看似語句兩邊類型不同,實際上,由于 Child 是 Parent 的子類,父類的指針可以指向子類對象,因此這里是合法的。這么做有什么好處呢?請看我們的 func() 函數是 virtual 的。而子類也有一個同名的 func() 函數構成了重寫的關系(注意,子類在重寫父類 virtual 函數時不需要寫出 virtual 關鍵字,這里我們只是為了明顯才寫出來)。virtual 關鍵字保證,在父類指針指向子類對象的情況下,正如我們這里看到的,使用這個父類指針調用 virtual 函數,會執(zhí)行子類的代碼。也就是說,我們的 p->func(); 會輸出 child。但是對于普通函數,例如這里的 func2(),就沒有這種關系。因此,p->func2(); 還是輸出 parent。這就是 virtual 的作用。要理解為什么我們寫的函數有很多并沒有被我們調用,或者是 Qt event 函數的參數是被誰傳進來的,是被誰調用的,就得理解 virtual 的含義。
下面試想一下 Qt 的設計。比如我們的 model。你怎么能知道用戶究竟需要什么樣的 model 呢?難道你能夠窮盡世界中所有的 model,并且每一個給出一個類嗎?當然不可能。那么怎么辦呢?我們的 view 就是需要有 model 啊!對于 Qt 設計人員,也面臨著這個問題。怎么解決呢?來看一下下面的代碼:
class AbstarctModel {
public: virtual void setData();
virtual int rowCount(); virtual int columnCount();
};
class View
{
public: void setModel(AbstractModel *m)
{
model = m;
}
void showView()
{
int r = model->rowCount();
int col = model->columnCount();
// ...
}
private: AbstractModel *model;
};
class MyModel : public AbstractModel
{ public: void setData(); i
nt rowCount();
int columnCount(); };
View *view = new View;
view->setModel(new MyModel);
AbstractModel 里面有三個 virtual 函數。View 需要一個 AbstractModel 的指針用來在 showView() 函數中使用。我們怎么讓用戶能夠簡單的使用 View 類呢?我們要求用戶去自定義一個 model,叫做 MyModel,這個 model 要求繼承 AbstractModel,并且必須重新它的三個函數。這樣,在我們建立 View 對象的時候,將這個 MyModel 的指針傳給 View 的 setModel() 函數。注意,這個函數的參數要求是 AbstractModel *,而由于 MyModel 是 AbstractModel 的子類,因此二者構成 is-a 的關系,所以這個函數也可以接受一個 MyModel 指針。這樣一來,我們就讓 View 和我們自己的 MyModel 協(xié)同工作起來。
從這個簡單的例子可以看出,我們自定義的 model 其實就是為了提供我們自己的幾個函數,讓 Qt 在使用其父類指針調用 virtual 函數的時候,實際執(zhí)行的是我們自己的代碼。這類似與一種運行時的代碼替換的功能。我們再仔細思考下 event 函數,其實也是類似的。注意,所有的 event 函數也是 virtual 的哦!當 Qt 去調用這些 virtual 函數的時候,就會把需要的 event 指針傳進去。
實際上,這是一個很有用的技術。幾乎所有的設計模式都是用這種技術,如果你希望再去深入學習各種設計模式,就要好好理解這種技術了。