1. QAbstractTableModel 例子中有很多定義的函數(shù)都并未看到被調(diào)用,我注意到了這一句話“這個(gè)函數(shù)在用戶編輯數(shù)據(jù)時(shí)會(huì)自動(dòng)調(diào)用”說的是 setData() 函數(shù),但是其他的難道也都是?可是這些都是自己定義的函數(shù)?系統(tǒng)怎么會(huì)知道?
2. 像void MyTableWidget::mouseMoveEvent(QMouseEvent *event) 這類的事件到底是誰調(diào)用它的?就是說我不明白那個(gè)event的參數(shù)是誰傳給它的?
為了說明這個(gè)問題,我們先來看這個(gè)例子:
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。下面我們?nèi)タ纯?QAbstractTableModel 的代碼,位于 src/corelib/kernel/qabstractitemmodel.h。我們發(fā)現(xiàn),除去第一個(gè) setCities(const QStringList &) 函數(shù),其他的函數(shù)在其基類中都標(biāo)有 virtual 關(guān)鍵字。
在面向?qū)ο笤O(shè)計(jì)中有一個(gè)概念是多態(tài)。多態(tài)的實(shí)現(xiàn)可以有很多種。例如,我們可以以父類的指針去指向一個(gè)子類的對(duì)象。為什么呢?因?yàn)樽宇惡透割愂?is-a 的關(guān)系,也就是說,如果 B 是 A 的子類,那么可以看成,B 是一個(gè) A。我們就可以用父類的指針去指向子類的對(duì)象,例如下面的代碼:
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(); 最后一行,看似語句兩邊類型不同,實(shí)際上,由于 Child 是 Parent 的子類,父類的指針可以指向子類對(duì)象,因此這里是合法的。這么做有什么好處呢?請(qǐng)看我們的 func() 函數(shù)是 virtual 的。而子類也有一個(gè)同名的 func() 函數(shù)構(gòu)成了重寫的關(guān)系(注意,子類在重寫父類 virtual 函數(shù)時(shí)不需要寫出 virtual 關(guān)鍵字,這里我們只是為了明顯才寫出來)。virtual 關(guān)鍵字保證,在父類指針指向子類對(duì)象的情況下,正如我們這里看到的,使用這個(gè)父類指針調(diào)用 virtual 函數(shù),會(huì)執(zhí)行子類的代碼。也就是說,我們的 p->func(); 會(huì)輸出 child。但是對(duì)于普通函數(shù),例如這里的 func2(),就沒有這種關(guān)系。因此,p->func2(); 還是輸出 parent。這就是 virtual 的作用。要理解為什么我們寫的函數(shù)有很多并沒有被我們調(diào)用,或者是 Qt event 函數(shù)的參數(shù)是被誰傳進(jìn)來的,是被誰調(diào)用的,就得理解 virtual 的含義。
下面試想一下 Qt 的設(shè)計(jì)。比如我們的 model。你怎么能知道用戶究竟需要什么樣的 model 呢?難道你能夠窮盡世界中所有的 model,并且每一個(gè)給出一個(gè)類嗎?當(dāng)然不可能。那么怎么辦呢?我們的 view 就是需要有 model 啊!對(duì)于 Qt 設(shè)計(jì)人員,也面臨著這個(gè)問題。怎么解決呢?來看一下下面的代碼:
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 里面有三個(gè) virtual 函數(shù)。View 需要一個(gè) AbstractModel 的指針用來在 showView() 函數(shù)中使用。我們?cè)趺醋層脩裟軌蚝唵蔚氖褂?View 類呢?我們要求用戶去自定義一個(gè) model,叫做 MyModel,這個(gè) model 要求繼承 AbstractModel,并且必須重新它的三個(gè)函數(shù)。這樣,在我們建立 View 對(duì)象的時(shí)候,將這個(gè) MyModel 的指針傳給 View 的 setModel() 函數(shù)。注意,這個(gè)函數(shù)的參數(shù)要求是 AbstractModel *,而由于 MyModel 是 AbstractModel 的子類,因此二者構(gòu)成 is-a 的關(guān)系,所以這個(gè)函數(shù)也可以接受一個(gè) MyModel 指針。這樣一來,我們就讓 View 和我們自己的 MyModel 協(xié)同工作起來。
從這個(gè)簡單的例子可以看出,我們自定義的 model 其實(shí)就是為了提供我們自己的幾個(gè)函數(shù),讓 Qt 在使用其父類指針調(diào)用 virtual 函數(shù)的時(shí)候,實(shí)際執(zhí)行的是我們自己的代碼。這類似與一種運(yùn)行時(shí)的代碼替換的功能。我們?cè)僮屑?xì)思考下 event 函數(shù),其實(shí)也是類似的。注意,所有的 event 函數(shù)也是 virtual 的哦!當(dāng) Qt 去調(diào)用這些 virtual 函數(shù)的時(shí)候,就會(huì)把需要的 event 指針傳進(jìn)去。
實(shí)際上,這是一個(gè)很有用的技術(shù)。幾乎所有的設(shè)計(jì)模式都是用這種技術(shù),如果你希望再去深入學(xué)習(xí)各種設(shè)計(jì)模式,就要好好理解這種技術(shù)了。