天天看點

Qt 5中信号和槽的新文法Qt 5中信号和槽的新文法 Qt 5之前的文法Qt 5之前信号和槽文法的問題新文法:使用函數指針現在該幹什麼呢?

Qt 5中信号和槽的新文法

參考原文:woboq: Olivier Goffart – Signals and Slots in Qt5

Qt 5 Alpha已經釋出。我曾經工作過的一個新特性就是信号和槽的新文法。本篇部落格将會為您講解這一内容。

Qt 5之前的文法

這裡是我們如何把信号和槽連接配接起來的方法:

1 2

connect(sender, SIGNAL(valueChanged(QString, QString)),

receiver, SLOT(updateValue(QString)));

幕後的實際情況是

SIGNAL

SLOT

這兩個宏會把它們的參數轉換成字元串。然後

QObject::connect()

将會把這些字元串和

moc

工具所收集的内省(introspection)資料進行比較。

Qt 5之前信号和槽文法的問題

雖然通常情況下都可以正常工作,我們還是發現了如下問題:

  • 沒有編譯期檢查:因為函數名被處理成字元串,所有的檢查都是在運作時完成的。這就是為什麼有時會發生編譯通過了,但槽并沒有被調用。此時,你就應該去檢查标準輸出,看看有沒有什麼警告說明這個連接配接沒有成功。
  • 因為處理的是字元串,是以槽函數中的類型名字必須與信号的完全一緻,而且在頭檔案中的和實際的

    connect()

    語句中的也必須一緻。也就是說,如果你使用了

    typedef

    或者命名空間,那麼這個連接配接就不能正常工作。

新文法:使用函數指針

在即将到來的Qt 5中提供了一套新的文法。之前的文法依然可以使用,但是現在,我們有了全新的方式:

1 2

connect(sender, &Sender::valueChanged,

receiver, &Receiver::updateValue);

哪一種方式更酷可能取決于個人喜好。但大家會更快的适應新方式。

現在讓我們先遠離美學視角,來看看新文法有什麼好處:

編譯期檢查

如果信号或者槽的名字的拼寫發生了錯誤,或者槽函數的參數與信号的不一緻,你會在編譯期就得到一個錯誤。

這肯定會在重構、或者修改信号或槽函數的名字時節省很多時間。

另一個好處是,如果我們少了

Q_OBJECT

宏,可以利用

static_cast

傳回更友好的錯誤資訊。

參數的自動類型轉換

現在,我們不僅可以更好地使用

typedef

或者命名空間,而且可以利用隐式類型轉換。

在下面的例子中,我們的信号有一個

QString

參數,而槽函數需要的是

QVariant

。它可以正常工作,是因為

QVariant

有一個可以使用

QString

的隐式構造函數。

1 2 3 4 5 6 7 8 9 10 11 12

class

Test :

public

QObject

{

Q_OBJECT

public

:

Test() {

connect(

this

, &Test::someSignal,

this

, &Test::someSlot);

}

signals:

void

someSignal(

const

QString &);

public

:

void

someSlot(

const

QVariant &);

};

連接配接到任意函數

如果你留心上面的例子,就會發現,我們的信号被連接配接到了一個槽,但是它的聲明隻有

public

,沒有

slot

。Qt的新文法通過函數指針直接調用函數,而不再需要

moc

的内省(但是信号依然需要)。

更進一步,我們可以将信号連接配接到任意函數或者函數對象(functor):

1 2 3 4 5 6

static

void

someFunction() {

qDebug() <<

"pressed"

;

}

// ... 然後其它地方

QObject::connect(button, &QPushButton::clicked, someFunction);

這樣處理,就可以讓你很友善的同

boost

或者

tr1::bind

進行協作。

C++11 Lambda表達式

至此之前,我們所有的示例都是基于

C++98

标準的。但是,如果你的編譯器支援

C++11

,我還是強烈建議你使用一些這個語言的新特性。現在,

Lambda

表達式至少被

MSVC 2010

GCC 4.5

clang 3.1

這幾個編譯器支援。不過對于後面兩個編譯器,你需要在編譯時加上

-std=c++0x

參數。

然後我們就可以這樣寫代碼了:

1 2 3 4 5 6 7 8 9 10 11

void

MyWindow::saveDocumentAs() {

QFileDialog *dlg =

new

QFileDialog();

dlg->open();

QObject::connect(dlg, &QDialog::finished, [=](

int

result) {

if

(result) {

QFile file(dlg->selectedFiles().first());

// ... 在這裡儲存文檔 ...

}

dlg->deleteLater();

});

}

這種文法允許我們更友善地編寫異步代碼。

更新:您還可以看看Qt 5中提供的其它C++11特性。

現在該幹什麼呢?

讓我們來嘗試一下吧。下載下傳Qt 5 Alpha,開始玩吧。不要忘記報告Bug呀。

(譯者注:感謝齊亮對本篇部落格全文進行了修正。)

繼續閱讀