江西省建設(shè)監(jiān)督網(wǎng)站收錄
1、Qt信號槽機(jī)制的優(yōu)勢
(1)類型安全。需要關(guān)聯(lián)的信號和槽的簽名必須是等同的,即信號的參數(shù)類型和參數(shù)個數(shù)同接收該信號的槽的參數(shù)類型和參數(shù)個數(shù)相同。不過,一個槽的參數(shù)個數(shù)是可以少于信號的參數(shù)個數(shù)的,但缺少的參數(shù)必須是信號參數(shù)的最后一個或幾個參數(shù)。如果信號和槽的簽名不符,編譯器就會報錯。
(2)松散耦合。信號和槽機(jī)制減弱了Qt對象的耦合度。激發(fā)信號的Qt對象無需知道是哪個對象的哪個槽需要接收它發(fā)出的信號,它只需在適當(dāng)?shù)臅r間發(fā)送適當(dāng)?shù)男盘柧涂梢粤?#xff0c;而不需要知道也不關(guān)心它的信號有沒有被接收到,更不需要知道是哪個對象的哪個槽收到了信號。同樣的,對象的槽也不知道是哪些信號關(guān)聯(lián)了自己,而一旦關(guān)聯(lián)信號和槽,Qt就保證了適合的槽得到了調(diào)用。即使關(guān)聯(lián)的對象在運(yùn)行時被刪除,應(yīng)用程序也不會崩潰。
(3)信號和槽機(jī)制增強(qiáng)了對象間通信的靈活性。一個信號可以關(guān)聯(lián)多個槽,也可以多個信號關(guān)聯(lián)一個槽。
2、Qt信號槽機(jī)制的不足
同回調(diào)函數(shù)相比,信號和槽機(jī)制運(yùn)行速度有些慢。通過傳遞一個信號來調(diào)用槽函數(shù)將會比直接調(diào)用非虛函數(shù)運(yùn)行速度慢10倍。原因如下:
(1)需要定位接收信號的對象;
(2)安全地遍歷所有的關(guān)聯(lián)(如一個信號關(guān)聯(lián)多個槽的情況);
(3)編組/解組傳遞的參數(shù);
(4)多線程的時候,信號可能需要排隊(duì)等待。
然而,與創(chuàng)建對象的new操作及刪除對象的delete操作相比,信號和槽的運(yùn)行代價只是他們很少的一部分。信號和槽機(jī)制導(dǎo)致的這點(diǎn)性能損耗,對實(shí)時應(yīng)用程序是可以忽略的。
3、請描述過程, 如何實(shí)現(xiàn)一個自定義按鈕, 使其在光標(biāo)進(jìn)入,按下,離開三種狀態(tài)下顯示不同的圖片.
創(chuàng)建一個類, 讓其從QPushButton類派生, 重寫該類中的事件處理器函數(shù)
方法一:
1>. enterEvent() – 光標(biāo)進(jìn)入
2>. leaveEvent() – 光標(biāo)離開
3>. mousePressEvent() – 鼠標(biāo)按下
4>. paintEvent() – 刷新背景圖
方法二:
通過setstylesheet設(shè)置
4. Qt信號和槽的本質(zhì)是什么
回調(diào)函數(shù)
5、描述QT中的文件流(QTextStream)和數(shù)據(jù)流(QDataStream)的區(qū)別, 他們都能幫助我們完成一些什么事情.
QTextStream – 文本流, 操作輕量級數(shù)據(jù)(int, double, QString), 數(shù)據(jù)寫入文件中之后以文本的方式呈現(xiàn)。
QDataStream – 數(shù)據(jù)流, 通過數(shù)據(jù)流可以操作各種數(shù)據(jù)類型, 包括類對象, 存儲到文件中數(shù)據(jù)可以還原到內(nèi)存(二進(jìn)制)。
QTextStream, QDataStream可以操作磁盤文件, 也可以操作內(nèi)存數(shù)據(jù), 通過流對象可以將數(shù)據(jù)打包到內(nèi)存, 進(jìn)行數(shù)據(jù)的傳輸.
6、描述Qt下Tcp通信的整個流程
服務(wù)器端:
- 創(chuàng)建用于監(jiān)聽的套接字
- 給套接字設(shè)置監(jiān)聽
- 如果有連接到來, 監(jiān)聽的套接字會發(fā)出信號newConnected
- 接收連接, 通過nextPendingConnection()函數(shù), 返回一個QTcpSocket類型的套接字對象(用于通信)
- 使用用于通信的套接字對象通信
1>. 發(fā)送數(shù)據(jù): write
2>. 接收數(shù)據(jù): readAll/read
客戶端: - 創(chuàng)建用于通信的套接字
- 連接服務(wù)器: connectToHost
- 連接成功與服務(wù)器通信
1>. 發(fā)送數(shù)據(jù): write
2>. 接收數(shù)據(jù): readAll/read
7、 描述QT下udp通信的整個流程
QT下udp通信服務(wù)器端和客戶端的關(guān)系是對等的, 做的處理也是一樣的.
- 創(chuàng)建套接字對象
- 如果需要接收數(shù)據(jù), 必須綁定端口
- 發(fā)送數(shù)據(jù): writeDatagram
- 接收數(shù)據(jù): readDatagram
8. 描述QT下多線程的兩種使用方法, 以及注意事項(xiàng)
方法一:
-
- 創(chuàng)建一個類從QThread類派生
-
- 在子線程類中重寫 run 函數(shù), 將處理操作寫入該函數(shù)中
-
- 在主線程中創(chuàng)建子線程對象, 啟動子線程, 調(diào)用start()函數(shù)
方法二:
-
- 將業(yè)務(wù)處理抽象成一個業(yè)務(wù)類, 在該類中創(chuàng)建一個業(yè)務(wù)處理函數(shù)
-
- 在主線程中創(chuàng)建一QThread類對象
-
- 在主線程中創(chuàng)建一個業(yè)務(wù)類對象
-
- 將業(yè)務(wù)類對象移動到子線程中
-
- 在主線程中啟動子線程
-
- 通過信號槽的方式, 執(zhí)行業(yè)務(wù)類中的業(yè)務(wù)處理函數(shù)
方法三:
QFuture fut1 = QtConcurrent::run(processFun, command);
processFun為線程回調(diào)函數(shù)
多線程使用注意事項(xiàng):
-
- 業(yè)務(wù)對象, 構(gòu)造的時候不能指定父對象
-
- 子線程中不能處理ui窗口(ui相關(guān)的類)
-
- 子線程中只能處理一些數(shù)據(jù)相關(guān)的操作, 不能涉及窗口
9、多線程下,信號槽分別在什么線程中執(zhí)行,如何控制
可以通過connect的第五個參數(shù)進(jìn)行控制信號槽執(zhí)行時所在的線程
connect有幾種連接方式,直接連接和隊(duì)列連接、自動連接
直接連接:信號槽在信號發(fā)出者所在的線程中執(zhí)行
隊(duì)列連接:信號在信號發(fā)出者所在的線程中執(zhí)行,槽函數(shù)在信號接收者所在的線程中執(zhí)行
自動連接:多線程時為隊(duì)列連接函數(shù),單線程時為直接連接函數(shù)。
10. 如何使用C++模擬Qt信號和槽
Qt的信號和槽原理就是回調(diào)函數(shù)。所以,我們需要保存對象綁定的回調(diào)函數(shù)
1. 創(chuàng)建槽類Slot,該類的功能是保存對象和對象綁定的回調(diào)函數(shù)
template<class T>
class SlotBase
{
public:virtual void Exec(T param1) = 0; //純虛函數(shù)virtual ~SlotBase(){}
};/*
* func: 槽函數(shù)
* parm:
* return:
*/
template<class T, class T1>
class Slot : public SlotBase<T1>
{
public:/* 定義Slot的時候,獲取槽函數(shù)信息 */Slot(T* pObj, void (T::*func)(T1)){m_pSlotBase = pObj;m_Func = func;}/* signal觸發(fā)時,調(diào)用 */void Exec(T1 param1){(m_pSlotBase->*m_Func)(param1);}private:/* 槽函數(shù)信息 暫存 */T* m_pSlotBase;void (T::*m_Func)(T1);
};
2. 創(chuàng)建signal類
重要闡述:
-
1.創(chuàng)建一個Signal 類,該類保主要是保存多個Slot對象,當(dāng)一個信號發(fā)送時,會遍歷這個表,對每一個slot綁定的回調(diào)函數(shù)進(jìn)行調(diào)用。
-
2.重載運(yùn)算符(), 遍歷這個表,調(diào)用回調(diào)函數(shù),即signal觸發(fā)機(jī)制
-
3.寫一個綁定函數(shù)Bind,用于將Slot對象添加到槽表中
template<class T1>
class Signal
{
public:/* 模板函數(shù) -> Bind時獲取槽函數(shù)指針 */template<class T>void Bind(T* pObj, void (T::*func)(T1)){m_pSlotSet.push_back(new Slot<T,T1>(pObj,func));}/* 重載操作符 -> signal觸發(fā)機(jī)制 */void operator()(T1 param1){for(int i=0;i<(int)m_pSlotSet.size();i++){m_pSlotSet[i]->Exec(param1);}}~Signal(){for(int i=0;i<(int)m_pSlotSet.size();i++){delete m_pSlotSet[i];}}private:vector<SlotBase<T1>*> m_pSlotSet; //這一句很重要,靠基類的指針來存儲 信號槽指針
};
3.測試類
測試類包含多個signal 當(dāng)調(diào)用接口就將調(diào)用signal的()函數(shù),從而調(diào)用slot
class TestSignal
{
public:TestSignal(){}void setValue(int value){emit ValueChanged(value);}void setfValue(int value){emit ValueChanged_f(value);}public slots:void FuncOfA(int parm){printf("enter FuncOfA parm = %d\n", parm);}void FuncOfB(int parm){printf("enter FuncOfB parm = %d\n", parm);}signals:Signal<int> ValueChanged;Signal<float> ValueChanged_f;
};
4.定義一個鏈接函數(shù)
#define Connect(sender, signal, receiver, method) ((sender)->signal.Bind(receiver, method))
11.QVariant使用
-
1、用戶自定義需要先注冊一個類型,即使用qRegisterMetaType,注冊到QT的一個Vector中
-
2、QVariant里面會new一個用戶自定義類型的內(nèi)存,并調(diào)用拷貝構(gòu)造函數(shù),QVariant自身的賦值會使用共享內(nèi)存管理
所以用戶可以傳入一個臨時變量地址,如果用戶傳入的是一個指針,這個指針需要用戶自己析構(gòu),改變這個指針的值,并不會改變QVariant,因?yàn)槭莾蓚€不同的空間了
而如果QVariant a1=b1(b1是QVariant),改變b1的值會改變a1的。因?yàn)檫@樣用的是shared指針
初看2以為是對的,驗(yàn)證發(fā)現(xiàn)不準(zhǔn)確,改變b1并沒有改變a1的值,細(xì)看發(fā)現(xiàn)這里面有QT使用了個小技巧,要取b1的值然后改變時,會調(diào)用data函數(shù)
CVariantHelp* pBTemp = reinterpret_cast<CVariantHelp*>(b1.data());
pBTemp->j_ = 99;
而data的實(shí)現(xiàn)會調(diào)用detach將shared分離
void* QVariant::data()
{
detach();
return const_cast<void *>(constData());
}
void QVariant::detach()
{
if (!d.is_shared || d.data.shared->ref == 1)
return;
Private dd;
dd.type = d.type;
handler->construct(&dd, constData());
if (!d.data.shared->ref.deref())handler->clear(&d);
d.data.shared = dd.data.shared;
}
12.Qt中的2指針
- QPointer
特點(diǎn):當(dāng)其指向的對象(T必須是QObject及其派生類)被銷毀時,它會被自動置NULL.
注意:它本身析構(gòu)時不會自動銷毀所guarded的對象
用途:當(dāng)你需要保存其他人所擁有的QObject對象的指針時,這點(diǎn)非常有
2.QScopedPointer QScopedArraytPointer與 std::unique_ptr/scoped_ptr
這是一個很類似auto_ptr的智能指針,它包裝了new操作符在堆上分配的動態(tài)對象,能夠保證動態(tài)創(chuàng)建的對象在任何時候都可以被正確地刪除。但它的所有權(quán)更加嚴(yán)格,不能轉(zhuǎn)讓,一旦獲取了對象的管理權(quán),你就無法再從它那里取回來。
無論是QScopedPointer 還是 std::unique_ptr 都擁有一個很好的名字,它向代碼的閱讀者傳遞了明確的信息:這個智能指針只能在本作用域里使用,不希望被轉(zhuǎn)讓。因?yàn)樗目截悩?gòu)造和賦值操作都是私有的,這點(diǎn)我們可以對比QObject及其派生類的對象哈。
- QSharedPointer QSharedArrayPointer 與 std::shared_ptr
QSharedPointer 與 std::shared_ptr 行為最接近原始指針,是最像指針的"智能指針",應(yīng)用范圍比前面的提到的更廣。
QSharedPointer 與 QScopedPointer 一樣包裝了new操作符在堆上分配的動態(tài)對象,但它實(shí)現(xiàn)的是引用計數(shù)型的智能指針 ,可以被自由地拷貝和賦值,在任意的地方共享它,當(dāng)沒有代碼使用(引用計數(shù)為0)它時才刪除被包裝的動態(tài)分配的對象。shared_ptr也可以安全地放到標(biāo)準(zhǔn)容器中,并彌補(bǔ)了std::auto_ptr 和 QScopedPointer 因?yàn)檗D(zhuǎn)移語義而不能把指針作為容器元素的缺陷。
- QWeakPointer 與 std::weak_ptr
強(qiáng)引用類型的QSharedPointer已經(jīng)非常好用,為什么還要有弱引用的 QWeakPointer?
QWeakPointer 是為配合 QSharedPointer 而引入的一種智能指針,它更像是 QSharedPointer 的一個助手(因?yàn)樗痪哂衅胀ㄖ羔樀男袨?#xff0c;沒有重載operator*和->)。它的最大作用在于協(xié)助 QSharedPointer 工作,像一個旁觀者一樣來觀測資源的使用情況。
weak_ptr 主要是為了避免強(qiáng)引用形成環(huán)狀。摘自msdn中一段話:
A cycle occurs when two or more resources controlled by shared_ptr objects hold mutually referencing shared_ptr objects. For example, a circular linked list with three elements has a head node N0; that node holds a shared_ptr object that owns the next node, N1; that node holds a shared_ptr object that owns the next node, N2; that node, in turn, holds a shared_ptr object that owns the head node, N0, closing the cycle. In this situation, none of the reference counts will ever become zero, and the nodes in the cycle will not be freed. To eliminate the cycle, the last node N2 should hold a weak_ptr object pointing to N0 instead of a shared_ptr object. Since the weak_ptr object does not own N0 it doesn’t affect N0’s reference count, and when the program’s last reference to the head node is destroyed the nodes in the list will also be destroyed.
在Qt中,對于QObject及其派生類對象,QWeakPointer有特殊處理。它可以作為QPointer的替代品
這種情況下,不需要QSharedPointer的存在
5. QSharedDataPointer
這是為配合 QSharedData 實(shí)現(xiàn)隱式共享(寫時復(fù)制 copy-on-write))而提供的便利工具。
Qt中眾多的類都使用了隱式共享技術(shù),比如QPixmap、QByteArray、QString、…。而我們?yōu)樽约旱念悓?shí)現(xiàn)隱式共享也很簡單,比如要實(shí)現(xiàn)一個 Employee類:
定義一個只含有一個數(shù)據(jù)成員(QSharedDataPointer) 的 Employee 類
我們需要的所有數(shù)據(jù)成員放置于 派生自QSharedData的 EmployeeData類中。
- QExplicitlySharedDataPointe
這是為配合 QSharedData 實(shí)現(xiàn)顯式共享而提供的便利工具。
QExplicitlySharedDataPointer 和 QSharedDataPointer 非常類似,但是它禁用了寫時復(fù)制功能。這使得我們創(chuàng)建的對象更像一個指針。
13. QT的d和p指針
保持一個庫中的所有公有類的大小恒定的問題可以通過單獨(dú)的私有指針給予解決。這個指針指向一個包含所有數(shù)據(jù)的私有數(shù)據(jù)結(jié)構(gòu)體。這個結(jié)構(gòu)體的大小可以隨意改變而不會產(chǎn)生副作用,應(yīng)用程序只使用相關(guān)的公有類,所使用的對象大小永遠(yuǎn)不會改變,它就是該指針的大小。這個指針就被稱作D指針。
D指針的其他好處
1.隱藏實(shí)現(xiàn)細(xì)節(jié)——我們可以不提供widget.cpp文件而只提供WidgetLib和相應(yīng)的頭文件和二進(jìn)制文件。
2.頭文件中沒有任何實(shí)現(xiàn)細(xì)節(jié),可以作為API使用。
3.由于原本在頭文件的實(shí)現(xiàn)部分轉(zhuǎn)移到了源文件,所以編譯速度有所提高。
4.二進(jìn)制兼容
其實(shí)以上的點(diǎn)都很細(xì)微,自己跟過源代碼的人都會了解,qt是隱藏了d指針的管理和核心源的實(shí)現(xiàn)。像是在_p.h中部分函數(shù)的聲明,qt也宣布在以后版本中將會刪除。( This file is not part of the Qt API. It exists purely as an implementation detail. This header file may change from version to version without notice, or even be removed.)
q_ptr指針指向父類,使用如下宏定義輔助函數(shù)和聲明友元類
#ifndef D_PTR_H
#define D_PTR_H#include <QObject>template <typename T> static inline T *GetPtrHelper(T *ptr) { return ptr; }#define DECLARE_PRIVATE(Class) \inline Class##Private* d_func() { return reinterpret_cast<Class##Private*>(GetPtrHelper(d_ptr)); } \inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private*>(GetPtrHelper(d_ptr)); }\friend class Class##Private;#define DPTR(Class) Class##Private * const d = d_func()class MyClassPrivate;class MyClass : public QObject {Q_OBJECT
public:explicit MyClass(QObject *parent = 0);virtual ~MyClass();void testFunc();protected:MyClass(MyClassPrivate &d);private:MyClassPrivate * const d_ptr;DECLARE_PRIVATE(MyClass);MyClass(const MyClass&);MyClass& operator= (const MyClass&);
};#endif #ifndef Q_PTR_H
#define Q_PTR_H#include <QObject>
#include "d_ptr.h"#define DECLARE_PUBLIC(Class) \inline Class* q_func() { return static_cast<Class *>(q_ptr); } \inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \friend class Class;#define QPTR(Class) Class * const q = q_func()class MyClassPrivate : public QObject
{
Q_OBJECTpublic:MyClassPrivate(MyClass *q, QObject *parent = 0);virtual ~MyClassPrivate() {}signals:void testSgnl();private slots:void testSlt();public:void fool();private:MyClass * const q_ptr;DECLARE_PUBLIC(MyClass);
};#endif