1. 程式人生 > >Qt 之 訊號槽機制及優缺點

Qt 之 訊號槽機制及優缺點

1. Qt 訊號槽機制

概念:
訊號槽是 Qt 框架引以為豪的機制之一。所謂訊號槽,實際就是觀察者模式。當某個事件發生之後,比如,按鈕檢測到自己被點選了一下,它就會發出一個訊號(signal)。這種發出是沒有目的的,類似廣播。如果有物件對這個訊號感興趣,它就會使用連線(connect)函式,意思是,將想要處理的訊號和自己的一個函式(稱為槽(slot))繫結來處理這個訊號。也就是說,當訊號發出時,被連線的槽函式會自動被回撥。

槽的本質是類的成員函式,其引數可以是任意型別的。和普通C++成員函式幾乎沒有區別,它可以是虛擬函式;也可以被過載;可以是公有的、保護的、私有的、也可以被其他C++成員函式呼叫。唯一區別的是:槽可以與訊號連線在一起,每當和槽連線的訊號被髮射的時候,就會呼叫這個槽。
自定義訊號槽注意事項:


(1)傳送者和接收者都需要是QObject的子類(當然,槽函式是全域性函式、Lambda 表示式等無需接收者的時候除外);
(2)使用 signals 標記訊號函式,訊號是一個函式宣告,返回 void,不需要實現函式程式碼;
(3)槽函式是普通的成員函式,作為成員函式,會受到 public、private、protected 的影響;
(4)使用 emit 在恰當的位置傳送訊號;
(5)使用QObject::connect()函式連線訊號和槽;
(6)任何成員函式、static 函式、全域性函式和 Lambda 表示式都可以作為槽函式。
訊號槽的多種用法:
(1)一個訊號可以和多個槽相連
  如果是這種情況,這些槽會一個接一個的被呼叫,但是它們的呼叫順序是不確定的。
(2)多個訊號可以連線到一個槽
  只要任意一個訊號發出,這個槽就會被呼叫。
(3)一個訊號可以連線到另外的一個訊號
  當第一個訊號發出時,第二個訊號被髮出。除此之外,這種訊號-訊號的形式和訊號-槽的形式沒有什麼區別。
(4)槽可以被取消連結
  這種情況並不經常出現,因為當一個物件delete之後,Qt自動取消所有連線到這個物件上面的槽。
(5)使用Lambda 表示式
  在使用 Qt 5 的時候,能夠支援 Qt 5 的編譯器都是支援 Lambda 表示式的。
總結:

QT訊號槽機制

2. QT訊號槽機制的優缺點

(1)問題:
為什麼Qt使用訊號與槽機制而不是傳統的回撥函式機制進行物件間的通訊呢?
回撥函式的本質是“你想讓別人的程式碼執行你的程式碼,而別人的程式碼你又不能動”這種需求下產生的。
回撥函式是函式指標的一種用法,如果多個類都關注某個類的狀態變化,此時需要維護一個列表,以存放多個回撥函式的地址。對於每一個被關注的類,都需要做類似的工作,因此這種做法效率低,不靈活。
(2)解決辦法:
Qt使用訊號與槽機制來解決這個問題,程式設計師只需要指定一個類含有哪些訊號函式、哪些槽函式,Qt會處理訊號函式和槽函式之間的繫結。當訊號函式被呼叫時,Qt會找到並執行與其繫結的槽函式。允許一個訊號函式和多個槽函式繫結,Qt會依次找到並執行與一個訊號函式繫結的所有槽函式,這種處理方式更靈活。
(3)優點:


Qt訊號與槽機制降低了Qt物件的耦合度.

3. 多執行緒情況下, Qt中的訊號槽分別在什麼執行緒中執行, 如何控制?

通過connect函式的第五個引數connectType來控制。
connect用於連線qt的訊號和槽,在qt程式設計過程中不可或缺。它其實有第五個引數,只是一般使用預設值,在滿足某些特殊需求的時候可能需要手動設定。

Qt::AutoConnection: 預設值,使用這個值則連線型別會在訊號傳送時決定。如果接收者和傳送者在同一個執行緒,則自動使用Qt::DirectConnection型別。如果接收者和傳送者不在一個執行緒,則自動使用Qt::QueuedConnection型別。

Qt::DirectConnection:槽函式會在訊號傳送的時候直接被呼叫,槽函式運行於訊號傳送者所線上程。效果看上去就像是直接在訊號傳送位置呼叫了槽函式。這個在多執行緒環境下比較危險,可能會造成奔潰。

Qt::QueuedConnection:槽函式在控制回到接收者所線上程的事件迴圈時被呼叫,槽函式運行於訊號接收者所線上程。傳送訊號之後,槽函式不會立刻被呼叫,等到接收者的當前函式執行完,進入事件迴圈之後,槽函式才會被呼叫。多執行緒環境下一般用這個。

Qt::BlockingQueuedConnection:槽函式的呼叫時機與Qt::QueuedConnection一致,不過傳送完訊號後傳送者所線上程會阻塞,直到槽函式執行完。接收者和傳送者絕對不能在一個執行緒,否則程式會死鎖。在多執行緒間需要同步的場合可能需要這個。

Qt::UniqueConnection:這個flag可以通過按位或(|)與以上四個結合在一起使用。當這個flag設定時,當某個訊號和槽已經連線時,再進行重複的連線就會失敗。也就是避免了重複連線。