柚子快報邀請碼778899分享:c++ QT經(jīng)驗手記
柚子快報邀請碼778899分享:c++ QT經(jīng)驗手記
復(fù)制這段內(nèi)容打開「百度網(wǎng)盤APP 即可獲取」 https://pan.baidu.com/s/1cP2NZUhntvbWGLl1hRU66g
1、講述Qt信號槽機制與優(yōu)勢與不足
優(yōu)點: ①類型安全。需要關(guān)聯(lián)的信號槽的簽名必須是等同的。即信號的參數(shù)類型和參數(shù)個數(shù)同接受該信號的槽的參數(shù)類型和參數(shù)個數(shù)相同。若信號和槽簽名不一致,編譯器會報錯。
②松散耦合。信號和槽機制減弱了Qt對象的耦合度。激發(fā)信號的Qt對象無需知道是那個對象的那個信號槽接收它發(fā)出的信號,它只需在適當(dāng)?shù)臅r間發(fā)送適當(dāng)?shù)男盘柤纯桑恍枰P(guān)心是否被接受和那個對象接受了。Qt就保證了適當(dāng)?shù)牟鄣玫搅苏{(diào)用,即使關(guān)聯(lián)的對象在運行時被刪除。程序也不會奔潰。
③靈活性。一個信號可以關(guān)聯(lián)多個槽,或多個信號關(guān)聯(lián)同一個槽。
**不足:**速度較慢。與回調(diào)函數(shù)相比,信號和槽機制運行速度比直接調(diào)用非虛函數(shù)慢10倍。
原因: ①需要定位接收信號的對象。 ②安全地遍歷所有關(guān)聯(lián)槽。 ③編組、解組傳遞參數(shù)。 ④多線程的時候,信號需要排隊等待。(然而,與創(chuàng)建對象的new操作及刪除對象的delete操作相比,信號和槽的運行代價只是他們很少的一部分。信號和槽機制導(dǎo)致的這點性能損耗,對實時應(yīng)用程序是可以忽略的。)
2、Qt信號和槽的本質(zhì)是什么
Qt信號和槽是一種用于實現(xiàn)對象間通信的機制。
其本質(zhì)是一種事件驅(qū)動的機制,通過信號和槽的連接,當(dāng)信號被觸發(fā)時,槽函數(shù)會被自動調(diào)用,從而實現(xiàn)對象間的通信和交互。 信號是一種特殊的函數(shù),用于表示某種事件的發(fā)生,當(dāng)事件發(fā)生時,信號會被自動發(fā)送出去,通知所有連接到該信號的槽函數(shù)。 槽是一種特殊的函數(shù),用于處理信號的觸發(fā)事件,當(dāng)槽函數(shù)被連接到某個信號時,當(dāng)該信號被觸發(fā)時,槽函數(shù)會自動被調(diào)用,從而實現(xiàn)對信號的響應(yīng)。 信號和槽的連接是通過Qt的元對象系統(tǒng)實現(xiàn)的,每個QObject派生類都有一個元對象,用于存儲該類的屬性、方法和信號槽信息。 通過元對象系統(tǒng),可以在運行時動態(tài)地連接信號和槽,從而實現(xiàn)對象間的通信。 總之,Qt信號和槽是一種事件驅(qū)動的機制,通過信號和槽的連接,實現(xiàn)了對象間的通信和交互。
3、描述Qt中的文件流(QTextStream)和數(shù)據(jù)流(QDataStream)的區(qū)別
在Qt中,QTextStream和QDataStream都是用于讀寫數(shù)據(jù)的類,但它們的使用場景和讀寫的數(shù)據(jù)類型不同。 QTextStream是用于讀寫文本數(shù)據(jù)的類,可以將QString、QByteArray等文本數(shù)據(jù)以類似于流的方式讀寫到文件、套接字等設(shè)備中。 QTextStream提供了一系列方便的方法,如readLine()、readAll()、operator<<()、operator>>()等,可以方便地讀寫文本文件和套接字等設(shè)備。 QDataStream是用于讀寫二進制數(shù)據(jù)的類,可以將各種類型的二進制數(shù)據(jù)(如int、float、double、QString等)以二進制方式讀寫到文件、套接字等設(shè)備中。 QDataStream通過內(nèi)部的序列化機制實現(xiàn)了數(shù)據(jù)的高效讀寫,可以保證在不同平臺上的數(shù)據(jù)兼容性。QDataStream提供了一系列方便的方法,如writeRawData()、writeInt()、readRawData()、readInt()等,可以方便地讀寫二進制文件和套接字等設(shè)備。 因此,QTextStream和QDataStream的主要區(qū)別在于它們所處理的數(shù)據(jù)類型不同,QTextStream主要處理文本數(shù)據(jù),而QDataStream主要處理二進制數(shù)據(jù)。在使用時,需要根據(jù)實際情況選擇合適的類進行讀寫操作。
4、描述Qt的TCP通訊流程
服務(wù)端:(QTcpServer)
①創(chuàng)建QTcpServer對象
②監(jiān)聽list需要的參數(shù)是地址和端口號
③當(dāng)有新的客戶端連接成功回發(fā)送newConnect信號
④在newConnection信號槽函數(shù)中,調(diào)用nextPendingConnection函數(shù)獲取新連接QTcpSocket對象
⑤連接QTcpSocket對象的readRead信號
⑥在readRead信號的槽函數(shù)使用read接收數(shù)據(jù)
⑦調(diào)用write成員函數(shù)發(fā)送數(shù)據(jù)
客戶端:(QTcpSocket)
? ①創(chuàng)建QTcpSocket對象
? ②當(dāng)對象與Server連接成功時會發(fā)送connected 信號
? ③調(diào)用成員函數(shù)connectToHost連接服務(wù)器,需要的參數(shù)是地址和端口號
? ④connected信號的槽函數(shù)開啟發(fā)送數(shù)據(jù)
? ⑤使用write發(fā)送數(shù)據(jù),read接收數(shù)據(jù)
5、 描述UDP 之 UdpSocket通訊
UDP(User Datagram Protocol即用戶數(shù)據(jù)報協(xié)議)是一個輕量級的,不可靠的,面向數(shù)據(jù)報的無連接協(xié)議。在網(wǎng)絡(luò)質(zhì)量令人十分不滿意的環(huán)境下,UDP協(xié)議數(shù)據(jù)包丟失嚴(yán)重。 由于UDP的特性:它不屬于連接型協(xié)議,因而具有資源消耗小,處理速度快的優(yōu)點,所以通常音頻、視頻和普通數(shù)據(jù)在傳送時使用UDP較多,因為它們即使偶爾丟失一兩個數(shù)據(jù)包,也不會對接收結(jié)果產(chǎn)生太大影響。 所以QQ這種對保密要求并不太高的聊天程序就是使用的UDP協(xié)議。
在Qt中提供了QUdpSocket 類來進行UDP數(shù)據(jù)報(datagrams)的發(fā)送和接收。Socket簡單地說,就是一個IP地址加一個port端口 。
流程: ①創(chuàng)建QUdpSocket套接字對象 ②如果需要接收數(shù)據(jù),必須綁定端口 ③發(fā)送數(shù)據(jù)用writeDatagram,接收數(shù)據(jù)用 readDatagram 。
6、多線程使用使用方法
方法一: ①創(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ù)
多線程使用注意事項:
* 1. 業(yè)務(wù)對象, 構(gòu)造的時候不能指定父對象
* 2. 子線程中不能處理ui窗口(ui相關(guān)的類)
* 3. 子線程中只能處理一些數(shù)據(jù)相關(guān)的操作, 不能涉及窗口
7、多線程下,信號槽分別在什么線程中執(zhí)行,如何控制
可以通過connect的第五個參數(shù)進行控制信號槽執(zhí)行時所在的線程
connect有幾種連接方式,直接連接和隊列連接、自動連接
直接連接(Qt::DirectConnection):信號槽在信號發(fā)出者所在的線程中執(zhí)行
隊列連接 (Qt::QueuedConnection):信號在信號發(fā)出者所在的線程中執(zhí)行,槽函數(shù)在信號接收者所在的線程中執(zhí)行
自動連接 (Qt::AutoConnection):多線程時為隊列連接函數(shù),單線程時為直接連接函數(shù)。
8、自定義控件流程
繼承需要自定義的控件類,如QPushButton; 從外觀設(shè)計上:QSS、繼承繪制函數(shù)重繪、繼承QStyle相關(guān)類重繪、組合拼裝等等; 從功能行為上:重寫事件函數(shù)、添加或者修改信號和槽等等。
9、對QObject的理解
QObject 類是Qt 所有類的基類。
QObject是Qt對象模型的核心。這個模型的中心要素就是一種強大的叫做信號與槽無縫對象溝通機制。你可以用 connect() 函數(shù)來把一個信號連接到槽,也可以用disconnect() 函數(shù)來破壞這個連接。為了避免永無止境的通知循環(huán),你可以用blockSignal() 函數(shù)來暫時阻塞信號。保護函數(shù) connectNotify() 和 disconnectNotify() 可以用來跟蹤連接。
對象樹都是通過QObject 組織起來的,當(dāng)以一個對象作為父類創(chuàng)建一個新的對象時,這個新對象會被自動加入到父類的 children() 隊列中。這個父類有子類的所有權(quán)。能夠在父類的析構(gòu)函數(shù)中自動刪除子類??梢酝ㄟ^findChild()和findChildren() 函數(shù)來尋找子類。
每個對象都一個對象名稱objectName() ,而且它的類名也可以通過metaObject()函數(shù)。你可以通過inherits() 函數(shù)來決定一個類是否繼承其他的類。當(dāng)一個對象被刪除時,它會發(fā)射destory() 信號,你可以抓住這個信號避免某些事情。
對象可以通過event() 函數(shù)來接收事情以及過濾來自其他對象的事件。就好比installEventFiter() 函數(shù)和eventFilter() 函數(shù)。childEvent() 函數(shù)能夠重載實現(xiàn)子對象的事件。
QObject還提供了基本的時間支持,QTimer類 提高了更高層次的時間支持。
任何對象要實現(xiàn)信號與槽機制,Q_OBJECT 宏都是強制的。你也需要在源原件上運行元對象編譯器。不管是否真正用到信號與槽機制,最好在所有QObject子類使用Q_OBJECT宏,以避免出現(xiàn)一些不必要的錯誤。
所有的Qt widgets 都是基礎(chǔ)QObject。如果一個對象是widget,那么isWidgetType()函數(shù)就能判斷出。
10、Qt自定義一個信號槽,觸發(fā)這個信號,Qt多個信號如何關(guān)聯(lián)一并處理
第一種方法:
在發(fā)送信號時,也發(fā)送一個int類型數(shù)字,或者說標(biāo)志,這樣在槽函數(shù)觸發(fā)是可以知道是哪個信號發(fā)出的;
第二種方法:
在槽函數(shù)內(nèi)有獲取發(fā)送信號的函數(shù),通過sender()函數(shù)獲取發(fā)送信號;
11、Qt如果一個信號的處理方法一直未被執(zhí)行有哪些可能性
1.斷開了
2.連接的時候失敗了
3.多線程的時候在排隊或者啟動鎖死了。
12、在Qt5的信號處理中如何使用lambda機制(可以代碼示例)
信號定義了,但是不寫對應(yīng)槽函數(shù),直接將函數(shù)寫到槽的位置。
connect(musicPlayer,SIGNAL(positionChanged(qint64)),this,SLOT(slotReflushStartTime(qint64)));
connect(musicPlayer,SIGNAL(positionChanged(qint64)),slotReflushStartTime(qint64));
直接就是將對象都不寫了,直接寫個函數(shù)。
13、段錯誤一般是什么原因造成的,如何快速排查
一般是指針的問題,出現(xiàn)野指針空指針;點燈或者用Debug去排查問題。
14、Qt定義面設(shè)計類,如果想自定義控件,只能通過寫代碼的方式嗎?
不一定。在Qt中,自定義控件可以通過寫代碼的方式實現(xiàn),也可以通過Qt Designer的插件機制來實現(xiàn)。
通過寫代碼的方式,可以繼承QWidget或其子類,并在其中實現(xiàn)自己的功能和界面。
可以根據(jù)需要添加、刪除、修改控件,實現(xiàn)自己的布局和樣式,甚至可以處理自定義的事件。通過這種方式,可以實現(xiàn)非常靈活、個性化的控件,但需要一定的編程能力和時間成本。
通過Qt Designer的插件機制,可以擴展Qt Designer的控件庫, 添加自定義控件??梢酝ㄟ^繼承QDesignerCustomWidgetInterface類,并在其中實現(xiàn)自己的功能和界面,然后將插件編譯成動態(tài)鏈接庫,就可以在Qt Designer中使用自定義控件了。這種方式相對于寫代碼的方式,更加便捷和可視化,但需要一定的Q開發(fā)經(jīng)驗和插件開發(fā)知識。
因此,自定義控件的方式可以根據(jù)具體情況選擇,根據(jù)自己的需求和技術(shù)水平選擇最適合自己的方式。
15、Qt 三大核心機制
信號槽
信號槽的五種連接方式(圖略)
connect(信號發(fā)出者,信號,信號接收者,槽,連接方式(隱藏默認(rèn)自動連接))//五個參數(shù)
元對象系統(tǒng)
元對象系統(tǒng)分為三大類:QObject類、Q_OBJECT宏和元對象編譯器moc
Qt的類包含Q_OBJECT宏 moc編譯器會對該類編譯成標(biāo)準(zhǔn)的C++代碼
事件模型
事件的創(chuàng)建
鼠標(biāo)事件,鍵盤事件,窗口調(diào)整事件,模擬事件
事件的交付
Qt通過調(diào)用虛函數(shù)QObject::event()來交付事件。
事件循環(huán)模型
主事件循環(huán)通過調(diào)用QCoreApplication::exec()啟動,
隨著QCoreApplication::exit()結(jié)束,
本地的事件循環(huán)可用利用QEventLoop構(gòu)建。
一般來說,事件是由觸發(fā)當(dāng)前的窗口系統(tǒng)產(chǎn)生的,但也可以通過使用 QCoreApplication::sendEvent()和
QCoreApplication::postEvent()來手工產(chǎn)生事件。需要說明的是QCoreApplication::sendEvent()會立即發(fā)送事件, QCoreApplication::postEvent()則會將事件放在事件隊列中分發(fā)。
自定義事件
16、Qt對象樹
QT提供了對象樹機制,能夠自動、有效的組織和管理繼承自QObject的對象。
每個繼承自QObject類的對象通過它的對象鏈表(QObjectList)來管理子類對象,當(dāng)用戶創(chuàng)建一個子對象時,其對象鏈表相應(yīng)更新子類對象的信息,對象鏈表可通過children()獲取。
當(dāng)父類對象析構(gòu)的時候,其對象鏈表中的所有(子類)對象也會被析構(gòu),父對象會自動,將其從父對象列表中刪除,QT保證沒有對象會被delete兩次。開發(fā)中手動回收資源時建議使用deleteLater代替delete,因為deleteLater多次是安全的。
17、描述QTextStream(文件流)和QDataStram(數(shù)據(jù)流)的區(qū)別
文本流用來操作輕量級的數(shù)據(jù),比如內(nèi)置的int、QString等,寫入文件后以文本的方式呈現(xiàn)
數(shù)據(jù)流,可以操作各種類型數(shù)據(jù)
總之,兩者都可以進行操作磁盤文件以及內(nèi)存數(shù)據(jù)。
18、信號槽的四種寫法和五種連接方式?
connect(信號發(fā)出者,信號,信號接收者,槽,連接方式(隱藏默認(rèn)自動連接))//五個參數(shù)
四種寫法:
1.用宏: connect(this,SIGNAL(clicked()),this,SLOT(colse())); //連接方式(隱藏默認(rèn)自動連接))
2.用函數(shù)指針: connect(this,&mainwindow::my_signal,this,&mainwindow::my_slot);
3.用重載函數(shù)指針Qoverload connect(this,Qoverload<參數(shù)>::of(&mainwindow::my_signal),this,Qoverload<參數(shù)>::of(&mainwindow::my_slot));
4.lambda表達(dá)式(匿名函數(shù)) 匿名函數(shù)代替槽 connect(this,&mainwindow::my_signal,this,[=]{qDebug()<<100;}); 連接方式:自動連接(默認(rèn)連接方式) 直接連接(用于單線程,自動匹配) 隊列(用于多線程也可用于單線程,自動匹配) 阻塞隊列(跨線程,多線程) 唯一連接(跨線程,多線程)
19、Qt模型
Qt中的View主要有三種QListView,QTreeView, QTabelView
而對應(yīng)的Model是:QStringListModel, QAbstractItemModel , QStandardItemModel。
抽象 標(biāo)準(zhǔn)
20、Qt中的MVD了解吧?
Qt的MVD包含三個部分Model(模型),View(視圖),代理(Delegate)。Model否則保存數(shù)據(jù),View負(fù)責(zé)展示數(shù)據(jù),Delegate負(fù)責(zé)Item樣式繪制或處理輸入。這三部分通過信號槽來進行通信,當(dāng)Model中數(shù)據(jù)發(fā)生變化時將會發(fā)送信號到View,在View中編輯數(shù)據(jù)時,Delegate負(fù)責(zé)將編輯狀態(tài)發(fā)送給Model層。基類分別為QAbstractItemModel、QAbstractItemView、QAbstractItemDelegate。Qt中提供了默認(rèn)實現(xiàn)的MVD類,如QTableWidget、QListWidget、QTreeWidget等。
21、Qt如果要進行網(wǎng)絡(luò)編程首先需要在.pro中添加如下代碼 QT network
1、在頭文件中包含相關(guān)頭文件 include QHostInfo include QNetworkInterface 因無法顯示 略去#與 )
2、QT的UdpSocket接收消息使用原則 第一步 new一個UdpSocket 第二步 調(diào)用UdpSocket的bind方法 同時指定端口號 第三步 使用connect將接收消息函數(shù)和UdpSocket對象做關(guān)聯(lián) 第四步 在接受消息槽函數(shù)當(dāng)中調(diào)用readDatagram接收消息
22、static和const的使用
static:靜態(tài)變量聲明,分為局部靜態(tài)變量,全局靜態(tài)變量,類靜態(tài)成員變量。也可修飾類成員函數(shù)。有以下幾類:
局部靜態(tài)變量:存儲在靜態(tài)存儲區(qū),程序運行期間只被初始化一次,作用域仍然為局部作用域,在變量定義的函數(shù)或語句塊中有效,程序結(jié)束時由操作系統(tǒng)回收資源。
全局靜態(tài)變量:存儲在靜態(tài)存儲區(qū),靜態(tài)存儲區(qū)中的資源在程序運行期間會一直存在,直到程序結(jié)束由系統(tǒng)回收。未初始化的變量會默認(rèn)為0,作用域在聲明他的文件中有效。
類靜態(tài)成員變量:被類的所有對象共享,包括子對象。必須在類外初始化,不可以在構(gòu)造函數(shù)內(nèi)進行初始化。
類靜態(tài)成員函數(shù):所有對象共享該函數(shù),不含this指針,不可使用類中非靜態(tài)成員。
? const:常量聲明,類常成員函數(shù)聲明。 const和static不可同時修飾類成員函數(shù),const修飾成員函數(shù)表示不能修改對象的狀態(tài),static修飾成員函數(shù)表示該函數(shù)屬于類,不屬于對象,二者相互矛盾。const修飾變量時表示變量不可修改,修飾成員函數(shù)表示不可修改任意成員變量。
23、指針和引用的異同
指針:是一個變量,但是這個變量存儲的是另一個變量的地址,我們可以通過訪問這個地址來修改變量。
? 引用:是一個別名,還是變量本身。對引用進行的任何操作就是對變量本身進行的操作。
? 相同點:二者都可以對變量進行修改。
? 不同點:指針可以不必須初始化,引用必須初始化。指針可以有多級,但是引用只有一級(int&& a不合法, int** p合法)。指針在初始化后可以改變,引用不能進行改變,即無法再對另一個同類型對象進行引用。sizeof指針可以得到指針本身大小,sizeof引用得到的是變量本身大小。指針傳參還是值傳遞,引用傳參傳的是變量本身。
24、常用數(shù)據(jù)結(jié)構(gòu)
vector:向量,連續(xù)存儲,可隨機訪問。
deque:雙向隊列,連續(xù)存儲,隨機訪問。
list:鏈表,內(nèi)存不連續(xù),不支持隨機訪問。
stack:棧,不可隨機訪問,只允許再開頭增加/刪除元素。
queue:單向隊列,尾部增加,開頭刪除。
set:集合,采用紅黑樹實現(xiàn),可隨機訪問。查找、插入、刪除時間復(fù)雜度為O(logn)。
map:圖,采用紅黑樹實現(xiàn),可隨機訪問。查找、插入、刪除時間復(fù)雜度為O(logn)。
hash_set:哈希表,隨機訪問。查找、插入、刪除時間復(fù)雜讀為O(1)。
25、談一談你對面向?qū)ο蟮睦斫?/p>
C++面向?qū)ο缶幊叹褪前岩磺惺挛锒甲兂梢粋€個對象,用屬性和方法來描述對象的信息,比如定義一個貓對象,貓的眼睛、毛發(fā)、嘴巴就可以定義為貓對象的屬性,貓的叫聲和走路就可以定義為貓對象的方法。
用對象的方式編程,不僅方便了程序員,也使得代碼的可復(fù)用性、可維護性變好。
C++面向?qū)ο蟮娜筇匦允欠庋b、繼承、多態(tài)。
26、什么場景下使用繼承方式,什么場景下使用組合?
? 繼承:通過擴展已有的類來獲得新功能的代碼重用方法
? 組合:新類由現(xiàn)有類的對象合并而成的類的構(gòu)造方式
使用場景:
如果二者存在一個“是”的關(guān)系,并且一個類要對另外一個類公開所有接口,那么繼承是更好的選擇 如果二者間存在一個“有”的關(guān)系,那么首選組合 如果沒有找到及其強烈無法辯駁的使用繼承的的理由的時候,一律使用組合。組合體現(xiàn)為現(xiàn)實層面,繼承主要體現(xiàn)在擴展方面。如果并不需要一個類的所有東西(包括接口和熟悉),那么就不需要使用繼承,使用組合更好。如果使用繼承,那么必須所有的都繼承,如果有的東西你不需要繼承但是你繼承了,那么這就是濫用繼承。
27、如何理解多態(tài)
? 定義:同一操作作用于不同的對象,產(chǎn)生不同的執(zhí)行結(jié)果。C++多態(tài)意味著當(dāng)調(diào)用虛成員函數(shù)時,會根據(jù)調(diào)用類型對象的實際類型執(zhí)行不同的操作。
? 實現(xiàn):通過虛函數(shù)實現(xiàn),用virtual聲明的成員函數(shù)就是虛函數(shù),允許子類重寫。聲明基類的指針或者引用指向不同的子類對象,調(diào)用相應(yīng)的虛函數(shù),可以根據(jù)指針或引用指向的子類的不同從而執(zhí)行不同的操作。
? Overload(重載):函數(shù)名相同,參數(shù)類型或順序不同的函數(shù)構(gòu)成重載。
? Override(重寫):派生類覆蓋基類用virtual聲明的成員函數(shù)。
? Overwrite(隱藏):派生類的函數(shù)屏蔽了與其同名的基類函數(shù)。派生類的函數(shù)與基類函數(shù)同名,但是參數(shù)不同,隱藏基類函數(shù)。如果參數(shù)相同,但是基類沒有virtual關(guān)鍵字,基類函數(shù)將被隱藏。
28、虛函數(shù)表
? 多態(tài)是由虛函數(shù)實現(xiàn)的,而虛函數(shù)主要是通過虛函數(shù)表實現(xiàn)的。如果一個類中包含虛函數(shù),那么這個類就會包含一張?zhí)摵瘮?shù)表,虛函數(shù)表存儲的每一項是一個虛函數(shù)的地址。該類的每個對象都會包含一個虛指針(虛指針存在于對象實例地址的最前面,保證虛函數(shù)表有最高的性能),需指針指向虛函數(shù)表。注意:對象不包含虛函數(shù)表,只有需指針,類才包含虛函數(shù)表,派生類會生成一個兼容基類的虛函數(shù)表。
29、分別寫出餓漢和懶漢線程安全的單例模式
? 單例模式:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點,該實例被所有程序模塊共享。根據(jù)單例對象創(chuàng)建時間,可分為兩種模式:懶漢模式和餓漢模式。
? 懶漢模式:延遲加載,不到萬不得已不會去實例化類,也就是說第一次用到類實例的時候才會實例化。
#include
#include
using namespace::std;
// 懶漢模式一:多線程不安全
template
class Singleton
{
public:
static T* getInstance()
{
if (instance_ == nullptr)
{
instance_ = new T();
}
return instance_;
}
private:
Singleton() = delete;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
static T* instance_;
};
template
T* Singleton
// 懶漢模式二:多線程安全
template
class Singleton2
{
public:
static T* getInstance()
{
if (instance_ == nullptr)
{
mutex_.lock();
if (instance_ == nullptr)
{
instance_ = new T();
}
mutex_.unlock();
}
return instance_;
}
private:
Singleton2() = delete;
Singleton2(const Singleton2&) = delete;
Singleton2& operator=(const Singleton2&) = delete;
private:
static T* instance_;
static mutex mutex_;
};
template
T* Singleton2
template
mutex Singleton2
class Printer
{
friend class Singleton
friend class Singleton2
private:
Printer() = default;
Printer(const Printer&) = delete;
Printer& operator=(const Printer&) = delete;
public:
void print() { cout << "Printer" << endl; }
};
int main(int argc, char* argv[])
{
Singleton
Singleton2
}
餓漢模式:在單例類定義的時候(即在main函數(shù)之前)就進行實例化。因為main函數(shù)執(zhí)行之前,全局作用域的類成員變量instance_已經(jīng)初始化,故沒有多線程的問題。
#include
#include
using namespace::std;
// 餓漢模式
template
class Singleton
{
private:
Singleton() = delete;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static T* getInstance()
{
return instance_;
}
private:
static T* instance_;
};
template
T* Singleton
class Printer
{
friend class Singleton
private:
Printer() = default;
Printer(const Printer&) = delete;
Printer& operator=(const Printer&) = delete;
public:
void print() { cout << "Printer" << endl; }
};
int main(int argc, char* argv[])
{
Singleton
}
30、說出觀察者模式類關(guān)系和優(yōu)點
定義了對象之間一對多的依賴,令多個觀察者對象同時監(jiān)聽某一個主題對象,當(dāng)主題對象發(fā)生改變時,所有的觀察者都會收到通知并更新。
優(yōu)點:
抽象耦合:在觀察者和被觀察者之間,建立了一個抽象的耦合,由于耦合是抽象的,可以很容易擴展觀察者和被觀察者。 廣播通信:觀察者模式支持廣播通信,類似于消息傳播,如果需要接收消息,只需要注冊一下即可。
缺點:
依賴過多:觀察者之間細(xì)節(jié)依賴過多,會增加時間消耗和程序的復(fù)雜程度。這里的細(xì)節(jié)依賴指的是觸發(fā)機制,觸發(fā)鏈條,如果觀察者設(shè)置過多,每次觸發(fā)都要花很長時間去處理通知。 循環(huán)調(diào)用:避免循環(huán)調(diào)用,觀察者和被觀察者之間絕對不允許循環(huán)依賴,否則會觸發(fā)二者之間的循環(huán)調(diào)用,導(dǎo)致系統(tǒng)崩潰。
31、說出代理模式類關(guān)系和優(yōu)點
優(yōu)點:
代理模式能將代理對象與真實被調(diào)用的目標(biāo)對象隔離。 一定程度上降低了系統(tǒng)的耦合度,擴展性好。 可以起到保護目標(biāo)對象的作用。 可以對目標(biāo)對象的功能增強。
缺點:
代理模式會造成系統(tǒng)設(shè)計中類的數(shù)量增加。 在客戶端和目標(biāo)對象增加一個代理對象,會造成請求處理速度變慢。
32、說出工廠模式概念和優(yōu)點
定義一個創(chuàng)建產(chǎn)品對象的工廠接口,將產(chǎn)品對象的實際創(chuàng)建工作推遲到具體子工廠類當(dāng)中。這滿足創(chuàng)建型模式中所要求的“創(chuàng)建與使用相分離”的特點。簡單工廠模式可以決定在什么時候創(chuàng)建哪一個產(chǎn)品類的實例。工廠方法模式有非常良好的擴展性。抽象工廠模式降低了模塊間的耦合性,提高了團隊開發(fā)效率。
33、說出構(gòu)造者模式概念
? 構(gòu)造者模式是較為復(fù)雜的創(chuàng)建型模式,它將客戶端與包含多個組成部分的復(fù)雜對象的創(chuàng)建過程分離??蛻舳藷o需知道具體的構(gòu)造過程,只需要與構(gòu)造器打交道即可,構(gòu)建與表示分離。
34、說出適配器模式概念
將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口,使得原本由于接口不兼容而不能在一起工作的那些類一起工作。
35、進程和線程的區(qū)別?
? 進程的定義:一個具有一定獨立功能的程序在一個數(shù)據(jù)集合上依次動態(tài)執(zhí)行的過程。進程是一個正在執(zhí)行程序的實例,包括程序計數(shù)器、寄存器和程序變量的當(dāng)前值。簡單來說,進程就是一個程序的執(zhí)行流程,內(nèi)部保存程序運行所需的資源。在操作系統(tǒng)中可以有多個進程在運行,可對于CPU來說,同一時刻,一個CPU只能運行一個進程,但在某一時間段內(nèi),CPU將這一時間段拆分成更短的時間片,CPU不停的在各個進程間游走,這就給人一種并行的錯覺,像CPU可以同時運行多個進程一樣,這就是偽并行。
? 線程的定義:線程是進程當(dāng)中的一條執(zhí)行流程,這幾乎就是進程的定義,一個進程內(nèi)可以有多個子執(zhí)行流程,即線程。從資源組合的角度看,進程把一組相關(guān)的資源組合起來,構(gòu)成一個資源平臺環(huán)境,包括地址空間(代碼段,數(shù)據(jù)段),打開的文件等各種資源。從運行的角度看:進程是代碼在這個資源平臺上的執(zhí)行流程,然而線程貌似也是這樣,但是進程比線程多了資源內(nèi)容列表樣式:進程 = 線程 + 共享資源。
? 進程是操作系統(tǒng)分配資源的單位,線程是調(diào)度的基本單位,線程之間共享進程資源。
36、進程之間的通信方式有哪些?
管道消息隊列共享內(nèi)存信號量套接字文件
37、信號和信號量的區(qū)別是什么?
? 信號:一種處理異步事件的方式。信號是比較復(fù)雜的通信方式,用于通知接收進程有某種事件發(fā)生,除了用于進程外,還可以發(fā)送信號給進程本身。
? 信號量:進程間通信處理同步互斥的機制。是在多線程環(huán)境下使用的一種設(shè)施,它負(fù)責(zé)協(xié)調(diào)各個線程,以保證它們能夠正確,合理的使用公共資源。
38、你覺得自定義控件的方法主要是哪些?
從外觀設(shè)計上:QSS、繼承繪制函數(shù)重繪、繼承QStyle相關(guān)類重繪、組合拼裝等等。
從功能行為上:重寫事件函數(shù)、添加或者修改信號和槽等等
39、QSS平時使用的多嗎?能舉幾個例子嗎?
1.將QSS統(tǒng)一寫在一個文件中,通過程序給主窗口加載;
2.寫成一個字符串中,通過程序給主窗口加載;
3.需要使用的地方,寫一個字符串,加載給對象;
4.QT Designer中填寫;
40、Qt程序是事件驅(qū)動的,事件到處都可以遇到。能說說平時經(jīng)常使用到哪些事件嗎?
常見的QT事件類型如下:
鍵盤事件: 按鍵按下和松開 鼠標(biāo)事件: 鼠標(biāo)移動,鼠標(biāo)按鍵的按下和松開
拖放事件: 用鼠標(biāo)進行拖放 滾輪事件: 鼠標(biāo)滾輪滾動
繪屏事件: 重繪屏幕的某些部分 定時事件: 定時器到時
焦點事件: 鍵盤焦點移動 進入和離開事件: 鼠標(biāo)移入widget之內(nèi),或是移出
移動事件: widget的位置改變 大小改變事件: widget的大小改變
顯示和隱藏事件: widget顯示和隱藏 窗口事件: 窗口是否為當(dāng)前窗口
41、多線程情況下, Qt中的信號槽分別在什么線程中執(zhí)行, 如何控制?
通過connect函數(shù)的第五個參數(shù)connectType來控制。
connect用于連接qt的信號和槽,在qt編程過程中不可或缺。它其實有第五個參數(shù),只是一般使用默認(rèn)值,在滿足某些特殊需求的時候可能需要手動設(shè)置。
Qt::AutoConnection**:** 默認(rèn)值,使用這個值則連接類型會在信號發(fā)送時決定。如果接收者和發(fā)送者在同一個線程,則自動使用Qt::DirectConnection類型。如果接收者和發(fā)送者不在一個線程,則自動使用Qt::QueuedConnection類型。
Qt::DirectConnection**:**槽函數(shù)會在信號發(fā)送的時候直接被調(diào)用,槽函數(shù)運行于信號發(fā)送者所在線程。效果看上去就像是直接在信號發(fā)送位置調(diào)用了槽函數(shù)。這個在多線程環(huán)境下比較危險,可能會造成奔潰。
Qt::QueuedConnection**:**槽函數(shù)在控制回到接收者所在線程的事件循環(huán)時被調(diào)用,槽函數(shù)運行于信號接收者所在線程。發(fā)送信號之后,槽函數(shù)不會立刻被調(diào)用,等到接收者的當(dāng)前函數(shù)執(zhí)行完,進入事件循環(huán)之后,槽函數(shù)才會被調(diào)用。多線程環(huán)境下一般用這個。
Qt::BlockingQueuedConnection**:**槽函數(shù)的調(diào)用時機與Qt::QueuedConnection一致,不過發(fā)送完信號后發(fā)送者所在線程會阻塞,直到槽函數(shù)運行完。接收者和發(fā)送者絕對不能在一個線程,否則程序會死鎖。在多線程間需要同步的場合可能需要這個。
Qt::UniqueConnection**:**這個flag可以通過按位或(|)與以上四個結(jié)合在一起使用。當(dāng)這個flag設(shè)置時,當(dāng)某個信號和槽已經(jīng)連接時,再進行重復(fù)的連接就會失敗。也就是避免了重復(fù)連接。
42、繼承與派生的區(qū)別?
1、角度不同
繼承是從子類的角度講的,派生是從基類的角度講的。
2、定義不同
派生指江河的源頭產(chǎn)生出支流。引申為從一個主要事物的發(fā)展中分化出來。繼承 是面向?qū)ο筌浖夹g(shù)當(dāng)中的一個概念,與多態(tài)、抽象共為面向?qū)ο蟮娜齻€基本特征。 繼承可以使得子類具有父類的屬性和方法或者重新定義、追加屬性和方法等。
43、單繼承和多繼承
單繼承(派生類只從一個直接基類繼承)時派生類的定義:
class 派生類名:繼承方式 基類名
{
新增成員聲明;
}
多繼承時派生類的定義:
class 派生類名:繼承方式1 基類名1,繼承方式2 基類名2,…
{
成員聲明;
}
**注意:**每一個“繼承方式”,只用于限制對緊隨其后之基類的繼承。
44、知道QT事件機制有幾種級別的事件過濾嗎?能大致描述下嗎?
根據(jù)對Qt事件機制的分析, 我們可以得到5種級別的事件過濾,處理辦法. 以功能從弱到強, 排列如下:
1)重載特定事件處理函數(shù).
最常見的事件處理辦法就是重載象mousePressEvent(), keyPressEvent(), paintEvent() 這樣的特定事件處理函數(shù).
2)重載event()函數(shù).
通過重載event()函數(shù),我們可以在事件被特定的事件處理函數(shù)處理之前(象keyPressEvent())處理它. 比如, 當(dāng)我們想改變tab鍵的默認(rèn)動作時,一般要重載這個函數(shù). 在處理一些不常見的事件(比如:LayoutDirectionChange)時,evnet()也很有用,因為這些函數(shù)沒有相應(yīng)的特定事件處理函數(shù). 當(dāng)我們重載event()函數(shù)時, 需要調(diào)用父類的event()函數(shù)來處理我們不需要處理或是不清楚如何處理的事件.
3) 在Qt對象上安裝事件過濾器.
安裝事件過濾器有兩個步驟: (假設(shè)要用A來監(jiān)視過濾B的事件)
首先調(diào)用B的installEventFilter( const QOject *obj ), 以A的指針作為參數(shù). 這樣所有發(fā)往B的事件都將先由A的eventFilter()處理.
然后, A要重載QObject::eventFilter()函數(shù), 在eventFilter() 中書寫對事件進行處理的代碼.
4) 給QAppliction對象安裝事件過濾器.
一旦我們給qApp(每個程序中唯一的QApplication對象)裝上過濾器,那么所有的事件在發(fā)往任何其他的過濾器時,都要先經(jīng)過當(dāng)前這個 eventFilter(). 在debug的時候,這個辦法就非常有用, 也常常被用來處理失效了的widget的鼠標(biāo)事件,通常這些事件會被QApplication::notify()丟掉. ( 在QApplication::notify() 中, 是先調(diào)用qApp的過濾器, 再對事件進行分析, 以決定是否合并或丟棄)
5) 繼承QApplication類,并重載notify()函數(shù).
Qt 是用QApplication::notify()函數(shù)來分發(fā)事件的.想要在任何事件過濾器查看任何事件之前先得到這些事件,重載這個函數(shù)是唯一的辦法. 通常來說事件過濾器更好用一些, 因為不需要去繼承QApplication類. 而且可以給QApplication對象安裝任意個數(shù)的事件。
45、有沒有使用過Qt4?Qt5的信號槽與Qt4相比有什么改進?
*編譯期:檢查信號與槽是否存在,參數(shù)類型檢查,Q_OBJECT是否存在
*信號可以和普通的函數(shù)、類的普通成員函數(shù)、lambda函數(shù)連接(而不再局限于信號函數(shù)和槽函數(shù))
*參數(shù)可以是 typedef 的或使用不同的namespace specifier
*可以允許一些自動的類型轉(zhuǎn)換(即信號和槽參數(shù)類型不必完全匹配)
46、信號槽是同步的還是異步的?分別如何實現(xiàn)?
通常使用的connect,實際上最后一個參數(shù)使用的是Qt::AutoConnection類型:Qt支持6種連接方式,其中3中最主要:
1.Qt::DirectConnection(直連方式)(信號與槽函數(shù)關(guān)系類似于函數(shù)調(diào)用,同步執(zhí)行)
當(dāng)信號發(fā)出后,相應(yīng)的槽函數(shù)將立即被調(diào)用。emit語句后的代碼將在所有槽函數(shù)執(zhí)行完畢后被執(zhí)行。
2.Qt::QueuedConnection(排隊方式)(此時信號被塞到信號隊列里了,信號與槽函數(shù)關(guān)系類似于消息通信,異步執(zhí)行)
當(dāng)信號發(fā)出后,排隊到信號隊列中,需等到接收對象所屬線程的事件循環(huán)取得控制權(quán)時才取得該信號,調(diào)用相應(yīng)的槽函數(shù)。emit語句后的代碼將在發(fā)出信號后立即被執(zhí)行,無需等待槽函數(shù)執(zhí)行完畢。
3.Qt::AutoConnection(自動方式)
Qt的默認(rèn)連接方式,如果信號的發(fā)出和接收這個信號的對象同屬一個線程,那個工作方式與直連方式相同;否則工作方式與排隊方式相同。
4.Qt::BlockingQueuedConnection(信號和槽必須在不同的線程中,否則就產(chǎn)生死鎖)
這個是完全同步隊列只有槽線程執(zhí)行完成才會返回,否則發(fā)送線程也會一直等待,相當(dāng)于是不同的線程可以同步起來執(zhí)行。
5.Qt::UniqueConnection
與默認(rèn)工作方式相同,只是不能重復(fù)連接相同的信號和槽,因為如果重復(fù)連接就會導(dǎo)致一個信號發(fā)出,對應(yīng)槽函數(shù)就會執(zhí)行多次。
6.Qt::AutoCompatConnection
工作方式與Qt::AutoConnection一樣。
47、知道死鎖嗎?死鎖是如何產(chǎn)生的?
死鎖的產(chǎn)生有如下四個必要條件
1. 資源是互斥的,同一時刻只能有一個進程占有該資源
2. 資源的釋放只能有該進程自己完成
3. 線程在獲取到需要資源之前,不會釋放已有資源
4. 存在這么一條循環(huán)等待的隊列,線程T1,T2,T3…, Tn
T1持有自己的資源請求T2的資源,….Tn持有自己的資源請求T1的資源
48、Qt線程同步的方法有哪些?
1.互斥量(QMutex)
QMutex m_Mutex; m_Mutex.lock(); m_Mutex.unlock();
2.互斥鎖(QMutexLocker)
QMutexLocker mutexLocker(&m_Mutex);
從聲明處開始(在構(gòu)造函數(shù)中加鎖),出了作用域自動解鎖(在析構(gòu)函數(shù)中解鎖)。
3.等待條件(QWaitCondition)
QWaitCondtion m_WaitCondition; m_WaitConditon.wait(&m_muxtex, time);
m_WaitCondition.wakeAll();
QReadWriteLock類
》一個線程試圖對一個加了讀鎖的互斥量進行上讀鎖,允許;
》一個線程試圖對一個加了讀鎖的互斥量進行上寫鎖,阻塞;
》一個線程試圖對一個加了寫鎖的互斥量進行上讀鎖,阻塞;、
》一個線程試圖對一個加了寫鎖的互斥量進行上寫鎖,阻塞。
讀寫鎖比較適用的情況是:需要多次對共享的數(shù)據(jù)進行讀操作的閱讀線程。
QReadWriterLock 與QMutex相似,除了它對 “read”,"write"訪問進行區(qū)別對待。它使得多個讀者可以共時訪問數(shù)據(jù)。使用QReadWriteLock而不是QMutex,可以使得多線程程序更具有并發(fā)性。
信號量QSemaphore
但是還有些互斥量(資源)的數(shù)量并不止一個,比如一個電腦安裝了2個打印機,我已經(jīng)申請了一個,但是我不能霸占這兩個,你來訪問的時候如果發(fā)現(xiàn)還有空閑的仍然可以申請到的。于是這個互斥量可以分為兩部分,已使用和未使用。
6.QReadLocker便利類和QWriteLocker便利類對QReadWriteLock進行加解鎖
49、工作中有沒有使用過動態(tài)庫和靜態(tài)庫?能不能簡單說下兩者的區(qū)別?
靜態(tài)庫:在鏈接階段將匯編生成的目標(biāo)文件.o與引用庫一起鏈接打包到可執(zhí)行文件中,可簡單看成(.o或者.obj文件的集合)。(1)對函數(shù)庫的鏈接是放在編譯時期完成的(2)程序在運行時與函數(shù)庫沒有瓜葛,移植方便(3)浪費空間和資源
動態(tài)庫:(1)將庫函數(shù)的鏈接載入推遲到程序運行時期(2)可以實現(xiàn)進程間的資源共享(因此也稱為共享庫)(3)將一些程序升級變得簡單(4)可以真正的做到鏈接載入完全由程序員在程序代碼中控制(顯示調(diào)用)
動態(tài)庫一般也會有個lib文件,那么和靜態(tài)庫lib文件有什么區(qū)別?
動態(tài)庫中的.lib文件叫做導(dǎo)入庫,對于導(dǎo)入庫而言,其實際的執(zhí)行代碼位于動態(tài)庫中,導(dǎo)入庫只包含了地址符號表等,確保程序找到對應(yīng)函數(shù)的一些基本地址信息。
靜態(tài)庫中的.lib叫做靜態(tài)庫,本身就包含了實際執(zhí)行代碼、符號表等等。
50、設(shè)計模式平時有使用到嗎?能不能說下常見的設(shè)計模式有哪些?能不能說說大致的概念?能不能具體說下工作中如何使用的?
總體來說設(shè)計模式分為三大類:
創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
結(jié)構(gòu)型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責(zé)任鏈模式、命令模式、備忘錄模式、狀態(tài)模式、訪問者模式、中介者模式、解釋器模式。
31、說出代理模式類關(guān)系和優(yōu)點
優(yōu)點:
代理模式能將代理對象與真實被調(diào)用的目標(biāo)對象隔離。 一定程度上降低了系統(tǒng)的耦合度,擴展性好。 可以起到保護目標(biāo)對象的作用。 可以對目標(biāo)對象的功能增強。
缺點:
代理模式會造成系統(tǒng)設(shè)計中類的數(shù)量增加。 在客戶端和目標(biāo)對象增加一個代理對象,會造成請求處理速度變慢。
32、說出工廠模式概念和優(yōu)點
定義一個創(chuàng)建產(chǎn)品對象的工廠接口,將產(chǎn)品對象的實際創(chuàng)建工作推遲到具體子工廠類當(dāng)中。這滿足創(chuàng)建型模式中所要求的“創(chuàng)建與使用相分離”的特點。簡單工廠模式可以決定在什么時候創(chuàng)建哪一個產(chǎn)品類的實例。工廠方法模式有非常良好的擴展性。抽象工廠模式降低了模塊間的耦合性,提高了團隊開發(fā)效率。
33、說出構(gòu)造者模式概念
? 構(gòu)造者模式是較為復(fù)雜的創(chuàng)建型模式,它將客戶端與包含多個組成部分的復(fù)雜對象的創(chuàng)建過程分離。客戶端無需知道具體的構(gòu)造過程,只需要與構(gòu)造器打交道即可,構(gòu)建與表示分離。
34、說出適配器模式概念
將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口,使得原本由于接口不兼容而不能在一起工作的那些類一起工作。
35、進程和線程的區(qū)別?
? 進程的定義:一個具有一定獨立功能的程序在一個數(shù)據(jù)集合上依次動態(tài)執(zhí)行的過程。進程是一個正在執(zhí)行程序的實例,包括程序計數(shù)器、寄存器和程序變量的當(dāng)前值。簡單來說,進程就是一個程序的執(zhí)行流程,內(nèi)部保存程序運行所需的資源。在操作系統(tǒng)中可以有多個進程在運行,可對于CPU來說,同一時刻,一個CPU只能運行一個進程,但在某一時間段內(nèi),CPU將這一時間段拆分成更短的時間片,CPU不停的在各個進程間游走,這就給人一種并行的錯覺,像CPU可以同時運行多個進程一樣,這就是偽并行。
? 線程的定義:線程是進程當(dāng)中的一條執(zhí)行流程,這幾乎就是進程的定義,一個進程內(nèi)可以有多個子執(zhí)行流程,即線程。從資源組合的角度看,進程把一組相關(guān)的資源組合起來,構(gòu)成一個資源平臺環(huán)境,包括地址空間(代碼段,數(shù)據(jù)段),打開的文件等各種資源。從運行的角度看:進程是代碼在這個資源平臺上的執(zhí)行流程,然而線程貌似也是這樣,但是進程比線程多了資源內(nèi)容列表樣式:進程 = 線程 + 共享資源。
? 進程是操作系統(tǒng)分配資源的單位,線程是調(diào)度的基本單位,線程之間共享進程資源。
36、進程之間的通信方式有哪些?
管道消息隊列共享內(nèi)存信號量套接字文件
37、信號和信號量的區(qū)別是什么?
? 信號:一種處理異步事件的方式。信號是比較復(fù)雜的通信方式,用于通知接收進程有某種事件發(fā)生,除了用于進程外,還可以發(fā)送信號給進程本身。
? 信號量:進程間通信處理同步互斥的機制。是在多線程環(huán)境下使用的一種設(shè)施,它負(fù)責(zé)協(xié)調(diào)各個線程,以保證它們能夠正確,合理的使用公共資源。
38、你覺得自定義控件的方法主要是哪些?
從外觀設(shè)計上:QSS、繼承繪制函數(shù)重繪、繼承QStyle相關(guān)類重繪、組合拼裝等等。
從功能行為上:重寫事件函數(shù)、添加或者修改信號和槽等等
39、QSS平時使用的多嗎?能舉幾個例子嗎?
1.將QSS統(tǒng)一寫在一個文件中,通過程序給主窗口加載;
2.寫成一個字符串中,通過程序給主窗口加載;
3.需要使用的地方,寫一個字符串,加載給對象;
4.QT Designer中填寫;
40、Qt程序是事件驅(qū)動的,事件到處都可以遇到。能說說平時經(jīng)常使用到哪些事件嗎?
常見的QT事件類型如下:
鍵盤事件: 按鍵按下和松開 鼠標(biāo)事件: 鼠標(biāo)移動,鼠標(biāo)按鍵的按下和松開
拖放事件: 用鼠標(biāo)進行拖放 滾輪事件: 鼠標(biāo)滾輪滾動
繪屏事件: 重繪屏幕的某些部分 定時事件: 定時器到時
焦點事件: 鍵盤焦點移動 進入和離開事件: 鼠標(biāo)移入widget之內(nèi),或是移出
移動事件: widget的位置改變 大小改變事件: widget的大小改變
顯示和隱藏事件: widget顯示和隱藏 窗口事件: 窗口是否為當(dāng)前窗口
41、多線程情況下, Qt中的信號槽分別在什么線程中執(zhí)行, 如何控制?
通過connect函數(shù)的第五個參數(shù)connectType來控制。
connect用于連接qt的信號和槽,在qt編程過程中不可或缺。它其實有第五個參數(shù),只是一般使用默認(rèn)值,在滿足某些特殊需求的時候可能需要手動設(shè)置。
Qt::AutoConnection**:** 默認(rèn)值,使用這個值則連接類型會在信號發(fā)送時決定。如果接收者和發(fā)送者在同一個線程,則自動使用Qt::DirectConnection類型。如果接收者和發(fā)送者不在一個線程,則自動使用Qt::QueuedConnection類型。
Qt::DirectConnection**:**槽函數(shù)會在信號發(fā)送的時候直接被調(diào)用,槽函數(shù)運行于信號發(fā)送者所在線程。效果看上去就像是直接在信號發(fā)送位置調(diào)用了槽函數(shù)。這個在多線程環(huán)境下比較危險,可能會造成奔潰。
Qt::QueuedConnection**:**槽函數(shù)在控制回到接收者所在線程的事件循環(huán)時被調(diào)用,槽函數(shù)運行于信號接收者所在線程。發(fā)送信號之后,槽函數(shù)不會立刻被調(diào)用,等到接收者的當(dāng)前函數(shù)執(zhí)行完,進入事件循環(huán)之后,槽函數(shù)才會被調(diào)用。多線程環(huán)境下一般用這個。
Qt::BlockingQueuedConnection**:**槽函數(shù)的調(diào)用時機與Qt::QueuedConnection一致,不過發(fā)送完信號后發(fā)送者所在線程會阻塞,直到槽函數(shù)運行完。接收者和發(fā)送者絕對不能在一個線程,否則程序會死鎖。在多線程間需要同步的場合可能需要這個。
Qt::UniqueConnection**:**這個flag可以通過按位或(|)與以上四個結(jié)合在一起使用。當(dāng)這個flag設(shè)置時,當(dāng)某個信號和槽已經(jīng)連接時,再進行重復(fù)的連接就會失敗。也就是避免了重復(fù)連接。
42、繼承與派生的區(qū)別?
1、角度不同
繼承是從子類的角度講的,派生是從基類的角度講的。
2、定義不同
派生指江河的源頭產(chǎn)生出支流。引申為從一個主要事物的發(fā)展中分化出來。繼承 是面向?qū)ο筌浖夹g(shù)當(dāng)中的一個概念,與多態(tài)、抽象共為面向?qū)ο蟮娜齻€基本特征。 繼承可以使得子類具有父類的屬性和方法或者重新定義、追加屬性和方法等。
43、單繼承和多繼承
單繼承(派生類只從一個直接基類繼承)時派生類的定義:
class 派生類名:繼承方式 基類名
{
新增成員聲明;
}
多繼承時派生類的定義:
class 派生類名:繼承方式1 基類名1,繼承方式2 基類名2,…
{
成員聲明;
}
**注意:**每一個“繼承方式”,只用于限制對緊隨其后之基類的繼承。
44、知道QT事件機制有幾種級別的事件過濾嗎?能大致描述下嗎?
根據(jù)對Qt事件機制的分析, 我們可以得到5種級別的事件過濾,處理辦法. 以功能從弱到強, 排列如下:
1)重載特定事件處理函數(shù).
最常見的事件處理辦法就是重載象mousePressEvent(), keyPressEvent(), paintEvent() 這樣的特定事件處理函數(shù).
2)重載event()函數(shù).
通過重載event()函數(shù),我們可以在事件被特定的事件處理函數(shù)處理之前(象keyPressEvent())處理它. 比如, 當(dāng)我們想改變tab鍵的默認(rèn)動作時,一般要重載這個函數(shù). 在處理一些不常見的事件(比如:LayoutDirectionChange)時,evnet()也很有用,因為這些函數(shù)沒有相應(yīng)的特定事件處理函數(shù). 當(dāng)我們重載event()函數(shù)時, 需要調(diào)用父類的event()函數(shù)來處理我們不需要處理或是不清楚如何處理的事件.
3) 在Qt對象上安裝事件過濾器.
安裝事件過濾器有兩個步驟: (假設(shè)要用A來監(jiān)視過濾B的事件)
首先調(diào)用B的installEventFilter( const QOject *obj ), 以A的指針作為參數(shù). 這樣所有發(fā)往B的事件都將先由A的eventFilter()處理.
然后, A要重載QObject::eventFilter()函數(shù), 在eventFilter() 中書寫對事件進行處理的代碼.
4) 給QAppliction對象安裝事件過濾器.
一旦我們給qApp(每個程序中唯一的QApplication對象)裝上過濾器,那么所有的事件在發(fā)往任何其他的過濾器時,都要先經(jīng)過當(dāng)前這個 eventFilter(). 在debug的時候,這個辦法就非常有用, 也常常被用來處理失效了的widget的鼠標(biāo)事件,通常這些事件會被QApplication::notify()丟掉. ( 在QApplication::notify() 中, 是先調(diào)用qApp的過濾器, 再對事件進行分析, 以決定是否合并或丟棄)
5) 繼承QApplication類,并重載notify()函數(shù).
Qt 是用QApplication::notify()函數(shù)來分發(fā)事件的.想要在任何事件過濾器查看任何事件之前先得到這些事件,重載這個函數(shù)是唯一的辦法. 通常來說事件過濾器更好用一些, 因為不需要去繼承QApplication類. 而且可以給QApplication對象安裝任意個數(shù)的事件。
45、有沒有使用過Qt4?Qt5的信號槽與Qt4相比有什么改進?
*編譯期:檢查信號與槽是否存在,參數(shù)類型檢查,Q_OBJECT是否存在
*信號可以和普通的函數(shù)、類的普通成員函數(shù)、lambda函數(shù)連接(而不再局限于信號函數(shù)和槽函數(shù))
*參數(shù)可以是 typedef 的或使用不同的namespace specifier
*可以允許一些自動的類型轉(zhuǎn)換(即信號和槽參數(shù)類型不必完全匹配)
46、信號槽是同步的還是異步的?分別如何實現(xiàn)?
通常使用的connect,實際上最后一個參數(shù)使用的是Qt::AutoConnection類型:Qt支持6種連接方式,其中3中最主要:
1.Qt::DirectConnection(直連方式)(信號與槽函數(shù)關(guān)系類似于函數(shù)調(diào)用,同步執(zhí)行)
當(dāng)信號發(fā)出后,相應(yīng)的槽函數(shù)將立即被調(diào)用。emit語句后的代碼將在所有槽函數(shù)執(zhí)行完畢后被執(zhí)行。
2.Qt::QueuedConnection(排隊方式)(此時信號被塞到信號隊列里了,信號與槽函數(shù)關(guān)系類似于消息通信,異步執(zhí)行)
當(dāng)信號發(fā)出后,排隊到信號隊列中,需等到接收對象所屬線程的事件循環(huán)取得控制權(quán)時才取得該信號,調(diào)用相應(yīng)的槽函數(shù)。emit語句后的代碼將在發(fā)出信號后立即被執(zhí)行,無需等待槽函數(shù)執(zhí)行完畢。
3.Qt::AutoConnection(自動方式)
Qt的默認(rèn)連接方式,如果信號的發(fā)出和接收這個信號的對象同屬一個線程,那個工作方式與直連方式相同;否則工作方式與排隊方式相同。
4.Qt::BlockingQueuedConnection(信號和槽必須在不同的線程中,否則就產(chǎn)生死鎖)
這個是完全同步隊列只有槽線程執(zhí)行完成才會返回,否則發(fā)送線程也會一直等待,相當(dāng)于是不同的線程可以同步起來執(zhí)行。
5.Qt::UniqueConnection
與默認(rèn)工作方式相同,只是不能重復(fù)連接相同的信號和槽,因為如果重復(fù)連接就會導(dǎo)致一個信號發(fā)出,對應(yīng)槽函數(shù)就會執(zhí)行多次。
6.Qt::AutoCompatConnection
工作方式與Qt::AutoConnection一樣。
47、知道死鎖嗎?死鎖是如何產(chǎn)生的?
死鎖的產(chǎn)生有如下四個必要條件
1. 資源是互斥的,同一時刻只能有一個進程占有該資源
2. 資源的釋放只能有該進程自己完成
3. 線程在獲取到需要資源之前,不會釋放已有資源
4. 存在這么一條循環(huán)等待的隊列,線程T1,T2,T3…, Tn
T1持有自己的資源請求T2的資源,….Tn持有自己的資源請求T1的資源
48、Qt線程同步的方法有哪些?
1.互斥量(QMutex)
QMutex m_Mutex; m_Mutex.lock(); m_Mutex.unlock();
2.互斥鎖(QMutexLocker)
QMutexLocker mutexLocker(&m_Mutex);
從聲明處開始(在構(gòu)造函數(shù)中加鎖),出了作用域自動解鎖(在析構(gòu)函數(shù)中解鎖)。
3.等待條件(QWaitCondition)
QWaitCondtion m_WaitCondition; m_WaitConditon.wait(&m_muxtex, time);
m_WaitCondition.wakeAll();
QReadWriteLock類
》一個線程試圖對一個加了讀鎖的互斥量進行上讀鎖,允許;
》一個線程試圖對一個加了讀鎖的互斥量進行上寫鎖,阻塞;
》一個線程試圖對一個加了寫鎖的互斥量進行上讀鎖,阻塞;、
》一個線程試圖對一個加了寫鎖的互斥量進行上寫鎖,阻塞。
讀寫鎖比較適用的情況是:需要多次對共享的數(shù)據(jù)進行讀操作的閱讀線程。
QReadWriterLock 與QMutex相似,除了它對 “read”,"write"訪問進行區(qū)別對待。它使得多個讀者可以共時訪問數(shù)據(jù)。使用QReadWriteLock而不是QMutex,可以使得多線程程序更具有并發(fā)性。
信號量QSemaphore
但是還有些互斥量(資源)的數(shù)量并不止一個,比如一個電腦安裝了2個打印機,我已經(jīng)申請了一個,但是我不能霸占這兩個,你來訪問的時候如果發(fā)現(xiàn)還有空閑的仍然可以申請到的。于是這個互斥量可以分為兩部分,已使用和未使用。
6.QReadLocker便利類和QWriteLocker便利類對QReadWriteLock進行加解鎖
49、工作中有沒有使用過動態(tài)庫和靜態(tài)庫?能不能簡單說下兩者的區(qū)別?
靜態(tài)庫:在鏈接階段將匯編生成的目標(biāo)文件.o與引用庫一起鏈接打包到可執(zhí)行文件中,可簡單看成(.o或者.obj文件的集合)。(1)對函數(shù)庫的鏈接是放在編譯時期完成的(2)程序在運行時與函數(shù)庫沒有瓜葛,移植方便(3)浪費空間和資源
動態(tài)庫:(1)將庫函數(shù)的鏈接載入推遲到程序運行時期(2)可以實現(xiàn)進程間的資源共享(因此也稱為共享庫)(3)將一些程序升級變得簡單(4)可以真正的做到鏈接載入完全由程序員在程序代碼中控制(顯示調(diào)用)
動態(tài)庫一般也會有個lib文件,那么和靜態(tài)庫lib文件有什么區(qū)別?
動態(tài)庫中的.lib文件叫做導(dǎo)入庫,對于導(dǎo)入庫而言,其實際的執(zhí)行代碼位于動態(tài)庫中,導(dǎo)入庫只包含了地址符號表等,確保程序找到對應(yīng)函數(shù)的一些基本地址信息。
靜態(tài)庫中的.lib叫做靜態(tài)庫,本身就包含了實際執(zhí)行代碼、符號表等等。
50、設(shè)計模式平時有使用到嗎?能不能說下常見的設(shè)計模式有哪些?能不能說說大致的概念?能不能具體說下工作中如何使用的?
總體來說設(shè)計模式分為三大類:
創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
結(jié)構(gòu)型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責(zé)任鏈模式、命令模式、備忘錄模式、狀態(tài)模式、訪問者模式、中介者模式、解釋器模式。
51、HTTP協(xié)議有使用過嗎?Qt5中使用的相關(guān)聯(lián)的主要的幾個類?
QNetworkAccessManager/QNetworkRequest/QNetworkReply。
52、平時使用算法比較多嗎?能簡單說下快排的思想嗎?時間復(fù)雜度是多少?
基本思想是:通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨立的兩部分,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對這兩部分?jǐn)?shù)據(jù)分別進行快速排序,整個排序過程可以遞歸進行,以此達(dá)到整個數(shù)據(jù)變成有序序列。
時間復(fù)雜度:平均為O(nlogn),最好為O(nlogn),最差為O(logn2)
53、如果軟件除了問題(Bug),如何快速定位?主要方法有哪些?
打印輸出/代碼調(diào)試/日志記錄/分析工具/找同事討論。
1、二分法定位技巧
無論是有多復(fù)雜的代碼,利用二分法定位技巧一般都是可以定位到問題所在。
從二分法定位技巧可以延伸出一些具體的處理bug的方法,比如:對輸入數(shù)據(jù)二分、對代碼版本二分、注釋掉部分代碼、在不同位置插入試探性代碼、對運行環(huán)境二分。
2、IDE調(diào)試
IDE的VS debug的功能簡直就是立竿見影。它可以加斷點,單步調(diào)試。
單步調(diào)試可以讓我們對代碼邏輯,執(zhí)行順序,以及各種中間結(jié)果更加清晰。
至于本身容易出錯的BUG,用IDE調(diào)試簡直是再合適不過了。
3、重新讀一遍程序
相對新手程序員來說,如果代碼出現(xiàn)bug,可以重新讀一遍程序。這種方法是最有效、最快速的 Debug 方式。
4、必殺,重寫一遍
如果你發(fā)現(xiàn)無論如何也找不到BUG,而且代碼只是復(fù)雜,本身不是很長,直接重寫代碼吧!
5、小黃鴨調(diào)試法
小黃鴨調(diào)試法是程序員們經(jīng)常使用的調(diào)試代碼方法之一。
小黃鴨不懂程序,所以我們可以向他解釋每一行程序的作用,以此來激發(fā)靈感。
54、引用和指針有何區(qū)別
1.指針是一個對象,而引用僅是一個對象的別名
2.引用使用時無需解引用,指針需要
3.引用只能在定義時初始化一次,而指針可變
4.引用不能為空,指針可以為空
5.有多級指針沒有多級引用
6.不會為引用變量開辟內(nèi)存空間,它和它引用的變量共用一塊內(nèi)存空間,指針會開辟內(nèi)存空間
7.引用自加改變引用值的內(nèi)容,指針自加改變指針的指向
8.sizeof含義不同,引用結(jié)果為引用類型的大小,指針始終是地址空間所占大小
9.引用比指針使用起來相對更安全
55、什么情況下使用虛函數(shù)?和純虛函數(shù)有什么區(qū)別?虛析構(gòu)函數(shù)的作用是什么?(虛函數(shù)表)
虛函數(shù)的主要作用是“運行時多態(tài)”。虛析構(gòu)函數(shù)的作用在于使用delete刪除一個對象時,能確保析構(gòu)函數(shù)被正確的執(zhí)行。
區(qū)別:
虛函數(shù)和純虛函數(shù)可以定義在同一個類(class)中,含有純虛函數(shù)的類被稱為抽象類(abstract class),而只含有虛函數(shù)的類(class)不能被稱為抽象類(abstract class)。 虛函數(shù)可以被直接使用,也可以被子類(sub class)重載以后以多態(tài)的形式調(diào)用,而純虛函數(shù)必須在子類(sub class)中實現(xiàn)該函數(shù)才可以使用,因為純虛函數(shù)在基類(base class)只有聲明而沒有定義。 虛函數(shù)和純虛函數(shù)都可以在子類(sub class)中被重載,以多態(tài)的形式被調(diào)用。 虛函數(shù)和純虛函數(shù)通常存在于抽象基類(abstract base class -ABC)之中,被繼承的子類重載,目的是提供一個統(tǒng)一的接口。 虛函數(shù)的定義形式:virtual {method body}
純虛函數(shù)的定義形式:virtual { } = 0;
在虛函數(shù)和純虛函數(shù)的定義中不能有static標(biāo)識符,原因很簡單,被static修飾的函數(shù)在編譯時候要求前期bind,然而虛函數(shù)卻是動態(tài)綁定(run-time bind),而且被兩者修飾的函數(shù)生命周期(life recycle)也不一樣。
虛函數(shù)必須實現(xiàn),如果不實現(xiàn),編譯器將報錯,錯誤提示為: error LNK****: unresolved external symbol "public: virtual void __thiscall ClassName::virtualFunctionName(void)" 對于虛函數(shù)來說,父類和子類都有各自的版本。由多態(tài)方式調(diào)用的時候動態(tài)綁定。 實現(xiàn)了純虛函數(shù)的子類,該純虛函數(shù)在子類中就編程了虛函數(shù),子類的子類即孫子類可以覆蓋 該虛函數(shù),由多態(tài)方式調(diào)用的時候動態(tài)綁定。 虛函數(shù)是C++中用于實現(xiàn)多態(tài)(polymorphism)的機制。核心理念就是通過基類訪問派生類定義的 函數(shù)。 多態(tài)性指相同對象收到不同消息或不同對象收到相同消息時產(chǎn)生不同的實現(xiàn)動作。C++支持兩種多態(tài)性:編譯時多態(tài)性,運行時多態(tài)性。
a.編譯時多態(tài)性:通過重載函數(shù)實現(xiàn)
b 運行時多態(tài)性:通過虛函數(shù)實現(xiàn)。
如果一個類中含有純虛函數(shù),那么任何試圖對該類進行實例化的語句都將導(dǎo)致錯誤的產(chǎn)生,因為抽象基類(ABC)是不能被直接調(diào)用的。必須被子類繼承重載以后,根據(jù)要求調(diào)用其子類的方法。
56、對Qt元對象系統(tǒng)了解嗎?
Qt對標(biāo)準(zhǔn)的C++進行了擴展,如信號槽、對象屬性等。Qt的元對象編譯系統(tǒng)MOC是一個預(yù)處理器,當(dāng)Qt讀取源文件時檢測到類中包含有Q_OBJECT宏時,則會創(chuàng)建一個新的文件(生成路徑下的moc開頭的文件),將源碼轉(zhuǎn)換為C++編譯器可以識別的代碼寫入moc開頭的文件,然后C++編譯器對其進行編譯。當(dāng)你的類需要使用Qt的擴展功能時,如信號槽、對象屬性等時,則必須使用MOC,反之如果你的類不使用這些功能的時候不要無畏的使用MOC增大源碼體積。使用MOC系統(tǒng)的方法:
繼承QObject。 類中添加Q_OBJECT宏。
57、Qt中的MVD了解嗎?
Qt的MVD包含三個部分Model(模型),View(視圖),代理(Delegate)。Model否則保存數(shù)據(jù),View負(fù)責(zé)展示數(shù)據(jù),Delegate負(fù)責(zé)Item樣式繪制或處理輸入。這三部分通過信號槽來進行通信,當(dāng)Model中數(shù)據(jù)發(fā)生變化時將會發(fā)送信號到View,在View中編輯數(shù)據(jù)時,Delegate負(fù)責(zé)將編輯狀態(tài)發(fā)送給Model層?;惙謩e為QAbstractItemModel、QAbstractItemView、QAbstractItemDelegate。Qt中提供了默認(rèn)實現(xiàn)的MVD類,如QTableWidget、QListWidget、QTreeWidget等。
58、QObject是否是線程安全的
QObject及其所有子類都不是線程安全的(但都是可重入的)。因此,你不能有兩個線程同時訪問一個QObject對象,除非這個對象的內(nèi)部數(shù)據(jù)都已經(jīng)很好地序列化(例如為每個數(shù)據(jù)訪問加鎖)。
59、QObject的線程依附性是否可以改變
調(diào)用QObject::moveToThread()函數(shù)。該函數(shù)會改變一個對象及其所有子對象的線程依附性。
由于QObject本身是線程不安全的,因此moveToThread接口的調(diào)用必須在QObject對象所在的線程內(nèi)調(diào)用。
60、如何安全的在另外一個線程中調(diào)用QObject對象的接口
QObject被設(shè)計成在一個單線程中創(chuàng)建與使用,因此,在一個線程中創(chuàng)建一個對象,而在另外的線程中調(diào)用它的函數(shù),這樣的行為不能保證工作良好。
使用信號槽的隊列連接或者QT的反射系統(tǒng)提供的QMetaObject::invokeMethed的隊列連接調(diào)用。這要求接口必須是內(nèi)省的,也就是說這個函數(shù)要么是一個槽函數(shù),要么標(biāo)記有Q_INVOKABLE宏。
將事件提交到接收對象所在線程的事件循環(huán);當(dāng)事件發(fā)出時,響應(yīng)函數(shù)就會被調(diào)用。
61、QFrame與QWidget的區(qū)別
QFrame 和 QWidget 都是 Qt 中的 GUI 組件,但是它們有一些區(qū)別:
繼承關(guān)系:QFrame 繼承自 QWidget,所以 QFrame 具有 QWidget 的所有功能。
功能:QFrame 提供了一個簡單的框架,可以作為其他控件的容器。它還可以用來繪制簡單的圖形,如線條。QWidget 沒有這樣的功能,但是提供了基礎(chǔ)的 GUI 組件功能,如設(shè)置尺寸和位置等。
外觀:QFrame 可以有邊框和背景顏色,因此外觀更加豐富。QWidget 只有背景顏色,沒有邊框。
通常,當(dāng)需要一個簡單的框架時,使用 QFrame,當(dāng)需要基礎(chǔ)的 GUI 組件功能時,使用 QWidget。
62、信號重載了,如何確定連接哪個信號?
采用函數(shù)指針確定連接哪個信號。
63、槽函數(shù)參數(shù)、信號的參數(shù)
槽函數(shù)的參數(shù)可以少于信號的參數(shù)。
槽函數(shù)本身參數(shù)比信號的少槽函數(shù)參數(shù)帶有默認(rèn)參數(shù)
64、槽函數(shù)的參數(shù)是否可以比信號的參數(shù)多?
也可以。唯一的情況就是槽函數(shù)參數(shù)帶有默認(rèn)參數(shù),除去默認(rèn)參數(shù)外,槽函數(shù)的參數(shù)必須小于等于信號的參數(shù)。
65、指針和引用有什么區(qū)別?什么情況下用指針,什么情況下用引用?
? 區(qū)別:
1.指針是一個變量,只不過這個變量存儲的是一個地址,指向內(nèi)存的一個存儲單元,即指針是一個實體;而引用跟原來的變量實質(zhì)上是同一個東西,只不過是原變量的一個別名而已。
2.有const指針,但是沒有const引用。
3.指針可以有多級,但是引用只能是一級(int** p;合法,而int&& a;不合法)。
4.指針的值可以為空,但是引用的值不可以,并且引用在定義的時候必須初始化。
5.指針的值在初始化后可以改變,即指向其它的存儲單元,而引用在初始化后就不會再改變了,從一而終。
6.sizeof引用得到的是所指向的變量(對象)的大小,而sizeof指針得到的是指針本身的大小。
7.指針和引用的自增++運算意義不一樣。
? 相同點:
1.都可以對變量就行修改。
2.都是地址的概念,指針指向一塊內(nèi)存,它的內(nèi)容是所指內(nèi)存的地址,引用是某塊內(nèi)存的別名。
? 何時使用:
1.當(dāng)考慮到存在不指向任何對象的可能,這時候應(yīng)該使用指針。
2.當(dāng)需要在能夠在不同的時刻指向不同的對象,這個時候使用指針。如果總是指向一個對象并且一旦指向一個對象后就不會改變指向,那么應(yīng)該使用引用。
3.當(dāng)重載某個操作符時,應(yīng)該使用引用。
66、一般什么情況下會出現(xiàn)內(nèi)存泄漏?怎么用C++在編碼層面盡量避免內(nèi)存泄漏。
內(nèi)存泄漏是指程序向系統(tǒng)申請分配內(nèi)存使用(new),用完以后卻沒有歸還(delete)。結(jié)果申請的那塊內(nèi)存程序不再使用,而系統(tǒng)也無法再講它分配給需要的程序。
造成內(nèi)存泄漏的幾種情況:
1.指針重新賦值 2.錯誤的內(nèi)存釋放 3.返回值的不正確處理 4.new和delete沒有配對使用。
如何避免內(nèi)存泄漏:
1.確保沒有訪問空指針。 2.盡量使用智能指針。 3.new和delete配對使用。
67、對C++11 的智能指針了解多少,可以自己實現(xiàn)一個智能指針嗎?
? 三種智能指針:
? std::shared_ptr:使用引用計數(shù),每一個shared_ptr的拷貝都指向相同的內(nèi)存,每次拷貝都會觸發(fā)引用計數(shù)+1,每次生命周期結(jié)束析構(gòu)的時候引用計數(shù)-1,在最后一個shared_ptr析構(gòu)的時候,內(nèi)存才會釋放。
? std::weak_ptr:用來監(jiān)視shared_ptr的生命周期,它不管理shared_ptr內(nèi)部的指針,它的拷貝析構(gòu)都不會影響引用計數(shù),純粹是作為一個旁觀者監(jiān)視shared_ptr中管理的資源是否存在,可以用來返回this指針和解決循環(huán)引用問題。
? std::unique_ptr:獨占型的智能指針,它不允許其它智能指針共享其內(nèi)部指針,也不允許unique_ptr的拷貝和賦值。
68、show()和exec()的區(qū)別
show顯示非模態(tài)窗口(不影響用戶對其他窗口操作),exec顯示模態(tài)窗口(阻塞其他窗口,必須在當(dāng)前窗口操作完成后才能訪問其他窗口),open半模態(tài)(阻塞其他窗口響應(yīng),但不影響后續(xù)代碼執(zhí)行)
69、Qt事件循環(huán)
Qt的主事件循環(huán)能夠從事件隊列中獲取本地窗口系統(tǒng)事件,然后判斷事件類型,并將事件分發(fā)給特定的接收對象。
主事件循環(huán)通過調(diào)用QCoreApplication::exec()啟動,隨著QCoreApplication::exit()結(jié)束,本地的事件循環(huán)可用利用QEventLoop構(gòu)建。
70、什么叫自定義控件?
qt本身的控件不能滿足需求將原控件功能提升達(dá)到要求,此時控件為自定義控件。
71、Qt的D指針(d_ptr)與Q指針(q_ptr)
題目:講一下Qt的D指針和Q指針?
D指針
PIMPL模式,指向一個包含所有數(shù)據(jù)的私有數(shù)據(jù)結(jié)構(gòu)體。
私有的結(jié)構(gòu)體可以隨意改變,而不需要重新編譯整個工程項目隱藏實現(xiàn)細(xì)節(jié)頭文件中沒有任何實現(xiàn)細(xì)節(jié),可以作為API使用原本在頭文件的實現(xiàn)部分轉(zhuǎn)移到樂源文件,所以編譯速度有所提高
Q指針
私有的結(jié)構(gòu)體中儲存一個指向公有類的Q指針。
總結(jié)
Qt中的一個類常用一個PrivateXXX類來處理內(nèi)部邏輯,使得內(nèi)部邏輯與外部接口分開,這個PrivateXXX對象通過D指針來訪問;在PrivateXXX中有需要引用Owner的內(nèi)容,通過Q指針來訪問。由于D和Q指針是從基類繼承下來的,子類中由于繼承導(dǎo)致類型發(fā)生變化,需要通過static_cast類型轉(zhuǎn)化,所以DPTR()與QPTR()宏定義實現(xiàn)了轉(zhuǎn)換。
72、Qt信號槽的調(diào)用流程
MOC查找頭文件中的signal與slots,標(biāo)記出信號槽。將信號槽信息儲存到類靜態(tài)變量staticMetaObject中,并按照聲明的順序進行存放,建立索引。connect鏈接,將信號槽的索引信息放到一個雙向鏈表中,彼此配對。emit被調(diào)用,調(diào)用信號函數(shù),且傳遞發(fā)送信號的對象指針,元對象指針,信號索引,參數(shù)列表到active函數(shù)。active函數(shù)在雙向鏈表中找到所有與信號對應(yīng)的槽索引,根據(jù)槽索引找到槽函數(shù),執(zhí)行槽函數(shù)。
73、Qt connect的第五個參數(shù)(信號槽鏈接方式)?
Qt::AutoConnection: 默認(rèn)值,使用這個值則連接類型會在信號發(fā)送時決定。如果接收者和發(fā)送者在同一個線程,則自動使用Qt::DirectConnection類型。如果接收者和發(fā)送者不在一個線程,則自動使用Qt::QueuedConnection類型。Qt::DirectConnection:槽函數(shù)會在信號發(fā)送的時候直接被調(diào)用,槽函數(shù)運行于信號發(fā)送者所在線程。效果看上去就像是直接在信號發(fā)送位置調(diào)用了槽函數(shù)。這個在多線程環(huán)境下比較危險,可能會造成奔潰。Qt::QueuedConnection:槽函數(shù)在控制回到接收者所在線程的事件循環(huán)時被調(diào)用,槽函數(shù)運行于信號接收者所在線程。發(fā)送信號之后,槽函數(shù)不會立刻被調(diào)用,等到接收者的當(dāng)前函數(shù)執(zhí)行完,進入事件循環(huán)之后,槽函數(shù)才會被調(diào)用。多線程環(huán)境下一般用這個。Qt::BlockingQueuedConnection:槽函數(shù)的調(diào)用時機與Qt::QueuedConnection一致,不過發(fā)送完信號后發(fā)送者所在線程會阻塞,直到槽函數(shù)運行完。接收者和發(fā)送者絕對不能在一個線程,否則程序會死鎖。在多線程間需要同步的場合可能需要這個。Qt::UniqueConnection:這個flag可以通過按位或(|)與以上四個結(jié)合在一起使用。當(dāng)這個flag設(shè)置時,當(dāng)某個信號和槽已經(jīng)連接時,再進行重復(fù)的連接就會失敗。也就是避免了重復(fù)連接。
74、了解Qt的QPointer嗎?
QPointer
QPointer只能用于指向QObject及派生類的對象。當(dāng)一個QObject或派生類對象被刪除后,QPointer能自動將其內(nèi)部的指針設(shè)置為0,這樣在使用QPointer之前就可以判斷一下是否有效樂。
QPointer對象超出作用域時,并不會刪除它指向的內(nèi)存對象。
75、了解Qt的QSharedPointer嗎?
QSharedPointer
用于實現(xiàn)數(shù)據(jù)的隱式共享。Qt中大量使用了隱式共享與寫時拷貝技術(shù),例如:
QString str1 = "abc";
QString str2 = str1;
str2[2] = "X";
第二行執(zhí)行完后,str2和str1指向同一片內(nèi)存數(shù)據(jù)。第三句執(zhí)行時,Qt會為str2的內(nèi)部數(shù)據(jù)重新分配內(nèi)存。這樣做的好處是可以有效地減少大片數(shù)據(jù)拷貝的次數(shù),提高程序的運行效率。
Qt中隱式共享和寫時拷貝就是利用QSharedDataPointer和QSharedData這兩個類實現(xiàn)的。
76、描述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)存。QTextStream, QDataStream可以操作磁盤文件, 也可以操作內(nèi)存數(shù)據(jù), 通過流對象可以將數(shù)據(jù)打包到內(nèi)存, 進行數(shù)據(jù)的傳輸。
77、詳解Qt中的內(nèi)存管理機制
所有繼承自QOBJECT類的類,如果在new的時候指定了父親,那么它的清理時在父親被delete的時候delete的,所以如果一個程序中,所有的QOBJECT類都指定了父親,那么他們是會一級級的在最上面的父親清理時被清理,而不用自己清理;
程序通常最上層會有一個根的QOBJECT,就是放在setCentralWidget()中的那個QOBJECT,這個QOBJECT在 new的時候不必指定它的父親,因為這個語句將設(shè)定它的父親為總的QAPPLICATION,當(dāng)整個QAPPLICATION沒有時它就自動清理,所以也無需清理。9這里QT4和QT3有不同,QT3中用的是setmainwidget函數(shù),但是這個函數(shù)不作為里面QOBJECT的父親,所以QT3中這個頂層的QOBJECT要自行銷毀)。
這是有人可能會問那如果我自行delete掉這些QT接管負(fù)責(zé)銷毀的指針了會出現(xiàn)什么情況呢,如果時這樣的話,正常情況下QT的擁有這個對象的那個父親會知道這件事情,它會直到它的兒子被你直接DELETE了,這樣它會將這個兒子移出它的列表,并且重新構(gòu)建顯示內(nèi)容,但是直接這樣做時有風(fēng)險的!也就是要說的下一條。
當(dāng)一個QOBJECT正在接受事件隊列時如果中途被你DELETE掉了,就是出現(xiàn)問題了,所以QT中建議大家不要直接DELETE掉一個 QOBJECT,如果一定要這樣做,要使用QOBJECT的deleteLater()函數(shù),它會讓所有事件都發(fā)送完一切處理好后馬上清除這片內(nèi)存,而且就算調(diào)用多次的deletelater也不會有問題。
QT不建議在一個QOBJECT 的父親的范圍之外持有對這個QOBJECT的指針,因為如果這樣外面的指針很可能不會察覺這個QOBJECT被釋放,會出現(xiàn)錯誤,如果一定要這樣,就要記住你在哪這樣做了,然后抓住那個被你違規(guī)使用的QOBJECT的destroyed()信號,當(dāng)它沒有時趕快置零你的外部指針。當(dāng)然我認(rèn)為這樣做是及其麻煩也不符合高效率編程規(guī)范的,所以如果要這樣在外部持有QOBJECT的指針,建議使用引用或者用智能指針,如QT就提供了智能指針針對這些情況,見一條。
QT中的智能指針封裝為QPointer類,所有QOBJECT的子類都可以用這個智能指針來包裝,很多用法與普通指針一樣,可以詳見QT assistant 通過調(diào)查這個QT的內(nèi)存管理功能,發(fā)現(xiàn)了很多東西,現(xiàn)在覺得雖然這個QT弄的有點小復(fù)雜,但是使用起來還是很方便的,
要說的是某些內(nèi)存泄露的檢測工具會認(rèn)為QT的程序因為這種方式存在內(nèi)存泄露,發(fā)現(xiàn)時大可不必理會。
78、QSS平時使用的多嗎?能舉幾個例子嗎?
1.將QSS統(tǒng)一寫在-一個文件中,通過程序給主窗口加載; 2.寫成一個字符串Q中,通過程序給主窗口加載; 3.需要使用的地方, 寫一個字符串,加載給對象; 4.QT Designer中填寫;
79、你覺得自定義控件的方法主要是哪些?
從外觀設(shè)計上: QSS、繼承繪制函數(shù)重繪、繼承QStyle相關(guān)類重繪 、組合拼裝等等 從功能行為上:重寫事件函數(shù)、添加或者修改信號和槽等等
80、知道Qt事件機制有幾種級別的事件過濾嗎?能大致描述下嗎?
根據(jù)對Qt事件機制的分析,我們可以得到5種級別的事件過濾,處理辦法.以功能從弱到強,排列如下:
1 )重載特定事件處理函數(shù).
最常見的事件處理辦法就是重載象mousePressEvent), keyPressEvent(), paintEvent()這樣的特定事件處理函數(shù).
2 )重載event()函數(shù).
通過重載event()函數(shù),我們可以在事件被特定的事件處理函數(shù)處理之前(象keyPressEvent()處理它.比如,當(dāng)我們想改變tab鍵的默認(rèn)動作 時,一般要重載這個函數(shù).在處理一些不常 見的事件(比如:L ayoutDirectionChange)時,evnet()也很有用,因為這些函數(shù)沒有相應(yīng)的特定事件處 理函數(shù).當(dāng)我們重載event()函數(shù)時,需要調(diào)用父類的event()函數(shù)來處理我們不需要處理或是不清楚如何處理的事件.
3)在Qt對象上安裝事件過濾器.
安裝事件過濾器有兩個步驟: (假設(shè)要用A來監(jiān)視過濾B的事件)
首先調(diào)用B的nstalEventFilter( const QOject *obj ),以A的指針作為參數(shù).這樣所有發(fā)往B的事件都將先由A的eventFilter()處理.
然后,A要重載QObject:eventFilter()函數(shù),在eventilter()中書瀉對事件進行處理的代碼.
4 )給QAppliction對象安裝事件過濾器.
一旦我們給qApp(每 個程序中唯一的QApplic ation對象)裝上過濾器,那么所有的事件在發(fā)往任何其他的過濾器時,都要先經(jīng)過當(dāng)前這個 eventFilter().在debug的時候這個辦法就非常有用,也常常被用來處理失效了的widget的鼠標(biāo)事件.通常這些事件會被QApplic aon:notif() 丟掉. (在Qplication:notify()中,是先調(diào)用qApp的過濾器,再對事件進行分析,以決定是否合并或丟棄)
5)繼承QApplication類 并重載ntify()函數(shù).
Qt是用Qpplcation:notify()函數(shù)來分發(fā)事件的想要在任何事件過濾器查看任何事件之前先得到這些事件重載這個函數(shù)是唯一的辦法. 通 常來說事件過濾器更好用一些, 因為不需要去繼承QApplication類.而且可以給QApplication對象安裝任意個數(shù)的事件。
81、什么是Qml
QML是語言的名稱(就像C++,那是一些其他的語言…)
QML代表Qt Meta Language或Qt Modelling Language,是一種人機界面標(biāo)記語言。
QtQuick是QML的一個工具包,允許用QML語言擴展圖形界面(還有其他的QML工具包,有些是圖形化的,如Sailfish Silica或BlackBerry Cascade,還有一些是非圖形化的,如QBS,它是QMake/CMake/make的替代品…)。
QtQuick 1.X變成了基于Qt4.X,使用QPainter/QGraphicsView API來吸引場景。QtQuick 2.X與Qt5.0一起推出,主要基于Scene Graph,這是一個OpenGLES2抽象層,經(jīng)過了相當(dāng)?shù)膬?yōu)化。
隨著Qt5.1的推出,Scene Graph變得更適合應(yīng)用多線程(QtQuick 2.1)和Qt5.2。
82、strcpy/sprintf/memcpy. 它們之間區(qū)別?
(1)執(zhí)行對象 strcpy:字符串 memcpy:可適用于任意數(shù)據(jù)類型 sprintf:目的對象是字符串,源對象可以是字符串、也可以是任意基本類型的數(shù)據(jù) (2) strcpy:不需要指定長度,它遇到被復(fù)制字符的串結(jié)束符"\0"才結(jié)束 memcpy() 會完整的復(fù)制 num 個字節(jié),不會因為遇到“\0”而結(jié)束
83、面向?qū)ο笕筇匦砸约癈++ 成員函數(shù)
面向?qū)ο蟮娜筇匦裕?/p>
? 封裝、繼承、多態(tài)
類的六個默認(rèn)成員函數(shù):
構(gòu)造函數(shù) 拷貝構(gòu)造函數(shù) 析構(gòu)函數(shù) 賦值操作符重載 取地址操作符重載 const修飾的取地址操作符重載
84、使用樣式表要注意的點
父控件采用樣式表設(shè)置屬性后,該屬性會傳遞到其子控件上,除非子控件使用同樣的方法修改屬性。如:利用樣式表設(shè)置父控件最小高度為x,則子控件的最小高度也為x,即使用setFixHeight()修改也無法消除,只能通過樣式表重新設(shè)置子控件的高度才有效。因此一般只對子控件使用樣式設(shè)置。
85、描述Windows下一個消息從觸發(fā)到處理的整個路由過程
應(yīng)用程序啟動, 操作系統(tǒng)為程序創(chuàng)建一個對應(yīng)的消息隊列, 用戶對創(chuàng)建進行操作, 產(chǎn)生一系列消息, 操作系統(tǒng)首先捕捉到這些消息, 將消息投遞到對應(yīng)的消息隊列中, 在應(yīng)用程序中對應(yīng)一個消息循環(huán) 。
消息循環(huán)每次從消息隊列中取出消息, 取出的消息如果是虛擬鍵消息, 會將其轉(zhuǎn)換成標(biāo)準(zhǔn)消息, 將轉(zhuǎn)換的消息再次投遞到消息隊列, 如果取出的是標(biāo)準(zhǔn)消息, 會將該消息發(fā)送給操作系統(tǒng), 操作系統(tǒng) 會調(diào)用對應(yīng)的窗口過程函數(shù), 下窗口過程函數(shù)中對對用的消息進程處理.
86、QApplication的主要作用是什么?
QApplication對象管理QtGui應(yīng)用程序的控制流程和主要的設(shè)置參數(shù)
87、請寫一個調(diào)用消息對話框提示報錯的程序
QMessageBox::waring(his,tr(“警告”), tr(“用戶名或密碼錯誤!”), QMessageBox :Yes)
88、Qt都提供哪些標(biāo)準(zhǔn)對話框以供使用,他們實現(xiàn)什么功能
9個QColorDialog顏色對話框,能夠允許用戶選擇顏色、QErrorMessage顯示錯誤信息、QFileDialog文件對話框,能夠允許用戶選的一個或者多個文件以及目錄、QFontDialog字體對話框,允許用戶選擇/設(shè)置字體、QInputDialog輸入對話框,允許用戶進行簡單的輸入、QPageSetupDialog葉設(shè)置對話框,配置與頁相關(guān)的打印機選項、QProgressDialog進度對話框指示一個長時間操作的工作進度,以提示用戶該操作是否已經(jīng)停止QPrintDialog打印對話框,配置打印機,可以允許用戶選擇可用的打印機、QMessageBox。
89、如何將UI界面問件轉(zhuǎn)化成代碼的. h文件? (假設(shè)ui文件名為gogogo. ui。)
UIC -o gogogo. h gogogo. ui
90、Qt5實現(xiàn)一個文件對話框
需要 #include
QString file_name=QFileDialog::getOpenFileName(this,"請選擇需要打開的文件:",".","*.txt *.png"); //打開文件對話框
//參數(shù)1 父控件
//參數(shù)2 標(biāo)題
//參數(shù)3 默認(rèn)路徑
//參數(shù)4 過濾文件格式
//返回值 文件全路徑---"D:/ss/注意事項.txt"
qDebug()< 91、QMainForm是從哪里派生的? QMainWindow::QWidget::QObject 92、Qwidget、Qobejct實現(xiàn)了哪些功能 QObject 1、信號和槽的非常強大的機制,使用connect()把信號和槽連接起來并且可以用disconnect()來破壞這種連接。為了避免從不結(jié)束的通知循環(huán),你可以調(diào)用blockSignals()臨時地阻塞信號。保護函數(shù)connectNotify()和disconnectNotify()使跟蹤連接成為可能。 2、QObject可以通過event()接收事件并且過濾其它對象的事件。詳細(xì)情況請參考installEventFilter()和eventFilter()。一個方便的處理者,childEvent(),能夠被重新實現(xiàn)來捕獲子對象事件。 3、最后但不是最不重要的一點,QObject提供了Qt中最基本的定時器,關(guān)于定時器的高級支持請參考QTimer。 4、注意Q_OBJECT宏對于任何實現(xiàn)信號、槽和屬性的對象都是強制的。 5、所有的Qt窗口部件繼承了QObject。方便的函數(shù)isWidgetType()返回這個對象實際上是不是一個窗口部件。它比inherits(“QWidget” )快得多。 QWidget 1、QWidget類是所有用戶界面對象的基類。 2、Widget是用戶界面的基本單元:它從窗口系統(tǒng)接收鼠標(biāo),鍵盤和其他事件,并在屏幕上繪制自己。每個Widget都是矩形的,它們按照Z-order進行排序。 93、參數(shù)傳值、指針、引用有什么區(qū)別,在什么場景常用哪種傳遞方式? 傳值、傳址、傳引用的區(qū)別,哪個更高效? 1.傳值 這種傳遞方式中,實參和形參是兩個不同的地址空間,參數(shù)傳遞的實質(zhì)是將原函數(shù)中變量的值,復(fù)制到被調(diào)用函數(shù)形參所在的存儲空間中,這個形參的地址空間在函數(shù)執(zhí)行完畢后,會被回收掉。整個被調(diào)用函數(shù)對形參的操作,只影響形參對應(yīng)的地址空間,不影響原來函數(shù)中的變量的值,因為這兩個不是同一個存儲空間。 即使形參的值在函數(shù)中發(fā)生了變化,實參的值也完全不會受到影響,仍為調(diào)用前的值。 2.傳址 這種參數(shù)傳遞方式中,實參是變量的地址,形參是指針類型的變量,在函數(shù)中對指針變量的操作,就是對實參(變量地址)所對應(yīng)的變量的操作,函數(shù)調(diào)用結(jié)束后,原函數(shù)中的變量的值將會發(fā)生改變。 被調(diào)用函數(shù)中對形參指針?biāo)赶虻牡刂分袃?nèi)容的任何改變都會影響到實參。 3.傳引用 這種參數(shù)傳遞方式中,形參是引用類型變量,其實就是實參的一個別名,在被調(diào)用函數(shù)中,對引用變量的所有操作等價于對實參的操作,這樣,整個函數(shù)執(zhí)行完畢后,原先的實參的值將會發(fā)生改變。 被調(diào)函數(shù)對形參做的任何操作都影響了主調(diào)函數(shù)中的實參變量。 4.哪一種更高效? 在內(nèi)置類型當(dāng)中三種傳遞方式的效率上都差不多; 在自定義類型當(dāng)中,傳引用的更高效一些,因為它沒有對形參進行一次拷貝 94、const與#define有什么區(qū)別 (1)const和#define都可以定義常量,但是const用途更廣。 (2)const 常量有數(shù)據(jù)類型,而宏常量沒有數(shù)據(jù)類型。編譯器可以對前者進行類型安全檢查。而對后者只進行字符替換,沒有類型安全檢查,并且在字符替換可能會產(chǎn)生意料不到的錯誤。 (3) 有些集成化的調(diào)試工具可以對const 常量進行調(diào)試,但是不能對宏常量進行調(diào)試。 95、struct和class有什么區(qū)別? C++中,class與struct都可以定義一個類。他們有以下兩點區(qū)別: 1.默認(rèn)繼承權(quán)限,如果不指定,來自class的繼承按照private繼承處理,來自struct的繼承按照public繼承處理; 2.成員的默認(rèn)訪問權(quán)限。class的成員默認(rèn)是private權(quán)限,struct默認(rèn)是public權(quán)限。 以上兩點也是struct和class最基本的差別,也是最本質(zhì)的差別; 但是在C++中,struct進行了擴展,現(xiàn)在它已經(jīng)不僅僅是一個包含不同數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu)了,它包括了更多的功能。 Struct能包含成員函數(shù)、有自己的構(gòu)造函數(shù)、可以有析構(gòu)函數(shù)、支持繼承、支持多態(tài)、支持Private、Protected、Public關(guān)鍵字。 如果是class的父類是struct關(guān)鍵字描述的,那么默認(rèn)訪問屬性是什么? 當(dāng)出現(xiàn)這種情況時,到底默認(rèn)是public繼承還是private繼承,取決于子類而不是基類。 class可以繼承自struct修飾的類;同時,struct也可以繼承自class修飾的類,繼承屬性如下列描述: class A{}; class B:A{}; // private 繼承 struct B:A{}; // public 繼承 最后,那么到底是使用struct,還是使用class呢? 一般來說,兩個關(guān)鍵字都是可以的,但是由于編程規(guī)范的問題,如果要定義的是一種數(shù)據(jù)結(jié)構(gòu),那么用struct,如果是一種對象的話,那么用class。 96、C++內(nèi)存分配有幾種方式? 內(nèi)存的三種分配方式: 1. 從靜態(tài)存儲區(qū)分配:此時的內(nèi)存在程序編譯的時候已經(jīng)分配好,并且在程序的整個運行期間都存在。全局變量,static變量等在此存儲。 2. 在棧區(qū)分配:相關(guān)代碼執(zhí)行時創(chuàng)建,執(zhí)行結(jié)束時被自動釋放。局部變量在此存儲。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率高,但容量有限。 3. 在堆區(qū)分配:動態(tài)分配內(nèi)存。用new/malloc時開辟,delete/free時釋放。生存期由用戶指定,靈活。但有內(nèi)存泄露等問題。 97、Qt設(shè)計界面有哪些方式? (1)手工編寫創(chuàng)建界面的代碼:此方法比較復(fù)雜,不夠直觀; (2) 使用Qt Designer界面編輯器設(shè)計:可直接拖放控件、設(shè)置控件的屬性,簡單、直觀、易于操作; (3)動態(tài)加載UI文件并生成界面:此方法很靈活,當(dāng)需要更改界面時只需更改.UI文件即可,無需重新編譯程序。 A、手工設(shè)計界面 使用手工創(chuàng)建代碼時,需要從Qt已有的GUI類庫中選擇一個類作為基類繼承,并且添加必要的其它成員。通常,我們會選擇從QDialog、 QWidget、QMainWindow等類中選擇一個作為主窗體;然后創(chuàng)建其它的控件,并使用布局管理器布局這些控件;最后將該布局設(shè)置為主窗體的布 局。此步驟用圖描述如下:例如,對于下圖所示的FindDialog對話框,就可以通過從QDialog繼承,并添加按鈕、布局管理器等到派生類中完成該對話框的設(shè)計。相關(guān)的代碼如下: class FindDialog : public QDialog[喝小酒的網(wǎng)摘]http://blog.hehehehehe.cn/a/8574.htm {Q_OBJECTpublic:FindDialog(QWidget *parent = 0);signals:void findNext(const QString &str, Qt::CaseSensitivity cs);void findPrevious(const QString &str, Qt::CaseSensitivity cs);private slots:void findClicked();void enableFindButton(const QString &text);private: // 窗體中的控件QLabel *label;QLineEdit *lineEdit; QCheckBox *caseCheckBox;QCheckBox *backwardCheckBox;QPushButton *findButton;QPushButton *closeButton; };FindDialog::FindDialog(QWidget *parent): QDialog(parent) {// 下面的代碼創(chuàng)建窗體中的控件label = new QLabel(tr("Find &what:"));lineEdit = new QLineEdit;label->setBuddy(lineEdit);caseCheckBox = new QCheckBox(tr("Match &case"));backwardCheckBox = new QCheckBox(tr("Search &backward"));findButton = new QPushButton(tr("&Find"));findButton->setDefault(true);findButton->setEnabled(false);closeButton = new QPushButton(tr("Close"));connect(lineEdit, SIGNAL(textChanged(const QString &)),this, SLOT(enableFindButton(const QString &)));connect(findButton, SIGNAL(clicked()),this, SLOT(findClicked()));connect(closeButton, SIGNAL(clicked()),this, SLOT(close()));// 使用布局管理器布局控件QHBoxLayout *topLeftLayout = new QHBoxLayout;topLeftLayout->addWidget(label);topLeftLayout->addWidget(lineEdit);QVBoxLayout *leftLayout = new QVBoxLayout;leftLayout->addLayout(topLeftLayout);leftLayout->addWidget(caseCheckBox);leftLayout->addWidget(backwardCheckBox);QVBoxLayout *rightLayout = new QVBoxLayout;rightLayout->addWidget(findButton);rightLayout->addWidget(closeButton);rightLayout->addStretch();QHBoxLayout *mainLayout = new QHBoxLayout;mainLayout->addLayout(leftLayout);mainLayout->addLayout(rightLayout);// 設(shè)置窗口的布局管理器setLayout(mainLayout); setWindowTitle(tr("Find"));setFixedHeight(sizeHint().height()); } B、使用Qt Designer設(shè)計界面 采用Qt Designer,使得快速創(chuàng)建對話框成為可能。在Qt Designer環(huán)境中,所有的操作都采用可視化的操作,可拖放控件、關(guān)聯(lián)信號與槽、設(shè)置特定控件的屬性。使用Qt Designer設(shè)計界面的方法如下圖所示: C、動態(tài)加載UI文件并生成界面 前面的兩種方法需要事先創(chuàng)建好相應(yīng)的文件或代碼,然后連同其它文件進行編譯,如果后期要修改界面則必須修改代碼或UI文件并重新編譯。而不需要重新編譯整個程序的方法是采用動態(tài)加載UI文件的方式?;镜牟僮鞣椒橄仁褂肣t Designer設(shè)計界面,然后按下圖的流程操作: 如下圖所示,創(chuàng)建一個mainwindow.ui的UI文件。之后就可以采用QUILoader類動態(tài)加載該文件,并生成該窗體。參考的代碼如下: #include #include {QApplication a(argc, argv);QUiLoader loader;QFile file("mainwindow.ui");loader.load(&file)->show();return a.exec(); }上面的代碼中UiLoader::load()使用了QFile對像作為數(shù)據(jù)源,并且會生成QWidget對像,最后使用了QWidget::show()顯示上圖中的窗體界面。另外需要注意的是,如果要使能UiLoader動態(tài)加載特性,必須在工程文件*.pro中添加如下行:CONFIG += uitools 98、Qt Socket通信的過程 Qt Socket通信的過程主要分為以下幾步: 創(chuàng)建Socket:使用QTcpSocket類創(chuàng)建Socket,并初始化連接參數(shù);連接服務(wù)器:使用connectToHost()函數(shù)連接服務(wù)器;發(fā)送數(shù)據(jù):使用write()函數(shù)發(fā)送數(shù)據(jù);接收數(shù)據(jù):使用read()函數(shù)接收數(shù)據(jù);斷開連接:使用disconnectFromHost()函數(shù)斷開連接。 99、QWidget和QML的技術(shù)本質(zhì)和使用上,有什么區(qū)別? QWidget是一種基于C++的桌面應(yīng)用程序開發(fā)技術(shù),主要用于開發(fā)桌面應(yīng)用程序,它是一種面向?qū)ο蟮募夹g(shù),可以使用C++語言來實現(xiàn)用戶界面的設(shè)計和編程。 QML是一種基于JavaScript的應(yīng)用程序開發(fā)技術(shù),主要用于開發(fā)桌面應(yīng)用程序和移動應(yīng)用程序,它是一種基于聲明式的技術(shù),可以使用JavaScript語言來實現(xiàn)用戶界面的設(shè)計和編程。 兩者的本質(zhì)有所不同,QWidget是基于C++的,QML是基于JavaScript的;使用上也有所不同,QWidget是面向?qū)ο蟮模琎ML是基于聲明式的。 100、用Qt實現(xiàn)一個三角形的按鈕,會如何實現(xiàn)? 首先,我們需要使用Qt的QPushButton類來創(chuàng)建一個按鈕,然后設(shè)置按鈕的樣式,使其可以顯示出一個三角形的形狀。 創(chuàng)建QPushButton類的實例,并設(shè)置按鈕的樣式: QPushButton *triangleButton = new QPushButton(); triangleButton->setStyleSheet(“QPushButton{border-image:url(:/images/triangle.png);}”); 設(shè)置按鈕的大?。?/p> triangleButton->setFixedSize(QSize(30, 30)); 連接按鈕的點擊信號和槽函數(shù): connect(triangleButton, SIGNAL(clicked()), this, SLOT(onTriangleButtonClicked())); 實現(xiàn)槽函數(shù): void onTriangleButtonClicked() { // 在這里實現(xiàn)點擊三角形按鈕時要執(zhí)行的操作 } 100、用Qt實現(xiàn)一個三角形的按鈕,會如何實現(xiàn)? 首先,我們需要使用Qt的QPushButton類來創(chuàng)建一個按鈕,然后設(shè)置按鈕的樣式,使其可以顯示出一個三角形的形狀。 創(chuàng)建QPushButton類的實例,并設(shè)置按鈕的樣式: QPushButton *triangleButton = new QPushButton(); triangleButton->setStyleSheet(“QPushButton{border-image:url(:/images/triangle.png);}”); 設(shè)置按鈕的大?。?/p> triangleButton->setFixedSize(QSize(30, 30)); 連接按鈕的點擊信號和槽函數(shù): connect(triangleButton, SIGNAL(clicked()), this, SLOT(onTriangleButtonClicked())); 實現(xiàn)槽函數(shù): void onTriangleButtonClicked() { // 在這里實現(xiàn)點擊三角形按鈕時要執(zhí)行的操作 } 101、Qt如何實現(xiàn)類似QQ登錄窗口的翻轉(zhuǎn) Qt可以使用QPropertyAnimation類來實現(xiàn)QQ登錄窗口的翻轉(zhuǎn)效果。 1、首先,創(chuàng)建一個QPropertyAnimation對象,并設(shè)置動畫的目標(biāo)對象、屬性和時間曲線: QPropertyAnimation *animation = new QPropertyAnimation(this, “geometry”); animation->setDuration(500); animation->setEasingCurve(QEasingCurve::OutExpo); 2、然后,設(shè)置動畫的起始值和結(jié)束值: //設(shè)置起始值 QRect startRect(0, 0, width(), height()); animation->setStartValue(startRect); //設(shè)置結(jié)束值 QRect endRect(width(), 0, -width(), height()); animation->setEndValue(endRect); 3、最后,啟動動畫: animation->start(); 102、Qt窗口圓角如何實現(xiàn) 在Qt中實現(xiàn)窗口圓角,可以使用Qt的樣式表實現(xiàn),如下所示: QWidget { border-radius: 10px; } 可以使用如下代碼來應(yīng)用樣式表: QFile file("style.qss"); file.open(QFile::ReadOnly); QString styleSheet = QLatin1String(file.readAll()); qApp->setStyleSheet(styleSheet); 103、Qt的智能指針,QSharePoint和shared_ptr有什么區(qū)別,weak_ptr呢? Qt智能指針是一種特殊的指針,它可以指向另一個指針。它可以用來創(chuàng)建復(fù)雜的數(shù)據(jù)結(jié)構(gòu),如鏈表或樹結(jié)構(gòu)。 QSharePoint是一種智能指針,它可以自動管理指向的對象的內(nèi)存分配和釋放,從而實現(xiàn)自動內(nèi)存管理。 shared_ptr也是一種智能指針,它可以跟蹤指向的對象的引用計數(shù),從而保證在沒有任何引用的情況下,可以自動釋放指向的對象。 weak_ptr是一種特殊的shared_ptr,它可以指向shared_ptr指向的對象,但不會增加指向?qū)ο蟮囊糜嫈?shù)。它可以用來避免循環(huán)引用導(dǎo)致的內(nèi)存泄漏問題。 在Qt中,指針指針(Pointer to Pointer)是一種指向指針的指針,通常用于動態(tài)分配內(nèi)存或者多級指針操作。而QSharedPointer和std::shared_ptr都是C++11中的智能指針,用于管理動態(tài)內(nèi)存,可以避免內(nèi)存泄漏和空懸指針等問題。它們的主要區(qū)別如下: QSharedPointer是Qt框架提供的智能指針,而std::shared_ptr是C++11標(biāo)準(zhǔn)庫提供的智能指針。 QSharedPointer可以與QObject一起使用,可以自動處理QObject的引用計數(shù),當(dāng)QObject被刪除時,QSharedPointer會自動釋放對它的引用。而std::shared_ptr無法處理QObject的引用計數(shù),需要手動管理。 QSharedPointer可以使用qSharedPointerCast進行類型轉(zhuǎn)換,可以方便地將一個QSharedPointer轉(zhuǎn)換為另一個QSharedPointer。而std::shared_ptr只能使用std::dynamic_pointer_cast進行類型轉(zhuǎn)換。 QSharedPointer的默認(rèn)刪除器是delete,而std::shared_ptr的默認(rèn)刪除器是std::default_delete。 相比之下,weak_ptr則是用來解決shared_ptr循環(huán)引用問題的。當(dāng)兩個或多個shared_ptr相互引用時,會形成循環(huán)引用,導(dǎo)致內(nèi)存泄漏。此時,可以使用weak_ptr來打破其中一個shared_ptr的引用,避免循環(huán)引用。weak_ptr是一種弱引用,它不會增加內(nèi)存對象的引用計數(shù),只是用來觀察對象是否已經(jīng)被釋放,可以通過lock方法獲取其對應(yīng)的shared_ptr。 104、 Qt的信號與槽,有哪幾種連接方式,對應(yīng)的應(yīng)用場景是什么? Qt的信號與槽有三種連接方式: 信號槽的直接連接:使用QObject::connect()函數(shù)連接信號和槽,當(dāng)信號發(fā)出時,槽函數(shù)自動被調(diào)用,適用于信號發(fā)出者與槽函數(shù)擁有者在同一線程的場景。 信號槽的槽函數(shù)鏈接:使用QObject::connect()函數(shù)連接信號和槽函數(shù),當(dāng)信號發(fā)出時,槽函數(shù)被調(diào)用,適用于信號發(fā)出者與槽函數(shù)擁有者不在同一線程的場景。 信號槽的信號連接:使用QObject::connect()函數(shù)連接信號和信號,當(dāng)信號發(fā)出時,另一個信號也會發(fā)出,適用于信號發(fā)出者與槽函數(shù)擁有者不在同一線程的場景。 105、QShareDataPoint作用 QShareDataPoint是一種用于收集和分享數(shù)據(jù)的技術(shù)。它可以幫助企業(yè)收集、分析和共享數(shù)據(jù),以便更好地管理業(yè)務(wù)。它可以幫助企業(yè)收集和分析客戶行為,改進服務(wù)質(zhì)量,提高運營效率,并幫助企業(yè)更好地理解市場動態(tài)。 106、死鎖怎么解決? 避免死鎖:可以采用一些技術(shù)避免死鎖的發(fā)生,比如破壞互斥條件、破壞請求和保持條件、破壞循環(huán)等等。 預(yù)防死鎖:可以采用一些技術(shù)來預(yù)防死鎖的發(fā)生,比如限制進程獲取資源的數(shù)量、安全序列、死鎖檢測等等。 解除死鎖:可以采用一些技術(shù)來解除已經(jīng)發(fā)生的死鎖,比如銀行家算法、延遲算法等等。 107、創(chuàng)建的對象有幾種方式,有什么區(qū)別 Qt創(chuàng)建對象有兩種方式: 1、使用Qt自帶的構(gòu)造函數(shù),如QWidget,QPushButton,QDialog等。 2、使用Qt的meta-object系統(tǒng),如QMetaObject::newInstance,QMetaObject::invokeMethod等。 這兩種方式的區(qū)別在于,第一種方式是使用Qt自帶的構(gòu)造函數(shù),它可以直接創(chuàng)建Qt對象,但是不能實現(xiàn)動態(tài)創(chuàng)建,也不能調(diào)用它們的函數(shù)或者訪問它們的成員變量。 第二種方式是使用Qt的meta-object系統(tǒng),它可以實現(xiàn)動態(tài)創(chuàng)建Qt對象,可以調(diào)用它們的函數(shù)或者訪問它們的成員變量。 108、你能用幾種方法修改QPushButton的大小,文字顏色等屬性。 使用Qt Designer設(shè)計師:可以使用Qt Designer設(shè)計師來調(diào)整QPushButton的大小、文字顏色等屬性。 使用Qt的API:可以使用Qt的API來調(diào)整QPushButton的大小、文字顏色等屬性,例如:setMinimumSize()、setMaximumSize()、setStyleSheet()等。 使用CSS:可以使用CSS語法來調(diào)整QPushButton的大小、文字顏色等屬性,例如:QPushButton {width: 100px; height: 50px; color: red;}。 109、常用的Qt布局有幾種,如何自適應(yīng)縮放? Qt布局有以下幾種: 絕對布局:使用絕對位置和尺寸來定位和調(diào)整控件的大?。? 盒子布局:使用排列和填充來定位和調(diào)整控件的大??; 柵格布局:使用表格排列和調(diào)整控件的大小; 流式布局:使用流動排列和調(diào)整控件的大小; 堆棧布局:使用堆棧排列和調(diào)整控件的大小。 Qt支持自適應(yīng)縮放,可以使用Qt的布局管理器來實現(xiàn)。例如,可以使用QGridLayout管理器來控制窗口的尺寸和位置,使其能夠根據(jù)窗口大小的變化而自動調(diào)整控件的大小和位置。 110、Qt如何實現(xiàn)QQ兩個客戶端的私聊功能? Qt可以通過使用Qt的網(wǎng)絡(luò)模塊來實現(xiàn)QQ兩個客戶端的私聊功能。Qt的網(wǎng)絡(luò)模塊提供了一系列的網(wǎng)絡(luò)協(xié)議,可以用于實現(xiàn)QQ兩個客戶端之間的私聊功能。 具體的實現(xiàn)步驟如下: 建立兩個客戶端的網(wǎng)絡(luò)連接:首先,使用Qt的網(wǎng)絡(luò)模塊建立兩個客戶端之間的網(wǎng)絡(luò)連接,以便進行私聊。 實現(xiàn)私聊功能:使用Qt的網(wǎng)絡(luò)模塊實現(xiàn)兩個客戶端之間的私聊功能,以便交換文字、圖片等信息。 實現(xiàn)斷開連接:在兩個客戶端之間的私聊結(jié)束后,可以使用Qt的網(wǎng)絡(luò)模塊實現(xiàn)斷開連接,以便釋放資源。 111、Qt的多線程,哪些是只有Qthread能實現(xiàn),QtConcurrent辦不到的? 1、QThread可以使用信號和槽機制,而QtConcurrent不支持。 2、QThread可以設(shè)置線程優(yōu)先級,而QtConcurrent不支持。 3、QThread可以實現(xiàn)跨平臺多線程,而QtConcurrent只能在支持C++11的平臺上實現(xiàn)。 4、QThread可以實現(xiàn)更復(fù)雜的多線程任務(wù),而QtConcurrent只能實現(xiàn)簡單的多線程任務(wù)。 112、什么是UI線程,UI線程阻塞后會怎樣? UI線程是Android應(yīng)用程序中的主線程,它負(fù)責(zé)繪制UI界面,處理用戶交互,以及調(diào)度其他相關(guān)任務(wù)。如果UI線程被阻塞,用戶界面將會停止響應(yīng),甚至可能會出現(xiàn)應(yīng)用程序崩潰的情況。 113、Qt中的兄弟窗口,想刷新重疊部分,請問流程是什么樣的,刷新的順序是什么樣的? 1、調(diào)用QWidget的update函數(shù),更新當(dāng)前窗口的所有內(nèi)容; 2、調(diào)用QWidget的updateGeometry函數(shù),更新當(dāng)前窗口的geometry; 3、調(diào)用QWidget的update函數(shù),更新當(dāng)前窗口的子窗口; 4、調(diào)用QWidget的updateGeometry函數(shù),更新當(dāng)前窗口的子窗口的geometry; 5、調(diào)用QWidget的update函數(shù),更新兄弟窗口; 6、調(diào)用QWidget的updateGeometry函數(shù),更新兄弟窗口的geometry; 7、調(diào)用QWidget的update函數(shù),更新兄弟窗口的子窗口; 8、調(diào)用QWidget的updateGeometry函數(shù),更新兄弟窗口的子窗口的geometry; 9、調(diào)用QWidget的repaint函數(shù),刷新重疊部分。 刷新的順序是:更新當(dāng)前窗口、更新當(dāng)前窗口的子窗口、更新兄弟窗口、更新兄弟窗口的子窗口、刷新重疊部分。 114、Qt如何操作數(shù)據(jù)庫 Qt操作數(shù)據(jù)庫主要是使用Qt的QSqlDatabase類,它提供了一系列的函數(shù)來連接、操作、查詢和管理數(shù)據(jù)庫。在Qt中,可以使用QSqlQuery類來查詢數(shù)據(jù)庫,QSqlTableModel類來操作數(shù)據(jù)庫表,QSqlRelationalTableModel類來管理關(guān)系數(shù)據(jù)庫表,QSqlError類來檢測錯誤,QSqlDriver類來檢查數(shù)據(jù)庫驅(qū)動程序,QSqlIndex類來創(chuàng)建索引,QSqlRecord類來操作數(shù)據(jù)庫記錄,QSqlResult類來執(zhí)行查詢等等。 115、Qt Remote Object的序列化與反序列化 Qt Remote Objects是一個基于Qt的遠(yuǎn)程對象系統(tǒng),它可以讓你在不同的進程之間共享對象。它使用Qt的序列化技術(shù)來序列化和反序列化遠(yuǎn)程對象,以便在不同的進程之間發(fā)送和接收。 Qt的序列化技術(shù)使用QDataStream類來序列化和反序列化基本的C++數(shù)據(jù)類型,QVariant和QObject的子類。它也支持自定義類型的序列化,只要實現(xiàn)QDataStream的operator<<和operator>>運算符重載。 Qt Remote Objects使用QDataStream來序列化和反序列化遠(yuǎn)程對象。當(dāng)客戶端請求遠(yuǎn)程對象時,Qt Remote Objects將使用QDataStream將遠(yuǎn)程對象序列化,然后發(fā)送給客戶端。當(dāng)客戶端收到序列化的遠(yuǎn)程對象時,它將使用QDataStream將遠(yuǎn)程對象反序列化,然后可以訪問遠(yuǎn)程對象的屬性和方法。 116、什么情況下,delete需要加一個中括號[] 當(dāng)要刪除的變量是一個數(shù)組時,delete需要加一個中括號[ ],例如:delete array[0]; 117、描述過程,如何實現(xiàn)一個自定義按鈕,使其在光標(biāo)進入,按下,離開三種狀態(tài)下顯示不同的圖片 在項目中創(chuàng)建三張圖片,分別表示光標(biāo)進入,按下,離開三種狀態(tài)下的圖片。 創(chuàng)建一個自定義按鈕類,在該類中重寫onMouseEnter,onMouseDown,onMouseLeave三個事件,分別設(shè)置按鈕的圖片為對應(yīng)的三張圖片。 在需要使用自定義按鈕的地方,實例化自定義按鈕類,并將其添加到頁面中,即可實現(xiàn)光標(biāo)進入,按下,離開三種狀態(tài)下顯示不同的圖片。 118、什么是Qt事件循環(huán)? Qt事件循環(huán)是一種程序架構(gòu),它用于處理窗口系統(tǒng)和其他用戶界面事件,以及與用戶界面無關(guān)的事件,例如定時器和網(wǎng)絡(luò)事件。 Qt事件循環(huán)以循環(huán)的方式運行,每次循環(huán)都會檢查是否有新的事件,如果有,就會調(diào)用相應(yīng)的處理程序來處理它們。 119、Qt打包程序 安裝pyinstaller:使用pip安裝pyinstaller,在命令行輸入pip install pyinstaller即可。 生成可執(zhí)行文件:在命令行輸入pyinstaller -F -w ,其中filename為要打包的文件名。 打包:將生成的可執(zhí)行文件和其他需要的文件(如圖片、音頻等)放到一個文件夾中,然后使用pyinstaller -F -w 命令打包。 安裝:將生成的可執(zhí)行文件安裝到目標(biāo)系統(tǒng)中即可。 120、純虛函數(shù)和普通的虛函數(shù)有什么區(qū)別 1、純虛函數(shù)是抽象類中的虛函數(shù),它只有聲明沒有實現(xiàn),它的實現(xiàn)由派生類完成,純虛函數(shù)必須在派生類中實現(xiàn)。 2、普通虛函數(shù)是普通類中的虛函數(shù),它有聲明也有實現(xiàn),派生類可以重定義它,也可以不重定義它。 121、虛繼承的作用 虛繼承的作用是避免了多重繼承時出現(xiàn)的二義性問題,即消除了基類的二義性,使得子類中只有一個完整的基類對象,從而避免了多重繼承時出現(xiàn)的二義性問題。 122、軟件如果出現(xiàn)問題,如何去定位的,如何處理的? 定位:首先,可以通過日志記錄和錯誤報告來定位問題,以及分析程序的運行狀態(tài),以便找出問題的根源。其次,可以使用分析工具,如性能分析工具、程序調(diào)試器等,以及通過檢查程序中的代碼,以及分析程序的運行狀態(tài),來定位問題。 處理:一旦定位到問題,就可以使用相應(yīng)的工具或方法來處理問題,比如修改程序的代碼,或者增加程序的功能,以及改善程序的性能。 123、為什么要異步刷新,如何異步刷新? 為什么要異步刷新: 異步刷新是指在不阻塞用戶界面的情況下,在后臺更新數(shù)據(jù),以便及時響應(yīng)用戶的操作。異步刷新能夠有效地提高網(wǎng)站的性能,提升用戶體驗,減少網(wǎng)絡(luò)資源的浪費。 如何異步刷新: 異步刷新可以通過AJAX技術(shù)實現(xiàn),AJAX是一種用于在后臺與服務(wù)器進行數(shù)據(jù)交換的技術(shù),它可以在不重新加載整個頁面的情況下更新部分頁面內(nèi)容。此外,還可以使用WebSocket技術(shù)實現(xiàn)異步刷新,WebSocket是一種雙向通信協(xié)議,它可以在瀏覽器和服務(wù)器之間建立持久連接,以實現(xiàn)雙向通信。 124、windows系統(tǒng)下,是怎么實現(xiàn)窗口刷新(窗口刷新機制);是立即刷新,還是異步刷新;每次我需要一個窗口刷新,他都能立馬刷新嗎 Windows系統(tǒng)使用異步刷新機制來實現(xiàn)窗口刷新,這意味著窗口刷新可能不會立即發(fā)生,而是在需要的時候才發(fā)生。比如,當(dāng)用戶移動窗口時,Windows會收集所有窗口的移動操作,然后在一次更新中完成所有窗口的移動。這樣做的好處是,它可以減少系統(tǒng)的負(fù)擔(dān),從而提高系統(tǒng)的性能。 125、如何將鍵盤和鼠標(biāo)的相關(guān)操作過濾出來并關(guān)聯(lián)到自己想要執(zhí)行的函數(shù)上? 使用jQuery的事件處理函數(shù),可以監(jiān)聽鍵盤和鼠標(biāo)的操作,并將它們關(guān)聯(lián)到自己想要執(zhí)行的函數(shù)上。 126、C++多線程加鎖,會劣化性能,請問有什么優(yōu)化的手段? 合理設(shè)計程序:盡量減少加鎖的次數(shù),把多個操作放到一個加鎖的代碼段中,減少加鎖的次數(shù)。 使用原子操作:C++11提供了一些原子操作,比如atomic_compare_exchange_strong()等,可以用來替代鎖,在某些場合可以有效提高性能。 使用鎖的粒度更小的技術(shù):比如讀寫鎖、條件變量等,可以替代普通的互斥鎖,減少鎖的粒度,提高性能。 使用鎖的粒度更大的技術(shù):比如使用全局鎖、細(xì)粒度鎖等,可以減少鎖的粒度,提高性能。 使用非阻塞技術(shù):比如CAS操作,可以避免線程之間的阻塞,從而提高性能。 127、Qt 中的容器類包括 QList:動態(tài)數(shù)組,支持隨機訪問和快速插入、刪除操作。 QVector:類似 QList,但具有更好的性能。 QLinkedList:雙向鏈表,支持快速插入、刪除操作。 QHash:哈希表,支持快速查找、插入、刪除操作。 QMap:基于紅黑樹的映射表,支持快速查找、插入、刪除操作。 128、Qt中的模型視圖框架是什么? 模型視圖框架是Qt中用于顯示數(shù)據(jù)的一種機制。該框架將數(shù)據(jù)模型和視圖分離,使得數(shù)據(jù)的表示和顯示可以獨立地進行管理。數(shù)據(jù)模型提供了數(shù)據(jù)的接口,視圖則負(fù)責(zé)繪制和交互。Qt中提供了多種類型的模型視圖類庫,包括QAbstractItemModel、QStandardItemModel、QListView、QTableView等。 129、Qt中的插件是什么? 插件是Qt中用于擴展應(yīng)用程序功能的一種機制。插件可以是動態(tài)鏈接庫或靜態(tài)鏈接庫,包含了一些特定的功能。Qt提供了QPluginLoader類和QFactoryInterface類用于管理插件。通過插件,可以將應(yīng)用程序的功能分解為多個獨立的部分,方便開發(fā)和維護。 130、Qt中的樣式表是什么? 樣式表是Qt中用于定制界面風(fēng)格的一種機制。樣式表使用CSS語法,可以定義界面元素的屬性、顏色、字體等。Qt中的樣式表可以應(yīng)用于整個應(yīng)用程序或特定的控件,使得應(yīng)用程序的界面可以與眾不同。Qt還提供了QStyle類和QStyleFactory類,用于管理系統(tǒng)默認(rèn)樣式和自定義樣式。 131、什么是Qt的MVC架構(gòu)? Qt的MVC架構(gòu)是一種基于模型、視圖和控制器的設(shè)計模式,它將應(yīng)用程序的數(shù)據(jù)、用戶界面和業(yè)務(wù)邏輯分離開來,使得各個部分之間的耦合度更低,易于維護和擴展。 132、什么是Qt的插件機制? Qt的插件機制是一種將應(yīng)用程序的功能模塊化的方法。通過使用Qt的插件機制,可以將應(yīng)用程序的一些功能打包成獨立的插件,這些插件可以在運行時動態(tài)加載和卸載,從而實現(xiàn)應(yīng)用程序的可擴展性和靈活性。 133、sizeof/strlen區(qū)別? C語言中malloc和C+ +語言中new有何區(qū)別? C/C++ 程序編譯的內(nèi)存分配情況? sizeof和strlen的區(qū)別: sizeof是一個操作符,可以用來獲取一個變量或類型的字節(jié)數(shù),不受變量值的影響。例如,sizeof(int)返回4,sizeof(char)返回1。 strlen是一個函數(shù),用于獲取一個字符串的長度,即字符數(shù)組中的字符個數(shù),不包括字符串結(jié)束符’\0’。例如,strlen(“hello”)返回5。 malloc和new的區(qū)別: malloc和new都是用于在堆上分配內(nèi)存的函數(shù)。它們的主要區(qū)別在于以下幾點: malloc返回一個void*指針,需要強制轉(zhuǎn)換為目標(biāo)類型指針,而new直接返回目標(biāo)類型指針。 malloc只負(fù)責(zé)分配內(nèi)存,不會自動調(diào)用構(gòu)造函數(shù),而new在分配內(nèi)存后會自動調(diào)用構(gòu)造函數(shù)。 malloc分配的內(nèi)存可以使用free函數(shù)釋放,而new分配的內(nèi)存必須使用delete操作符釋放。 C/C++程序編譯的內(nèi)存分配情況: 在C/C++程序編譯時,內(nèi)存分配主要分為以下幾種情況: 棧內(nèi)存分配:用于存儲局部變量和函數(shù)調(diào)用的參數(shù)和返回值。棧內(nèi)存分配由編譯器自動完成,不需要手動分配和釋放。 堆內(nèi)存分配:用于存儲動態(tài)分配的內(nèi)存,需要使用malloc或new函數(shù)手動分配,并在不需要時使用free或delete操作符釋放。 全局變量和靜態(tài)變量分配:在程序運行前就進行內(nèi)存分配,存儲在靜態(tài)存儲區(qū)或全局存儲區(qū),程序結(jié)束后才會釋放。 134、strcpy/sprintf/memcpy它們之間區(qū)別? strcpy、sprintf和memcpy都是C語言中的字符串和內(nèi)存操作函數(shù),它們之間的主要區(qū)別如下: strcpy用于將一個字符串復(fù)制到另一個字符串中,比如將src字符串復(fù)制到dest字符串中。函數(shù)原型為:char *strcpy(char *dest, const char *src); sprintf用于將格式化的數(shù)據(jù)轉(zhuǎn)換成字符串并存儲到另一個字符串中,比如將格式化的數(shù)字或文本保存到buf字符串中。函數(shù)原型為:int sprintf(char *buf, const char *format, …); memcpy用于將一段內(nèi)存的內(nèi)容復(fù)制到另一個內(nèi)存中,比如將src內(nèi)存的內(nèi)容復(fù)制到dest內(nèi)存中。函數(shù)原型為:void *memcpy(void *dest, const void *src, size_t n); 總的來說,這三個函數(shù)的作用不同,strcpy和memcpy主要用于字符串和內(nèi)存的復(fù)制,而sprintf主要用于格式化數(shù)據(jù)的轉(zhuǎn)換。使用時需要結(jié)合具體的需求選擇合適的函數(shù)來操作。 135、面向?qū)ο蟮娜筇卣鳎?C+ +語言的空類有哪些成員函數(shù)? 面向?qū)ο蟮娜筇卣魇牵?/p> 封裝:將數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)封裝在一個類中,隱藏了具體實現(xiàn)細(xì)節(jié),只暴露必要的接口給外部使用,保證了數(shù)據(jù)的安全性和可靠性。 繼承:通過繼承已有的類,可以擴展其功能,減少代碼的冗余,提高代碼的復(fù)用性和可維護性。 多態(tài):通過函數(shù)重載、虛函數(shù)和模板等機制,實現(xiàn)不同對象對同一消息的不同響應(yīng),提高了程序的靈活性和可擴展性。 C++語言的空類是指沒有任何成員變量和成員函數(shù)的類??疹惸J(rèn)會自動生成一些成員函數(shù),包括默認(rèn)構(gòu)造函數(shù)、析構(gòu)函數(shù)、拷貝構(gòu)造函數(shù)和賦值運算符等。如果需要控制這些默認(rèn)生成的成員函數(shù)的行為,可以通過定義相應(yīng)的函數(shù)來實現(xiàn)。 136、多態(tài)實現(xiàn)的原理?鏈表和數(shù)組有何區(qū)別?隊列和棧區(qū)別? 多態(tài)實現(xiàn)的原理: 多態(tài)是面向?qū)ο缶幊讨械囊环N重要概念,實現(xiàn)多態(tài)的關(guān)鍵是虛函數(shù)和指針。在C++中,通過將基類中的某個函數(shù)聲明為虛函數(shù),可以使得派生類中的同名函數(shù)成為虛函數(shù),從而實現(xiàn)多態(tài)。當(dāng)通過基類指針或引用調(diào)用虛函數(shù)時,會根據(jù)指針或引用實際指向的對象類型來調(diào)用相應(yīng)的函數(shù),實現(xiàn)了動態(tài)綁定。這樣就可以在不同的對象之間實現(xiàn)相同的操作,提高了代碼的復(fù)用性和可維護性。 鏈表和數(shù)組的區(qū)別: 鏈表和數(shù)組都是常見的數(shù)據(jù)結(jié)構(gòu),它們的主要區(qū)別在于: 數(shù)組是一組具有相同數(shù)據(jù)類型的元素的集合,可以通過下標(biāo)來訪問每個元素,但是數(shù)組的長度是固定的,不能動態(tài)改變。 鏈表是由一組節(jié)點構(gòu)成的數(shù)據(jù)結(jié)構(gòu),每個節(jié)點包含數(shù)據(jù)和指向下一個節(jié)點的指針,可以動態(tài)添加、刪除、修改節(jié)點,但是訪問鏈表中的元素需要遍歷整個鏈表,效率較低。 隊列和棧的區(qū)別: 隊列和棧都是常見的數(shù)據(jù)結(jié)構(gòu),它們的主要區(qū)別在于: 隊列是一種先進先出(FIFO)的數(shù)據(jù)結(jié)構(gòu),可以在隊列的一端插入元素,在隊列的另一端刪除元素,典型的應(yīng)用是操作系統(tǒng)的進程調(diào)度。 棧是一種后進先出(LIFO)的數(shù)據(jù)結(jié)構(gòu),可以在棧頂插入和刪除元素,典型的應(yīng)用是函數(shù)調(diào)用和表達(dá)式求值。 137、多態(tài)實現(xiàn)的原理?鏈表和數(shù)組有何區(qū)別?隊列和棧區(qū)別? 多態(tài)實現(xiàn)的原理: 多態(tài)是面向?qū)ο缶幊讨械囊环N重要概念,實現(xiàn)多態(tài)的關(guān)鍵是虛函數(shù)和指針。在C++中,通過將基類中的某個函數(shù)聲明為虛函數(shù),可以使得派生類中的同名函數(shù)成為虛函數(shù),從而實現(xiàn)多態(tài)。當(dāng)通過基類指針或引用調(diào)用虛函數(shù)時,會根據(jù)指針或引用實際指向的對象類型來調(diào)用相應(yīng)的函數(shù),實現(xiàn)了動態(tài)綁定。這樣就可以在不同的對象之間實現(xiàn)相同的操作,提高了代碼的復(fù)用性和可維護性。 鏈表和數(shù)組的區(qū)別: 鏈表和數(shù)組都是常見的數(shù)據(jù)結(jié)構(gòu),它們的主要區(qū)別在于: 數(shù)組是一組具有相同數(shù)據(jù)類型的元素的集合,可以通過下標(biāo)來訪問每個元素,但是數(shù)組的長度是固定的,不能動態(tài)改變。 鏈表是由一組節(jié)點構(gòu)成的數(shù)據(jù)結(jié)構(gòu),每個節(jié)點包含數(shù)據(jù)和指向下一個節(jié)點的指針,可以動態(tài)添加、刪除、修改節(jié)點,但是訪問鏈表中的元素需要遍歷整個鏈表,效率較低。 隊列和棧的區(qū)別: 隊列和棧都是常見的數(shù)據(jù)結(jié)構(gòu),它們的主要區(qū)別在于: 隊列是一種先進先出(FIFO)的數(shù)據(jù)結(jié)構(gòu),可以在隊列的一端插入元素,在隊列的另一端刪除元素,典型的應(yīng)用是操作系統(tǒng)的進程調(diào)度。 棧是一種后進先出(LIFO)的數(shù)據(jù)結(jié)構(gòu),可以在棧頂插入和刪除元素,典型的應(yīng)用是函數(shù)調(diào)用和表達(dá)式求值。 138、&&/ & II |有什么區(qū)別? Typedef/define/const/static 有什么區(qū)別? &&是邏輯與運算符,表示兩個條件都為真時結(jié)果為真。 &是位運算符,表示對兩個數(shù)的位進行與運算。 ||是邏輯或運算符,表示兩個條件中有一個為真時結(jié)果為真。 |是位運算符,表示對兩個數(shù)的位進行或運算。 Typedef/define/const/static 有什么區(qū)別? typedef:用來給數(shù)據(jù)類型取一個新的名字,方便在程序中使用。例如:typedef int INT; 表示將int類型取一個新的名字INT。 define:用來定義常量或宏,會在編譯時被預(yù)處理器替換為定義的內(nèi)容。例如:#define PI 3.14159 表示將PI定義為3.14159。 const:用來定義常量,在程序中不可修改。例如:const int MAX_NUM = 100; 表示將MAX_NUM定義為100,不可修改。 static:用來定義靜態(tài)變量,靜態(tài)變量只會被初始化一次,不會隨函數(shù)的調(diào)用而重復(fù)初始化。靜態(tài)變量的作用域只在定義它的函數(shù)內(nèi),但是它的值會被保留。靜態(tài)函數(shù)只能在定義它的文件內(nèi)使用。 139、如何避免“野指針”? 野指針是指指向無效內(nèi)存地址的指針,它的出現(xiàn)往往會導(dǎo)致程序崩潰或者產(chǎn)生不可預(yù)期的結(jié)果。為了避免野指針的出現(xiàn),可以采取以下措施: 將指針初始化為NULL或nullptr,即空指針,可以防止指針誤用。 在指針被釋放之后,將其賦值為NULL或nullptr,可以避免指針成為野指針。 使用智能指針,智能指針可以自動管理動態(tài)內(nèi)存,避免內(nèi)存泄漏和野指針問題。 避免指針操作過程中越界訪問數(shù)組,可以使用STL容器代替數(shù)組,STL容器可以自動管理內(nèi)存。 對于指向棧上的變量的指針,需要注意其生命周期,避免在變量被銷毀后仍然使用指針。 總之,在程序中使用指針時,需要時刻保持警惕,避免出現(xiàn)野指針問題。 140、向鏈表的末尾添加一個元素?從鏈表尾部到頭部打印結(jié)點信息?如何合并兩個有序鏈表? 向鏈表的末尾添加一個元素的步驟如下: 新建一個節(jié)點,為其賦值; 遍歷鏈表,找到最后一個節(jié)點; 將最后一個節(jié)點的next指針指向新節(jié)點; 新節(jié)點的next指針賦值為NULL,表示鏈表的末尾。 從鏈表尾部到頭部打印節(jié)點信息的步驟如下: 遍歷鏈表,將每個節(jié)點的值存放在棧中; 遍歷棧,將棧中的值彈出并輸出,即為鏈表的逆序輸出。 合并兩個有序鏈表的步驟如下: 新建一個空鏈表,作為合并后的鏈表; 遍歷兩個有序鏈表,比較當(dāng)前兩個鏈表的節(jié)點值,將較小的節(jié)點插入到新鏈表中; 如果其中一個鏈表已經(jīng)遍歷完,將另一個鏈表剩余的節(jié)點插入到新鏈表的末尾; 返回新鏈表。 示例代碼如下: // 向鏈表的末尾添加一個元素` `void addNode(Node* head, int val) {` `Node* newNode = new Node(val);` `Node* cur = head;` `while (cur->next != NULL) {` `cur = cur->next;` `}` `cur->next = newNode;` `}` `// 從鏈表尾部到頭部打印節(jié)點信息` `void printListReverse(Node* head) {` `stack Node* cur = head;` `while (cur != NULL) {` `s.push(cur);` `cur = cur->next;` `}` `while (!s.empty()) {` `Node* node = s.top();` `s.pop();` `cout << node->val << " ";` `}` `}` `// 合并兩個有序鏈表` `Node* mergeList(Node* head1, Node* head2) {` `Node* dummy = new Node(-1);` `Node* cur = dummy;` `while (head1 != NULL && head2 != NULL) {` `if (head1->val < head2->val) {` `cur->next = head1;` `head1 = head1->next;` `} else {` `cur->next = head2;` `head2 = head2->next;` `}` `cur = cur->next;` `}` `if (head1 != NULL) {` `cur->next = head1;` `}` `if (head2 != NULL) {` `cur->next = head2;` `}` `return dummy->next;` `}` 141、如何反轉(zhuǎn)鏈表?判斷鏈表是否是回文鏈表?如何判斷鏈表相交? 反轉(zhuǎn)鏈表的步驟如下: 新建一個空鏈表,作為反轉(zhuǎn)后的鏈表;遍歷原鏈表,將每個節(jié)點插入到新鏈表的頭部,即使新節(jié)點成為新鏈表的頭節(jié)點;返回新鏈表。 示例代碼如下: Node* reverseList(Node* head) { Node* newHead = NULL; Node* cur = head; while (cur != NULL) { Node* next = cur->next; cur->next = newHead; newHead = cur; cur = next; } return newHead; } 2.判斷鏈表是否是回文鏈表的步驟如下: 使用快慢指針法,找到鏈表的中間節(jié)點;將鏈表的后半部分反轉(zhuǎn);比較前半部分和后半部分是否相同;將后半部分反轉(zhuǎn)回來;返回比較結(jié)果。 示例代碼如下: bool isPalindrome(Node* head) { if (head == NULL || head->next == NULL) { return true; } Node* slow = head; Node* fast = head; while (fast != NULL && fast->next != NULL) { slow = slow->next; fast = fast->next->next; } Node* newHead = reverseList(slow); Node* cur1 = head; Node* cur2 = newHead; bool res = true; while (cur1 != NULL && cur2 != NULL) { if (cur1->val != cur2->val) { res = false; break; } cur1 = cur1->next; cur2 = cur2->next; } reverseList(newHead); return res; } 3.判斷鏈表相交的步驟如下: 遍歷兩個鏈表,分別得到它們的長度len1和len2;讓較長的鏈表從頭開始走len1-len2步,使得兩個鏈表到達(dá)相同的位置;同時遍歷兩個鏈表,找到第一個相同的節(jié)點,即為相交節(jié)點;如果兩個鏈表沒有相交,則返回NULL;返回相交節(jié)點。 示例代碼如下: Node* getIntersectionNode(Node* headA, Node* headB) { int len1 = 0, len2 = 0; Node* cur1 = headA; Node* cur2 = headB; while (cur1 != NULL) { len1++; cur1 = cur1->next; } while (cur2 != NULL) { len2++; cur2 = cur2->next; } cur1 = headA; cur2 = headB; int diff = abs(len1 - len2); if (len1 > len2) { while (diff-- > 0) { cur1 = cur1->next; } } else { while (diff-- > 0) { cur2 = cur2->next; } } while (cur1 != NULL && cur2 != NULL) { if (cur1 == cur2) { return cur1; } cur1 = cur1->next; cur2 = cur2->next; } return NULL; } 142、假設(shè)現(xiàn)有n個有序數(shù)組,如何合并成一個有序數(shù)組? 假設(shè)有n個有序數(shù)組A1、A2、…、An,每個數(shù)組的長度分別為L1、L2、…、Ln,合并成一個有序數(shù)組的步驟如下: 從每個數(shù)組中取出第一個元素,將它們放入一個最小堆中; 取出堆頂元素(即當(dāng)前n個數(shù)組中最小的元素),將它放入結(jié)果數(shù)組中; 如果該元素來自某個數(shù)組的末尾,則不再將它的下一個元素加入堆中; 如果堆為空,則說明所有元素都已經(jīng)被取出,合并結(jié)束。 時間復(fù)雜度為O(knlogn),其中k為數(shù)組中元素的平均數(shù)量。 示例代碼如下: vector vector int n = arrays.size(); if (n == 0) { return res; } priority_queue for (int i = 0; i < n; i++) { if (arrays[i].size() > 0) { pq.push({arrays[i][0], i}); } } while (!pq.empty()) { auto p = pq.top(); pq.pop(); int val = p.first; int i = p.second; res.push_back(val); if (arrays[i].size() > 1) { pq.push({arrays[i][1], i}); } arrays[i].erase(arrays[i].begin()); } return res; } 143、棧和隊列、字符串、樹、遞歸、AVL樹、紅黑樹、哈弗曼編碼、B+樹、map/unordered map、動態(tài)規(guī)劃。 棧和隊列、字符串、樹、遞歸、AVL樹、紅黑樹、哈夫曼編碼、B+樹、map/unordered map、動態(tài)規(guī)劃是常見的數(shù)據(jù)結(jié)構(gòu)或算法。 棧和隊列:棧和隊列是線性數(shù)據(jù)結(jié)構(gòu),棧是后進先出,隊列是先進先出。 字符串:字符串是由字符組成的序列,常用于文本處理、模式匹配等。 樹:樹是非線性數(shù)據(jù)結(jié)構(gòu),由節(jié)點和邊組成,每個節(jié)點可以有多個子節(jié)點。 遞歸:遞歸是一種算法或編程技巧,通過函數(shù)自身調(diào)用實現(xiàn)問題的分解和求解。 AVL樹:AVL樹是自平衡二叉搜索樹,保證任意節(jié)點的左右子樹高度差不超過1。 紅黑樹:紅黑樹也是自平衡二叉搜索樹,通過變色和旋轉(zhuǎn)操作來保持平衡。 哈夫曼編碼:哈夫曼編碼是一種將字符編碼為二進制的算法,通過構(gòu)建哈夫曼樹來實現(xiàn)。 B+樹:B+樹是一種多路搜索樹,常用于數(shù)據(jù)庫索引等場景。 map/unordered map:map和unordered map是鍵值對的容器,支持O(1)的查找和O(logn)的插入、刪除等操作。 動態(tài)規(guī)劃:動態(tài)規(guī)劃是一種求解最優(yōu)化問題的算法,通過將大問題分解為小問題求解。 144、什么時候產(chǎn)生默認(rèn)拷貝構(gòu)造函數(shù)?什么是深拷貝?什么是淺拷貝? 默認(rèn)拷貝構(gòu)造函數(shù)會在以下情況下自動生成: 如果沒有定義自己的拷貝構(gòu)造函數(shù),且類的成員變量都是可拷貝的,則編譯器會自動生成默認(rèn)的拷貝構(gòu)造函數(shù); 如果定義了拷貝構(gòu)造函數(shù),但沒有實現(xiàn)任何操作,則編譯器也會自動生成默認(rèn)的拷貝構(gòu)造函數(shù)。 深拷貝和淺拷貝是指在拷貝對象時,是否會將對象的動態(tài)內(nèi)存也復(fù)制一份。 深拷貝是指在拷貝對象時,會將對象的動態(tài)內(nèi)存也復(fù)制一份,每個對象都有自己的一份動態(tài)內(nèi)存,互不干擾。 淺拷貝是指在拷貝對象時,只是將對象的指針或引用復(fù)制一份,兩個指針指向同一個動態(tài)內(nèi)存,修改其中一個對象的動態(tài)內(nèi)存會影響到另一個對象。 在使用動態(tài)內(nèi)存分配時,如果不進行深拷貝,可能會導(dǎo)致多個對象共享同一塊動態(tài)內(nèi)存,當(dāng)一個對象釋放動態(tài)內(nèi)存時,其他對象也會受到影響,可能會導(dǎo)致程序崩潰或數(shù)據(jù)錯誤。因此,在使用動態(tài)內(nèi)存分配時,通常需要進行深拷貝。 145、索引為什么要使用B+樹而不是:二叉樹或者B樹? 索引是數(shù)據(jù)庫中常用的數(shù)據(jù)結(jié)構(gòu),用于加速數(shù)據(jù)的查詢和檢索。B+樹相對于二叉樹和B樹的優(yōu)勢在于其更適合于磁盤存儲的特點,主要表現(xiàn)在以下幾個方面: 減少磁盤I/O開銷:B+樹的每個節(jié)點可以存儲更多的關(guān)鍵字,因此可以減少磁盤I/O操作的次數(shù)。相比較而言,B樹每個節(jié)點的關(guān)鍵字?jǐn)?shù)目較少,因此需要更多的I/O操作,而二叉樹的高度也可能很大,導(dǎo)致I/O次數(shù)增多。 便于范圍查詢:B+樹的葉子節(jié)點形成了一個有序鏈表,可以方便地進行范圍查詢。相比較而言,B樹和二叉樹需要在非葉子節(jié)點進行回溯才能找到所有滿足條件的記錄,這會增加額外的開銷。 便于掃描操作:B+樹的葉子節(jié)點形成了一個有序鏈表,可以方便地進行順序掃描,以及分頁查詢等操作。相比較而言,B樹和二叉樹需要在非葉子節(jié)點進行回溯才能找到下一個節(jié)點,這會增加額外的開銷。 因此,在需要進行大量磁盤I/O操作的場景下,B+樹是更加合適的選擇。而在內(nèi)存中的數(shù)據(jù)結(jié)構(gòu),比如紅黑樹和哈希表等,由于磁盤I/O操作的次數(shù)較少,因此使用B+樹并不會帶來很大的優(yōu)勢。 146、SQL流入原理?如何避免SQL注入? SQL注入是一種常見的Web攻擊方式,攻擊者通過在輸入框中注入特定的SQL語句,從而獲取敏感數(shù)據(jù)或者控制數(shù)據(jù)庫。為了避免SQL注入,需要采取一些措施,包括以下幾個方面: 使用參數(shù)化查詢:參數(shù)化查詢可以避免將用戶輸入的數(shù)據(jù)直接拼接到SQL語句中,從而避免SQL注入。參數(shù)化查詢可以將用戶輸入的數(shù)據(jù)作為參數(shù)傳遞給SQL語句,而不是直接拼接到SQL語句中。這樣,即使用戶輸入了惡意的SQL語句,也不會對數(shù)據(jù)庫造成影響。 對用戶輸入進行過濾和驗證:對用戶輸入進行驗證和過濾可以避免非法字符和SQL關(guān)鍵字的注入。例如,可以使用正則表達(dá)式或者特定的函數(shù)對用戶輸入進行過濾,從而確保輸入的數(shù)據(jù)符合預(yù)期的格式。 最小化數(shù)據(jù)庫權(quán)限:為了最小化數(shù)據(jù)庫被攻擊的風(fēng)險,應(yīng)該為數(shù)據(jù)庫分配最小的權(quán)限。例如,只授權(quán)給應(yīng)用程序需要的最小權(quán)限,避免將管理員權(quán)限授予普通用戶。 使用防火墻和安全軟件:使用防火墻和安全軟件可以檢測和攔截惡意的SQL語句,從而保護數(shù)據(jù)庫的安全。 SQL流入是指將SQL注入攻擊的嘗試記錄下來,分析攻擊的方式和來源,從而采取相應(yīng)的措施。SQL流入可以通過日志分析軟件或者數(shù)據(jù)庫自身的審計功能來實現(xiàn)。通過SQL流入,可以及時發(fā)現(xiàn)SQL注入攻擊,采取相應(yīng)的措施保護數(shù)據(jù)庫的安全。 147、MySQL死鎖問題產(chǎn)生原因及如何解決? MySQL死鎖是指兩個或多個事務(wù)相互等待對方釋放鎖,從而導(dǎo)致事務(wù)無法繼續(xù)執(zhí)行的情況。產(chǎn)生死鎖的原因主要有以下幾個方面: 并發(fā)訪問:多個事務(wù)同時訪問同一個資源(例如表、行、頁等),并且訪問方式不同(例如讀、寫等),從而導(dǎo)致鎖的沖突。 鎖的順序:多個事務(wù)請求鎖的順序不同,從而導(dǎo)致死鎖的產(chǎn)生。 鎖的粒度:鎖的粒度太細(xì)或者太大,都可能導(dǎo)致死鎖的產(chǎn)生。 為了解決MySQL死鎖問題,可以采取以下幾個措施: 優(yōu)化SQL語句:通過優(yōu)化SQL語句,減少事務(wù)的數(shù)量和時間,從而減少死鎖的發(fā)生概率。 調(diào)整鎖的粒度:通過調(diào)整鎖的粒度,使得鎖的數(shù)量和范圍都適當(dāng),從而減少死鎖的發(fā)生概率。 調(diào)整事務(wù)隔離級別:通過調(diào)整事務(wù)隔離級別,使得事務(wù)的訪問方式和范圍適當(dāng),并且避免兩個事務(wù)同時對同一個資源進行修改。 監(jiān)控死鎖:通過監(jiān)控死鎖,及時發(fā)現(xiàn)死鎖的產(chǎn)生,并采取相應(yīng)的措施解決死鎖問題。 加鎖順序:使用相同的加鎖順序,從而避免死鎖問題的發(fā)生。 限制事務(wù)等待時間:通過限制事務(wù)等待時間,使得事務(wù)在等待一定時間后自動回滾,從而避免死鎖的長時間占用資源。 通過以上措施,可以有效地解決MySQL死鎖問題,提高數(shù)據(jù)庫的性能和穩(wěn)定性。 148、TCP三次握手的過程/為什么不可以兩次握手? TCP三次握手是TCP協(xié)議建立可靠連接的過程,其過程如下: 客戶端發(fā)送SYN包給服務(wù)器,表示請求建立連接。 服務(wù)器收到客戶端的SYN包后,發(fā)送ACK包給客戶端,表示確認(rèn)請求,并發(fā)送自己的SYN包給客戶端。 客戶端收到服務(wù)器的ACK包和SYN包后,發(fā)送ACK包給服務(wù)器,表示確認(rèn)連接建立。 因為TCP協(xié)議是面向連接的協(xié)議,需要在建立連接之后才能進行數(shù)據(jù)傳輸。三次握手的過程可以確保建立連接的可靠性,防止因為網(wǎng)絡(luò)延遲或者丟包導(dǎo)致連接建立失敗。如果只進行兩次握手,就不能確定對方已經(jīng)接收到自己的SYN包,也就不能確定是否建立連接。例如,如果客戶端發(fā)送SYN包后,服務(wù)器沒有收到,那么客戶端會一直等待ACK包,而服務(wù)器會認(rèn)為連接已經(jīng)建立。這樣就會導(dǎo)致數(shù)據(jù)的不可靠性和安全性問題。因此,TCP協(xié)議需要進行三次握手,以確保連接的可靠性。 149、TCP四次揮手的過程? TCP是如何保證可靠性? TCP四次揮手是TCP協(xié)議結(jié)束連接的過程,其過程如下: 客戶端發(fā)送FIN包給服務(wù)器,表示請求關(guān)閉連接。 服務(wù)器收到客戶端的FIN包后,發(fā)送ACK包給客戶端,表示確認(rèn)關(guān)閉請求,并且告訴客戶端可以關(guān)閉連接了。 服務(wù)器發(fā)送自己的FIN包給客戶端,表示請求關(guān)閉連接。 客戶端收到服務(wù)器的FIN包后,發(fā)送ACK包給服務(wù)器,表示確認(rèn)關(guān)閉請求,并且告訴服務(wù)器可以關(guān)閉連接了。 TCP協(xié)議通過以下幾個機制來保證可靠性: 序列號和確認(rèn)應(yīng)答:TCP協(xié)議在傳輸數(shù)據(jù)時,會為每一個數(shù)據(jù)包設(shè)置一個序列號,接收方收到數(shù)據(jù)包后,需要發(fā)送一個確認(rèn)應(yīng)答包,告訴發(fā)送方收到了哪些數(shù)據(jù)包。如果發(fā)送方?jīng)]有收到確認(rèn)應(yīng)答包,就會重新發(fā)送數(shù)據(jù)包,直到接收方確認(rèn)收到為止。 超時重傳:如果發(fā)送方發(fā)送了一個數(shù)據(jù)包,但是沒有收到確認(rèn)應(yīng)答包,就會重新發(fā)送數(shù)據(jù)包,直到收到確認(rèn)應(yīng)答或者達(dá)到重傳次數(shù)上限為止。 滑動窗口:TCP協(xié)議中,發(fā)送方和接收方都有一個滑動窗口,用來控制數(shù)據(jù)流的傳輸。發(fā)送方根據(jù)接收方的確認(rèn)應(yīng)答,動態(tài)調(diào)整窗口大小,控制數(shù)據(jù)的發(fā)送速度。接收方根據(jù)需要,動態(tài)調(diào)整窗口大小,控制數(shù)據(jù)的接收速度。 流量控制:TCP協(xié)議中,通過滑動窗口來控制數(shù)據(jù)流的傳輸,可以防止發(fā)送方發(fā)送過多的數(shù)據(jù),導(dǎo)致接收方無法處理。 擁塞控制:TCP協(xié)議中,通過動態(tài)調(diào)整窗口大小和發(fā)送速度,來控制網(wǎng)絡(luò)擁塞的發(fā)生,保證網(wǎng)絡(luò)的穩(wěn)定性和可靠性。 通過以上機制,TCP協(xié)議可以保證數(shù)據(jù)的可靠傳輸,同時也可以保證網(wǎng)絡(luò)的穩(wěn)定性和可靠性。 150、什么是連接半打開,頭關(guān)閉狀態(tài)? 連接半打開狀態(tài)(TCP SYN_SENT狀態(tài))是指TCP連接建立過程中,客戶端發(fā)送SYN包給服務(wù)器,但是服務(wù)器還沒有發(fā)送ACK包進行確認(rèn)的狀態(tài)。在這個狀態(tài)下,客戶端等待服務(wù)器的確認(rèn),如果服務(wù)器沒有響應(yīng),則客戶端會發(fā)送多個SYN包,直到建立連接或者達(dá)到重試次數(shù)上限為止。 頭關(guān)閉狀態(tài)(TCP FIN_WAIT_1和FIN_WAIT_2狀態(tài))是指TCP連接關(guān)閉過程中,發(fā)送方(可以是客戶端也可以是服務(wù)器)發(fā)送FIN包給接收方,請求關(guān)閉連接之后,等待接收方的ACK包的狀態(tài)。在FIN_WAIT_1狀態(tài)下,發(fā)送方等待接收方的ACK包,如果接收方?jīng)]有回復(fù),則發(fā)送方會重新發(fā)送FIN包;在FIN_WAIT_2狀態(tài)下,發(fā)送方等待接收方的FIN包,如果接收方?jīng)]有發(fā)送FIN包,則發(fā)送方會一直等待直到超時。 在連接半打開狀態(tài)和頭關(guān)閉狀態(tài)下,TCP連接可能會出現(xiàn)異常情況,例如網(wǎng)絡(luò)延遲、丟包等,導(dǎo)致連接無法正常建立或關(guān)閉。因此,在使用TCP協(xié)議時,需要注意處理連接半打開狀態(tài)和頭關(guān)閉狀態(tài),以確保連接的可靠性和穩(wěn)定性。 151、Qt信號槽機制的優(yōu)點及缺點? Qt信號槽機制是一種事件驅(qū)動的編程模型,它的優(yōu)點和缺點如下: 優(yōu)點: 松耦合:信號槽機制可以實現(xiàn)組件之間的松耦合,組件之間不需要直接相互調(diào)用,只需要通過信號和槽進行通信即可,這樣可以降低組件之間的耦合度,提高代碼的復(fù)用性和可維護性。 靈活性:信號槽機制可以實現(xiàn)非常靈活的事件處理,可以動態(tài)的連接和斷開信號和槽,可以在運行時動態(tài)修改信號和槽的參數(shù)等。 易于擴展:信號槽機制可以非常容易地擴展新的事件和處理邏輯,只需要定義新的信號和槽即可,無需修改原有代碼。 跨線程:信號槽機制可以支持跨線程的事件處理,可以將信號和槽連接在不同的線程中,這樣可以實現(xiàn)線程之間的通信。 缺點: 性能:信號槽機制的性能相對于直接調(diào)用函數(shù)來說會有一定的開銷,因為它需要進行信號的發(fā)射和槽的執(zhí)行,而且還需要維護信號槽的連接關(guān)系。 調(diào)試:由于信號槽機制是基于事件驅(qū)動的編程模型,因此調(diào)試起來可能會比較困難,特別是在信號和槽之間存在多層嵌套的情況下。 安全性:由于信號槽機制可以動態(tài)連接和斷開信號和槽,因此在使用時需要注意安全性問題,避免出現(xiàn)槽函數(shù)被誤調(diào)用的情況。 總的來說,Qt信號槽機制是一種非常靈活和方便的事件驅(qū)動編程模型,它的優(yōu)點在于松耦合、靈活性、易于擴展和跨線程等方面,但是在性能、調(diào)試和安全性等方面需要注意一些問題。 152、Qt如何實現(xiàn)自定義按鈕,使其在光標(biāo)進入、按下、離開三種狀態(tài)下顯示不同的圖片? Qt可以通過派生QPushButton類并重載其鼠標(biāo)事件函數(shù)來實現(xiàn)自定義按鈕,具體步驟如下: 定義一個新的類,繼承自QPushButton。在類的構(gòu)造函數(shù)中設(shè)置按鈕的三種狀態(tài)下的圖片。重載鼠標(biāo)進入事件(QEvent::Enter)、鼠標(biāo)按下事件(QEvent::MouseButtonPress)和鼠標(biāo)離開事件(QEvent::Leave)函數(shù),分別在這三個事件中設(shè)置按鈕的狀態(tài)圖片。 示例代碼: class MyButton : public QPushButton { public: MyButton(QWidget *parent = nullptr) : QPushButton(parent) { // 設(shè)置按鈕的三種狀態(tài)圖片 setStyleSheet("QPushButton {border-image: url(normal.png);}" "QPushButton:hover {border-image: url(hover.png);}" "QPushButton:pressed {border-image: url(pressed.png);}"); } protected: void enterEvent(QEvent *event) override { // 鼠標(biāo)進入事件,設(shè)置按鈕狀態(tài)為hover QPushButton::enterEvent(event); setStyleSheet("QPushButton {border-image: url(normal.png);}" "QPushButton:hover {border-image: url(hover.png);}" "QPushButton:pressed {border-image: url(pressed.png);}"); } void mousePressEvent(QMouseEvent *event) override { // 鼠標(biāo)按下事件,設(shè)置按鈕狀態(tài)為pressed QPushButton::mousePressEvent(event); setStyleSheet("QPushButton {border-image: url(normal.png);}" "QPushButton:hover {border-image: url(hover.png);}" "QPushButton:pressed {border-image: url(pressed.png);}"); } void leaveEvent(QEvent *event) override { // 鼠標(biāo)離開事件,設(shè)置按鈕狀態(tài)為normal QPushButton::leaveEvent(event); setStyleSheet("QPushButton {border-image: url(normal.png);}" "QPushButton:hover {border-image: url(hover.png);}" "QPushButton:pressed {border-image: url(pressed.png);}"); } }; 在這個示例代碼中,我們派生了一個新的類MyButton,重載了它的鼠標(biāo)進入、按下和離開事件函數(shù),根據(jù)不同的事件類型,設(shè)置按鈕的狀態(tài)圖片。當(dāng)鼠標(biāo)進入按鈕區(qū)域時,按鈕的樣式會變成hover狀態(tài),當(dāng)鼠標(biāo)按下按鈕時,按鈕的樣式會變成pressed狀態(tài),當(dāng)鼠標(biāo)離開按鈕時,按鈕的樣式會變成normal狀態(tài)。 153、Qt信號和槽本質(zhì)? Qt中的信號和槽是一種事件驅(qū)動的機制,用于在對象之間傳遞消息和實現(xiàn)對象間的通信。信號和槽的本質(zhì)可以解釋為以下兩個方面: 信號和槽本質(zhì)是函數(shù): 在Qt中,信號和槽本質(zhì)上是函數(shù),信號是一種特殊的函數(shù),它不包含函數(shù)體,只有函數(shù)聲明和參數(shù)列表,用于向外部發(fā)出某種事件的通知。槽也是一種函數(shù),它是一個普通的成員函數(shù),用于處理信號發(fā)出的事件。在信號和槽連接時,實際上是將信號函數(shù)和槽函數(shù)通過一個中介對象(連接器)連接起來,使得信號函數(shù)能夠調(diào)用槽函數(shù),從而實現(xiàn)對象間的通信。 信號和槽本質(zhì)是元對象系統(tǒng)的一部分: 在Qt中,信號和槽是元對象系統(tǒng)的一部分,元對象系統(tǒng)是一個用于支持Qt元編程的框架,它允許在運行時動態(tài)地查詢和操作對象的元數(shù)據(jù),包括類名、屬性、方法等。信號和槽的實現(xiàn)依賴元對象系統(tǒng)的元數(shù)據(jù),每個QObject對象在創(chuàng)建時都會生成一個元對象,其中包含了該對象的所有信息,包括信號和槽的聲明和實現(xiàn)。當(dāng)信號和槽連接時,會根據(jù)元數(shù)據(jù)進行檢查和匹配,保證信號和槽的正確連接和調(diào)用。 綜上所述,信號和槽本質(zhì)上是函數(shù),它們通過元對象系統(tǒng)實現(xiàn)對象間的通信,使得對象之間能夠相互響應(yīng)和交互,是Qt中非常重要的一種機制。 154、Qt當(dāng)中的數(shù)據(jù)流(QDataStream) 和文件流(QTextStream) 有何區(qū)別? Qt中的數(shù)據(jù)流(QDataStream)和文件流(QTextStream)都是用于讀寫數(shù)據(jù)的流類,但它們有以下區(qū)別: 數(shù)據(jù)類型:數(shù)據(jù)流(QDataStream)支持Qt中的所有基本數(shù)據(jù)類型和自定義類型,如QString、QByteArray等,而文件流(QTextStream)只能讀寫文本數(shù)據(jù),不支持二進制數(shù)據(jù)。 數(shù)據(jù)格式:數(shù)據(jù)流(QDataStream)是二進制格式,可以直接讀寫二進制數(shù)據(jù),而文件流(QTextStream)是文本格式,只能讀寫文本數(shù)據(jù),對于二進制數(shù)據(jù)需要進行編碼和解碼。 數(shù)據(jù)編碼:文件流(QTextStream)默認(rèn)使用Unicode編碼,可以通過設(shè)置不同的編碼格式來讀寫不同的文本數(shù)據(jù),而數(shù)據(jù)流(QDataStream)不需要進行編碼,它直接以二進制形式讀寫數(shù)據(jù)。 應(yīng)用場景:數(shù)據(jù)流(QDataStream)適用于讀寫二進制數(shù)據(jù),例如讀寫文件、網(wǎng)絡(luò)傳輸?shù)葓鼍?,而文件?QTextStream)適用于讀寫文本數(shù)據(jù),例如讀寫配置文件、日志文件等場景。 綜上所述,數(shù)據(jù)流(QDataStream)和文件流(QTextStream)雖然都是Qt中的流類,但它們的數(shù)據(jù)類型、格式、編碼和應(yīng)用場景等方面有所不同,需要根據(jù)具體的需求選擇合適的流類進行讀寫操作。 155、Qt 網(wǎng)絡(luò)通信中,TCP./UDP 整體流程(服務(wù)器,客戶端) ? 在Qt中,TCP/UDP網(wǎng)絡(luò)通信的整體流程主要包括以下步驟: 服務(wù)器端的創(chuàng)建和監(jiān)聽 服務(wù)器端首先需要創(chuàng)建一個QTcpServer或QUdpSocket對象,用于監(jiān)聽客戶端的連接或接收數(shù)據(jù)。創(chuàng)建時需要指定端口號和IP地址(如果有多個網(wǎng)卡,則需要指定監(jiān)聽的網(wǎng)卡地址)。然后調(diào)用listen()函數(shù)開始監(jiān)聽客戶端的連接或數(shù)據(jù)包的到來。 客戶端的連接或數(shù)據(jù)發(fā)送 客戶端需要創(chuàng)建一個QTcpSocket或QUdpSocket對象,用于連接服務(wù)器或發(fā)送數(shù)據(jù)包。對于TCP通信,客戶端需要調(diào)用connectToHost()函數(shù)連接服務(wù)器;對于UDP通信,客戶端可以直接使用writeDatagram()函數(shù)發(fā)送數(shù)據(jù)包。 服務(wù)器端的響應(yīng)和數(shù)據(jù)處理 當(dāng)客戶端連接到服務(wù)器或發(fā)送數(shù)據(jù)包時,服務(wù)器端會觸發(fā)相應(yīng)的信號(如newConnection()、readyRead()等),在相應(yīng)的槽函數(shù)中進行響應(yīng)和數(shù)據(jù)處理。對于TCP通信,服務(wù)器端需要在newConnection()槽函數(shù)中調(diào)用nextPendingConnection()函數(shù)獲取客戶端的QTcpSocket對象,然后在客戶端的QTcpSocket對象上調(diào)用read()函數(shù)讀取數(shù)據(jù);對于UDP通信,服務(wù)器端可以直接在readyRead()槽函數(shù)中調(diào)用readDatagram()函數(shù)讀取數(shù)據(jù)包。 客戶端的數(shù)據(jù)接收和響應(yīng) 當(dāng)客戶端連接到服務(wù)器或發(fā)送數(shù)據(jù)包時,客戶端會觸發(fā)相應(yīng)的信號(如connected()、readyRead()等),在相應(yīng)的槽函數(shù)中進行數(shù)據(jù)接收和響應(yīng)。對于TCP通信,客戶端需要在connected()槽函數(shù)中調(diào)用write()函數(shù)發(fā)送數(shù)據(jù),然后在readyRead()槽函數(shù)中調(diào)用read()函數(shù)讀取服務(wù)器端的響應(yīng)數(shù)據(jù);對于UDP通信,客戶端可以直接在readyRead()槽函數(shù)中調(diào)用readDatagram()函數(shù)讀取服務(wù)器端的響應(yīng)數(shù)據(jù)包。 斷開連接和清理資源 當(dāng)通信完成或出現(xiàn)錯誤時,需要斷開連接并清理資源。對于TCP通信,可以在客戶端或服務(wù)器端的QTcpSocket對象上調(diào)用disconnectFromHost()函數(shù)或close()函數(shù)斷開連接,并在socketDisconnected()槽函數(shù)中清理資源;對于UDP通信,不需要顯式地斷開連接,系統(tǒng)會自動管理資源。 綜上所述,TCP/UDP網(wǎng)絡(luò)通信的整體流程包括服務(wù)器端的創(chuàng)建和監(jiān)聽、客戶端的連接或數(shù)據(jù)發(fā)送、服務(wù)器端的響應(yīng)和數(shù)據(jù)處理、客戶端的數(shù)據(jù)接收和響應(yīng),以及斷開連接和清理資源。根據(jù)具體的需求,可以選擇使用QTcpServer、QTcpSocket、QUdpSocket等類進行開發(fā)。 156、Qt編程當(dāng)中,多線程的兩種使用方法? 在Qt編程中,多線程的兩種使用方法分別是: 繼承QThread類 繼承QThread類是一種常見的多線程編程方法。該方法需要定義一個新類,繼承自QThread類,并重寫run()函數(shù),run()函數(shù)中包含了需要在新線程中執(zhí)行的代碼。在主線程中創(chuàng)建該類的實例對象,調(diào)用start()函數(shù)啟動新線程。 示例代碼: class MyThread : public QThread { Q_OBJECT public: void run() override { // 在新線程中執(zhí)行的代碼 } }; MyThread thread; thread.start(); 2.繼承QObject類,使用QThread對象 繼承QObject類,使用QThread對象是另一種常見的多線程編程方法。該方法需要定義一個新類,繼承自QObject類,并在該類中定義需要在新線程中執(zhí)行的槽函數(shù)。在主線程中創(chuàng)建QThread對象,將新類的實例對象移動到該QThread對象所在的線程中,然后啟動該QThread對象。 示例代碼: class MyObject : public QObject { Q_OBJECT public slots: void doWork() { // 在新線程中執(zhí)行的代碼 } }; QThread thread; MyObject *obj = new MyObject(); obj->moveToThread(&thread); QObject::connect(&thread, &QThread::started, obj, &MyObject::doWork); thread.start(); 以上兩種方法都可以實現(xiàn)多線程編程,選擇哪種方法取決于具體的需求和編程風(fēng)格。需要注意的是,在多線程編程中,需要注意線程間的同步和互斥問題,避免出現(xiàn)競態(tài)條件和死鎖等問題??梢允褂肣t提供的線程同步機制(如QMutex、QWaitCondition等)或標(biāo)準(zhǔn)的C++11線程庫(如std::mutex、std::condition_variable等)進行處理。 157、創(chuàng)建signal類? QVariant 應(yīng)用? 在Qt中,創(chuàng)建signal類可以通過繼承QObject類并使用signals關(guān)鍵字來聲明信號。一個signal類通常包含一個或多個信號,每個信號都由一個信號名稱和一個信號參數(shù)列表組成,其中信號參數(shù)列表可以為空。示例代碼如下: class MySignalClass : public QObject { Q_OBJECT public: MySignalClass(QObject *parent = nullptr) : QObject(parent) {} signals: void mySignal(int value); void anotherSignal(QString text, int count); }; 在上面的代碼中,MySignalClass類繼承自QObject類,并使用signals關(guān)鍵字聲明了兩個信號,分別是mySignal和anotherSignal,其中mySignal信號有一個int類型的參數(shù),anotherSignal信號有一個QString類型和一個int類型的參數(shù)。 QVariant是Qt中一個用于封裝任意類型數(shù)據(jù)的類,它可以用于在不同類型之間進行數(shù)據(jù)轉(zhuǎn)換??梢允褂肣Variant::fromXXX()函數(shù)將不同類型的數(shù)據(jù)轉(zhuǎn)換為QVariant類型,如QVariant::fromInt()、QVariant::fromString()等;可以使用QVariant::toXXX()函數(shù)將QVariant類型的數(shù)據(jù)轉(zhuǎn)換為其他類型,如QVariant::toInt()、QVariant::toString()等。示例代碼如下: QVariant var = QVariant::fromInt(123); // 將int類型的數(shù)據(jù)轉(zhuǎn)換為QVariant類型 int value = var.toInt(); // 將QVariant類型的數(shù)據(jù)轉(zhuǎn)換為int類型 在上面的代碼中,QVariant::fromInt()函數(shù)將int類型的數(shù)據(jù)轉(zhuǎn)換為QVariant類型,QVariant::toInt()函數(shù)將QVariant類型的數(shù)據(jù)轉(zhuǎn)換為int類型。需要注意的是,QVariant類型的數(shù)據(jù)轉(zhuǎn)換可能會存在精度損失和類型錯誤等問題,需要根據(jù)實際情況進行處理。 158、Qt中的指針: QPointer、QScopedPointer、 QSharedPointer、QWeakPointer、std::weak_ ptr、QSharedDataPointer? 在Qt中,指針是一個很重要的概念,Qt提供了多種指針類來幫助我們管理內(nèi)存和避免內(nèi)存泄漏。下面是對常見的指針類進行簡要介紹: QPointer QPointer是Qt中的智能指針,它可以自動管理指針的生命周期,并且可以檢測被管理的對象是否已經(jīng)被銷毀。如果被管理的對象已經(jīng)被銷毀,QPointer會自動將指針置為nullptr。QPointer通常用于管理QWidget對象等需要在程序運行期間動態(tài)創(chuàng)建和銷毀的對象。 QScopedPointer QScopedPointer是Qt中的局部智能指針,它可以自動管理指針的生命周期,并且可以在指針超出作用域時自動釋放指針指向的內(nèi)存。QScopedPointer通常用于管理局部變量和堆內(nèi)存對象等需要在作用域結(jié)束時自動釋放的對象。 QSharedPointer QSharedPointer是Qt中的共享智能指針,它可以在多個指針之間共享同一個對象,并且可以自動管理指針的生命周期。當(dāng)最后一個指向?qū)ο蟮腝SharedPointer被銷毀時,QSharedPointer會自動釋放對象的內(nèi)存。QSharedPointer通常用于管理需要在多個地方使用同一個對象的情況。 QWeakPointer QWeakPointer是Qt中的弱智能指針,它類似于QSharedPointer,但是不會增加對象的引用計數(shù)。QWeakPointer通常用于在多個指針之間共享同一個對象時,避免產(chǎn)生循環(huán)引用問題。 std::weak_ptr std::weak_ptr是C++11標(biāo)準(zhǔn)庫中的弱智能指針,它類似于QWeakPointer,但是不是Qt所提供的。std::weak_ptr可以用于在多個指針之間共享同一個對象時,避免產(chǎn)生循環(huán)引用問題。 QSharedDataPointer QSharedDataPointer是Qt中的共享數(shù)據(jù)指針,它可以在多個對象之間共享同一個數(shù)據(jù),而不需要共享整個對象。QSharedDataPointer通常用于實現(xiàn)copy-on-write(COW)技術(shù),避免不必要的復(fù)制操作。 需要注意的是,不同的指針類適用于不同的場景和需求,需要根據(jù)實際情況進行選擇和使用。 159、Qt當(dāng)中的show和exec區(qū)別? 在Qt中,show和exec都是用來顯示對話框的方法,它們的主要區(qū)別在于它們的運行機制和返回值。 show方法是在當(dāng)前線程中顯示對話框并立即返回,該方法不會阻塞當(dāng)前線程,因此當(dāng)對話框顯示后,程序會繼續(xù)執(zhí)行后續(xù)代碼。如果在對話框顯示期間需要執(zhí)行一些其他操作,可以在對話框關(guān)閉事件中處理,或者使用Qt的事件循環(huán)機制(如QEventLoop)來阻塞程序執(zhí)行。 exec方法是在當(dāng)前線程中顯示對話框,并且阻塞當(dāng)前線程,直到用戶關(guān)閉對話框為止。在執(zhí)行exec方法后,程序會進入一個事件循環(huán)(QEventLoop),直到對話框關(guān)閉事件被觸發(fā)。因此,如果需要在對話框關(guān)閉之前執(zhí)行一些其他操作,需要在對話框關(guān)閉事件之前處理。 需要注意的是,show方法和exec方法都可以用來顯示對話框,但是它們的使用場景不同。show方法通常用于顯示模態(tài)對話框和非模態(tài)對話框,而exec方法通常用于顯示模態(tài)對話框。如果需要在對話框顯示期間執(zhí)行一些其他操作,建議使用show方法,如果需要等待用戶關(guān)閉對話框后再執(zhí)行其他操作,建議使用exec方法。 160、 QString與基本數(shù)據(jù)類型如何轉(zhuǎn)換? 在Qt中,QString與基本數(shù)據(jù)類型之間可以進行方便的相互轉(zhuǎn)換。下面是常見的轉(zhuǎn)換方法: 將QString轉(zhuǎn)換為基本數(shù)據(jù)類型 通過QString的各種轉(zhuǎn)換函數(shù),可以將QString轉(zhuǎn)換為int、float、double等基本數(shù)據(jù)類型。例如: QString str = "123"; int num = str.toInt(); // 將QString轉(zhuǎn)換為int類型 float f = str.toFloat(); // 將QString轉(zhuǎn)換為float類型 double d = str.toDouble(); // 將QString轉(zhuǎn)換為double類型 2.將基本數(shù)據(jù)類型轉(zhuǎn)換為QString 通過QString的靜態(tài)函數(shù),可以將int、float、double等基本數(shù)據(jù)類型轉(zhuǎn)換為QString。例如: 1. int num = 123; QString str = QString::number(num); // 將int類型轉(zhuǎn)換為QString float f = 3.14; QString str2 = QString::number(f); // 將float類型轉(zhuǎn)換為QString 需要注意的是,QString和基本數(shù)據(jù)類型之間的轉(zhuǎn)換可能會存在精度和數(shù)據(jù)溢出等問題,需要根據(jù)實際情況進行選擇和處理。 161、 QMap類/QHash類/QVectoro類作用和區(qū)別? 在Qt中,QMap類、QHash類和QVector類都是用來管理數(shù)據(jù)的容器類,它們的作用和區(qū)別如下: QMap類 QMap類是一種有序的映射容器,它可以將一個鍵值對映射到另一個鍵值對,且鍵值對是按照鍵的順序進行排序的。QMap類通常用于對一組數(shù)據(jù)進行映射操作,常見的操作包括查找、插入、刪除等。例如: QMap map.insert("Tom", 18); map.insert("Jack", 20); map.insert("Lucy", 17); int age = map.value("Tom"); // 獲取鍵為"Tom"的值 map.remove("Lucy"); // 刪除鍵為"Lucy"的鍵值對 2.QHash類 QHash類是一種無序的哈希容器,它可以將一個鍵值對映射到另一個鍵值對,且鍵值對是按照哈希值進行存儲的。QHash類通常用于對一組數(shù)據(jù)進行哈希操作,常見的操作包括查找、插入、刪除等。例如: QHash hash.insert("Tom", 18); hash.insert("Jack", 20); hash.insert("Lucy", 17); int age = hash.value("Tom"); // 獲取鍵為"Tom"的值 hash.remove("Lucy"); // 刪除鍵為"Lucy"的鍵值對 3.QVector類 QVector類是一種動態(tài)數(shù)組容器,它可以存儲任意類型的數(shù)據(jù),并且支持動態(tài)增加和刪除操作。QVector類通常用于存儲一組數(shù)據(jù),并且需要對數(shù)據(jù)進行動態(tài)操作,如排序、查找等。例如: QVector vec.append(1); vec.append(2); vec.append(3); int num = vec.at(1); // 獲取第2個元素 vec.removeLast(); // 刪除最后一個元素 需要注意的是,QMap類、QHash類和QVector類應(yīng)根據(jù)實際情況進行選擇和使用。如果需要對數(shù)據(jù)進行排序和查找操作,建議使用QMap類;如果需要對數(shù)據(jù)進行哈希操作,建議使用QHash類;如果需要對數(shù)據(jù)進行動態(tài)操作,建議使用QVector類。 162、QList 類/QLinkedList類作用? 在Qt中,QList類和QLinkedList類都是用來管理數(shù)據(jù)的容器類,它們的作用和區(qū)別如下: QList類 QList類是一種動態(tài)數(shù)組容器,它可以存儲任意類型的數(shù)據(jù),并且支持動態(tài)增加和刪除操作。QList類的內(nèi)部實現(xiàn)是一個可變大小的數(shù)組,可以動態(tài)調(diào)整數(shù)組的大小以適應(yīng)數(shù)據(jù)的存儲需求。QList類通常用于存儲一組數(shù)據(jù),并且需要對數(shù)據(jù)進行動態(tài)操作,如排序、查找等。例如: QList list.append(1); list.append(2); list.append(3); int num = list.at(1); // 獲取第2個元素 list.removeLast(); // 刪除最后一個元素 2.QLinkedList類 QLinkedList類是一種雙向鏈表容器,它可以存儲任意類型的數(shù)據(jù),并且支持動態(tài)增加和刪除操作。QLinkedList類的內(nèi)部實現(xiàn)是一個雙向鏈表,可以動態(tài)調(diào)整鏈表的大小以適應(yīng)數(shù)據(jù)的存儲需求。QLinkedList類通常用于存儲一組數(shù)據(jù),并且需要對數(shù)據(jù)進行動態(tài)操作,如排序、查找等。例如: QLinkedList list.append(1); list.append(2); list.append(3); int num = list.at(1); // 獲取第2個元素 list.removeLast(); // 刪除最后一個元素 需要注意的是,QList類和QLinkedList類應(yīng)根據(jù)實際情況進行選擇和使用。如果需要對數(shù)據(jù)進行頻繁的插入和刪除操作,建議使用QLinkedList類;如果需要對數(shù)據(jù)進行頻繁的訪問和遍歷操作,建議使用QList類。 163、請說出Qt常用8類個控件? Qt中常用的8個控件包括: QLabel(標(biāo)簽控件):用于顯示文本或圖像。QPushButton(按鈕控件):用于接收用戶的點擊事件。QLineEdit(單行編輯框控件):用于接收用戶輸入的單行文本。QTextEdit(多行編輯框控件):用于接收用戶輸入的多行文本。QComboBox(下拉框控件):用于在預(yù)定義的選項中進行選擇。QCheckBox(復(fù)選框控件):用于允許用戶選擇一個或多個選項。QRadioButton(單選按鈕控件):用于允許用戶從多個選項中選擇一個。QSpinBox/QDoubleSpinBox(數(shù)值輸入框控件):用于接收用戶輸入的數(shù)值。 這些控件是Qt中非常常用的控件,常常用于創(chuàng)建用戶界面。需要根據(jù)實際需求進行選擇和使用。 164、QLayout QStackedWidget類/QSplitter類/QDockWidget類? 在Qt中,QLayout類、QStackedWidget類、QSplitter類和QDockWidget類都是用于創(chuàng)建用戶界面的類,它們的作用如下: QLayout類 QLayout類是Qt中用于管理控件布局的類。它可以將控件按照一定的布局方式進行排列,如水平布局、垂直布局、網(wǎng)格布局等。QLayout類通常用于創(chuàng)建復(fù)雜的用戶界面,可以有效地管理控件的位置和大小。例如: QHBoxLayout *layout = new QHBoxLayout; layout->addWidget(button1); layout->addWidget(button2); layout->addWidget(button3); setLayout(layout); 2.QStackedWidget類 QStackedWidget類是一種堆棧容器控件,它可以將多個子控件按照堆棧的方式進行管理,只顯示當(dāng)前的子控件,其它子控件被隱藏。QStackedWidget類通常用于創(chuàng)建多頁應(yīng)用程序,例如多個窗口之間的切換。例如: QStackedWidget *stack = new QStackedWidget; stack->addWidget(page1); stack->addWidget(page2); stack->addWidget(page3); stack->setCurrentIndex(0); 3.QSplitter類 QSplitter類是一種分割窗口控件,它可以將窗口分割成多個部分,每個部分可以包含一個或多個子控件。QSplitter類通常用于創(chuàng)建可調(diào)整大小的用戶界面,可以讓用戶自由調(diào)整窗口的大小和位置。例如: QSplitter *splitter = new QSplitter(Qt::Horizontal); splitter->addWidget(widget1); splitter->addWidget(widget2); splitter->addWidget(widget3); 4.QDockWidget類 QDockWidget類是一種??看翱诳丶?,它可以將窗口??吭谥鞔翱诘倪吘壔蚋釉谥鞔翱谏戏?。QDockWidget類通常用于創(chuàng)建多文檔界面(MDI)應(yīng)用程序,可以讓用戶自由調(diào)整窗口的??课恢煤痛笮 @纾?/p> QDockWidget *dock = new QDockWidget; dock->setWidget(widget); dock->setWindowTitle("Dock Window"); addDockWidget(Qt::LeftDockWidgetArea, dock); 需要注意的是,QLayout類、QStackedWidget類、QSplitter類和QDockWidget類都是用于創(chuàng)建用戶界面的類,需要根據(jù)實際需求進行選擇和使用。 165、Qt當(dāng)中文件對話框、字體對話體、輸入對話框、消息對話框應(yīng)用實戰(zhàn)? 以下是四個對話框在Qt中的應(yīng)用實戰(zhàn): 文件對話框(QFileDialog) QFileDialog是Qt中用于打開和保存文件的對話框類。它可以讓用戶選擇文件的路徑和名稱,并且支持多種文件格式的過濾。以下是一個文件對話框的應(yīng)用實例: QString fileName = QFileDialog::getOpenFileName(this, "Open File", QDir::homePath(), "Text Files (*.txt)"); if(!fileName.isEmpty()) { QFile file(fileName); if(file.open(QIODevice::ReadOnly)) { // 讀取文件內(nèi)容 file.close(); } } 2.字體對話框(QFontDialog) QFontDialog是Qt中用于選擇字體的對話框類。它可以讓用戶選擇字體的名稱、大小、顏色等屬性。以下是一個字體對話框的應(yīng)用實例: bool ok; QFont font = QFontDialog::getFont(&ok, QFont("Helvetica [Cronyx]", 10), this); if(ok) { // 應(yīng)用選擇的字體 setFont(font); } 3.輸入對話框(QInputDialog) QInputDialog是Qt中用于輸入文本、數(shù)字、列表等的對話框類。它可以讓用戶輸入各種類型的數(shù)據(jù),并且支持自定義對話框標(biāo)題、提示信息等。以下是一個輸入對話框的應(yīng)用實例: QString text = QInputDialog::getText(this, "Input Dialog", "Enter your name:", QLineEdit::Normal, "", &ok); if(ok && !text.isEmpty()) { // 處理用戶輸入的文本 } 4.消息對話框(QMessageBox) QMessageBox是Qt中用于顯示消息、提示、警告等的對話框類。它可以讓用戶選擇不同的按鈕選項,并且支持自定義對話框標(biāo)題、提示信息等。以下是一個消息對話框的應(yīng)用實例: QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "Message Box", "Are you sure to quit?", QMessageBox::Yes | QMessageBox::No); if(reply == QMessageBox::Yes) { // 處理用戶選擇 } 需要注意的是,四個對話框的應(yīng)用場景不同,需要根據(jù)實際需求進行選擇和使用。 166、Qt繪制原理雙緩沖機制? Qt繪制原理中的雙緩沖機制是指在繪制過程中使用兩個緩沖區(qū),一個用于繪制,一個用于顯示,從而避免了繪制過程中的閃爍等問題。具體來說,雙緩沖機制的實現(xiàn)過程如下: 創(chuàng)建兩個緩沖區(qū),一個用于繪制,一個用于顯示;在繪制過程中,將所有的繪制操作都先繪制到繪制緩沖區(qū)中;繪制完成后,將繪制緩沖區(qū)中的內(nèi)容復(fù)制到顯示緩沖區(qū)中;顯示緩沖區(qū)中的內(nèi)容會被顯示在屏幕上。 這樣,由于繪制操作是在繪制緩沖區(qū)中進行的,因此不會直接影響到顯示緩沖區(qū)中的內(nèi)容,從而避免了閃爍等問題。 在Qt中,雙緩沖機制是由QPainter類和QWidget類共同實現(xiàn)的。QPainter類用于在繪制緩沖區(qū)中進行繪制操作,而QWidget類則在顯示緩沖區(qū)中進行顯示操作。具體來說,當(dāng)QWidget類的paintEvent()函數(shù)被觸發(fā)時,會創(chuàng)建一個QPainter對象,然后在其中調(diào)用繪制函數(shù),將所有的繪制操作都繪制到繪制緩沖區(qū)中。繪制完成后,QPainter對象會自動將繪制緩沖區(qū)中的內(nèi)容復(fù)制到顯示緩沖區(qū)中,然后顯示緩沖區(qū)中的內(nèi)容會被顯示在屏幕上。 需要注意的是,雙緩沖機制雖然可以避免閃爍等問題,但是也會增加內(nèi)存的消耗。因此,在實際應(yīng)用中需要根據(jù)具體情況進行選擇和使用。 167、Graphics View圖形視圖框架結(jié)構(gòu)? Graphics View是Qt中用于顯示和處理大量圖形元素的框架,其主要包括以下幾個重要的類和組件: QGraphicsItem和QGraphicsScene:QGraphicsItem類是Graphics View框架中所有圖形項的基類,它定義了所有圖形項的共同屬性和行為。QGraphicsScene類是Graphics View框架中的場景類,它管理著所有的圖形項,并且提供了對它們進行操作的接口。QGraphicsView:QGraphicsView類是Graphics View框架中的視圖類,它用于在屏幕上顯示QGraphicsScene中的內(nèi)容,并且提供了對場景進行縮放、平移、旋轉(zhuǎn)等操作的接口。QGraphicsWidget:QGraphicsWidget類是Graphics View框架中的窗口類,它可以作為一個圖形項添加到QGraphicsScene中,并且支持鼠標(biāo)事件、鍵盤事件等交互操作。QGraphicsProxyWidget:QGraphicsProxyWidget類是Graphics View框架中的窗口代理類,它可以將一個QWidget對象轉(zhuǎn)換為一個圖形項,然后添加到QGraphicsScene中。QGraphicsItemGroup:QGraphicsItemGroup類是Graphics View框架中的圖形項組類,它可以將多個圖形項組合成一個整體,從而方便對它們進行操作。QGraphicsEffect:QGraphicsEffect類是Graphics View框架中的特效類,它可以對QGraphicsItem進行圖形效果的處理,例如模糊、陰影、發(fā)光等。QGraphicsPixmapItem和QGraphicsTextItem:QGraphicsPixmapItem類是Graphics View框架中的圖片項類,它可以將一個QPixmap對象顯示在場景中。QGraphicsTextItem類是Graphics View框架中的文本項類,它可以將一個字符串顯示在場景中。 以上是Graphics View框架的主要組件和類,它們共同構(gòu)成了Graphics View框架的結(jié)構(gòu)。通過這些組件和類的使用,我們可以方便地實現(xiàn)各種復(fù)雜的圖形界面和圖形效果。 168、Qt當(dāng)中如何讀寫文件? 在Qt中,可以使用QFile類和QTextStream類來讀寫文件。 讀文件: QFile file("test.txt"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return; QTextStream in(&file); while (!in.atEnd()) { QString line = in.readLine(); // 處理每一行數(shù)據(jù) } file.close(); 上述代碼中,首先使用QFile類打開要讀取的文件,然后使用QTextStream類對文件進行讀取。通過while循環(huán),每次讀取一行數(shù)據(jù),然后進行處理。最后關(guān)閉文件。 寫文件: QFile file("test.txt"); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return; QTextStream out(&file); out << "Hello world" << endl; out << "Qt is awesome" << endl; file.close(); 上述代碼中,首先使用QFile類打開要寫入的文件,然后使用QTextStream類對文件進行寫入。通過向QTextStream對象中寫入數(shù)據(jù),可以將數(shù)據(jù)寫入文件中。最后關(guān)閉文件。 需要注意的是,在進行文件讀寫操作時,需要確保文件路徑正確,并且文件權(quán)限足夠。同時,需要根據(jù)具體情況使用不同的打開模式,例如只讀模式、只寫模式、追加模式等。 169、Qt中事件過濾處理方法? Qt中的事件過濾器可以用于對某個對象的事件進行攔截和處理。事件過濾器是一個QObject對象,它可以安裝到任何QObject派生類中,并且可以監(jiān)聽該對象的所有事件。當(dāng)事件發(fā)生時,事件過濾器可以攔截并處理該事件,也可以將該事件轉(zhuǎn)發(fā)給原始的事件接收者對象進行處理。 事件過濾器的處理方法如下: 創(chuàng)建一個QObject派生類,實現(xiàn)其eventFilter()函數(shù),該函數(shù)會在事件發(fā)生時被調(diào)用。 class MyEventFilter : public QObject { Q_OBJECT public: explicit MyEventFilter(QObject *parent = nullptr); protected: bool eventFilter(QObject *watched, QEvent *event) override; }; 2.在需要監(jiān)聽的對象中,調(diào)用installEventFilter()函數(shù)安裝事件過濾器。 MyEventFilter *eventFilter = new MyEventFilter; QLabel *label = new QLabel("Hello, World!"); label->installEventFilter(eventFilter); 3.在eventFilter()函數(shù)中處理事件,可以通過判斷事件類型和事件源來對事件進行不同的處理。 bool MyEventFilter::eventFilter(QObject *watched, QEvent *event) { if (watched == label && event->type() == QEvent::MouseButtonPress) { QMouseEvent *mouseEvent = static_cast // 處理鼠標(biāo)事件 return true; } return false; } 需要注意的是,在eventFilter()函數(shù)中返回true表示該事件已經(jīng)被處理,不再繼續(xù)傳遞給事件接收者對象進行處理;返回false表示該事件仍然需要傳遞給事件接收者對象進行處理。 170、Qt 操作INI文件、JSON 文件、XML文件? Qt提供了三種常用的文件格式處理方式,分別是INI文件、JSON文件和XML文件。下面分別介紹如何在Qt中操作這三種文件格式。 操作INI文件 INI文件是一種常用的配置文件格式,在Qt中可以通過QSettings類來讀寫INI文件。以下是一個例子: QSettings settings("myApp.ini", QSettings::IniFormat); // 寫入配置項 settings.setValue("General/Name", "Tom"); settings.setValue("General/Age", 20); // 讀取配置項 QString name = settings.value("General/Name").toString(); int age = settings.value("General/Age").toInt(); 2.操作JSON文件 JSON是一種輕量級的數(shù)據(jù)交換格式,在Qt中可以通過QJsonDocument類和QJsonObject類來讀寫JSON文件。以下是一個例子: // 讀取JSON文件 QFile file("data.json"); if (file.open(QIODevice::ReadOnly)) { QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error); if (error.error == QJsonParseError::NoError && doc.isObject()) { QJsonObject obj = doc.object(); QString name = obj.value("name").toString(); int age = obj.value("age").toInt(); } file.close(); } // 寫入JSON文件 QJsonObject obj; obj.insert("name", "Tom"); obj.insert("age", 20); QJsonDocument doc(obj); QFile file("data.json"); if (file.open(QIODevice::WriteOnly)) { file.write(doc.toJson()); file.close(); } 3.操作XML文件 XML是一種常用的文本格式,用于表示結(jié)構(gòu)化的數(shù)據(jù),在Qt中可以通過QXmlStreamReader類和QXmlStreamWriter類來讀寫XML文件。以下是一個例子: // 讀取XML文件 QFile file("data.xml"); if (file.open(QIODevice::ReadOnly)) { QXmlStreamReader reader(&file); while (!reader.atEnd()) { reader.readNext(); if (reader.isStartElement() && reader.name() == "person") { QString name = reader.attributes().value("name").toString(); int age = reader.attributes().value("age").toInt(); // 處理讀取到的數(shù)據(jù) } } file.close(); } // 寫入XML文件 QFile file("data.xml"); if (file.open(QIODevice::WriteOnly)) { QXmlStreamWriter writer(&file); writer.setAutoFormatting(true); writer.writeStartDocument(); writer.writeStartElement("persons"); writer.writeStartElement("person"); writer.writeAttribute("name", "Tom"); writer.writeAttribute("age", "20"); writer.writeEndElement(); writer.writeEndElement(); writer.writeEndDocument(); file.close(); } 以上是Qt中操作INI文件、JSON文件和XML文件的簡單示例,通過這些方法,我們可以方便地讀寫和處理各種數(shù)據(jù)格式的文件。 171、HTTP 協(xié)議、WebSocket 協(xié)議? HTTP協(xié)議和WebSocket協(xié)議都是常用的網(wǎng)絡(luò)協(xié)議,下面分別介紹它們的特點和使用場景。 HTTP協(xié)議 HTTP(Hypertext Transfer Protocol)是一種應(yīng)用層協(xié)議,用于在Web瀏覽器和Web服務(wù)器之間傳輸數(shù)據(jù)。HTTP協(xié)議基于TCP協(xié)議,使用請求-響應(yīng)模式,客戶端向服務(wù)器發(fā)送請求,服務(wù)器返回響應(yīng)。HTTP協(xié)議的特點如下: 簡單易用:HTTP協(xié)議的請求和響應(yīng)格式簡單明了,易于理解和實現(xiàn)。無狀態(tài)協(xié)議:HTTP協(xié)議是無狀態(tài)協(xié)議,即服務(wù)器不會保存客戶端的狀態(tài)信息,每個請求都是獨立的?;谡埱?響應(yīng)模式:客戶端向服務(wù)器發(fā)送請求,服務(wù)器返回響應(yīng),這種模式適合Web頁面的請求和響應(yīng)。 HTTP協(xié)議主要用于Web瀏覽器和Web服務(wù)器之間的數(shù)據(jù)傳輸,常用于瀏覽器請求Web頁面、獲取靜態(tài)資源、提交表單等場景。 WebSocket協(xié)議 WebSocket是一種基于TCP協(xié)議的全雙工通信協(xié)議,用于在Web瀏覽器和Web服務(wù)器之間實現(xiàn)雙向通信。WebSocket協(xié)議支持在同一個TCP連接上進行數(shù)據(jù)傳輸,避免了HTTP協(xié)議的重復(fù)連接和斷開操作,從而提高了通信效率。WebSocket協(xié)議的特點如下: 雙向通信:WebSocket協(xié)議支持在同一個TCP連接上進行雙向通信,客戶端和服務(wù)器可以同時發(fā)送和接收數(shù)據(jù)。實時性:WebSocket協(xié)議支持實時通信,數(shù)據(jù)傳輸?shù)难舆t和帶寬占用都比HTTP協(xié)議更低??缬蛑С郑篧ebSocket協(xié)議支持跨域通信,可以在不同域名和端口之間進行通信。 WebSocket協(xié)議主要用于需要實時通信和雙向通信的場景,例如在線聊天、實時游戲、遠(yuǎn)程控制等。需要注意的是,WebSocket協(xié)議需要服務(wù)器端和客戶端都支持該協(xié)議才能進行通信。 172、QtChart (圖表、曲線圖、餅狀圖、柱形、拆線圖等) ? QtChart是Qt自帶的圖表庫,用于繪制各種類型的圖表,例如曲線圖、餅狀圖、柱形圖、折線圖等。使用QtChart可以方便地將數(shù)據(jù)可視化,并支持用戶交互和定制化設(shè)置。下面介紹QtChart中常用的幾種圖表類型及其使用方法。 曲線圖 曲線圖用于展示一段時間內(nèi)某個變量的變化趨勢,例如溫度、濕度、壓力等。使用QtChart繪制曲線圖的步驟如下: 創(chuàng)建QChart對象,并設(shè)置標(biāo)題和坐標(biāo)軸。創(chuàng)建QLineSeries對象,設(shè)置曲線的名稱和數(shù)據(jù)。將QLineSeries對象添加到QChart對象中。創(chuàng)建一個QChartView對象,并將QChart對象設(shè)置為其父對象。將QChartView對象添加到布局中。 以下是一個簡單的曲線圖繪制示例: QChart *chart = new QChart(); chart->setTitle("Temperature"); chart->legend()->hide(); chart->createDefaultAxes(); QLineSeries *series = new QLineSeries(); series->setName("Temperature"); series->append(0, 20); series->append(1, 22); series->append(2, 24); series->append(3, 26); chart->addSeries(series); QChartView *chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); ui->chartLayout->addWidget(chartView); 2.餅狀圖 餅狀圖用于展示不同數(shù)據(jù)之間的比例關(guān)系,例如不同國家的GDP占比、不同產(chǎn)品的銷售占比等。使用QtChart繪制餅狀圖的步驟如下: 創(chuàng)建QChart對象,并設(shè)置標(biāo)題。創(chuàng)建QPieSeries對象,設(shè)置餅狀圖的名稱和數(shù)據(jù)。將QPieSeries對象添加到QChart對象中。創(chuàng)建一個QChartView對象,并將QChart對象設(shè)置為其父對象。將QChartView對象添加到布局中。 以下是一個簡單的餅狀圖繪制示例: QChart *chart = new QChart(); chart->setTitle("Sales"); QPieSeries *series = new QPieSeries(); series->setName("Product"); series->append("Product A", 25); series->append("Product B", 35); series->append("Product C", 40); chart->addSeries(series); chart->legend()->setAlignment(Qt::AlignRight); QChartView *chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); ui->chartLayout->addWidget(chartView); 3.柱形圖 柱形圖用于展示不同數(shù)據(jù)之間的數(shù)量關(guān)系,例如不同月份的銷售額、不同省份的人口等。使用QtChart繪制柱形圖的步驟如下: 創(chuàng)建QChart對象,并設(shè)置標(biāo)題和坐標(biāo)軸。創(chuàng)建QBarSeries對象,設(shè)置柱形圖的名稱和數(shù)據(jù)。將QBarSeries對象添加到QChart對象中。創(chuàng)建一個QChartView對象,并將QChart對象設(shè)置為其父對象。將QChartView對象添加到布局中。 以下是一個簡單的柱形圖繪制示例: QChart *chart = new QChart(); chart->setTitle("Sales"); chart->setAnimationOptions(QChart::SeriesAnimations); QBarSeries *series = new QBarSeries(); series->setName("Monthly Sales"); QBarSet *set1 = new QBarSet("January"); QBarSet *set2 = new QBarSet("February"); QBarSet *set3 = new QBarSet("March"); *set1 << 100 << 200 << 150; *set2 << 150 << 100 << 250; *set3 << 200 << 150 << 100; series->append(set1); series->append(set2); series->append(set3); chart->addSeries(series); chart->createDefaultAxes(); chart->legend()->setVisible(true); QChartView *chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); ui->chartLayout->addWidget(chartView); 4.折線圖 折線圖用于展示不同數(shù)據(jù)之間的趨勢變化,例如不同時間段內(nèi)的股票價格、不同季度的GDP增長率等。使用QtChart繪制折線圖的步驟與曲線圖類似。 以上是QtChart中常用的幾種圖表類型的繪制示例,通過QtChart,我們可以方便地將數(shù)據(jù)可視化,讓數(shù)據(jù)更加直觀和易于理解。 173、Qt 中音頻類和視頻類分別是什么? 在Qt中,音頻類和視頻類分別是QAudio和QMediaPlayer。 QAudio QAudio類提供了音頻的輸入和輸出功能,可以用于錄音、播放音頻等操作。使用QAudio需要以下步驟: 創(chuàng)建QAudioDeviceInfo對象,獲取可用的音頻設(shè)備信息。創(chuàng)建QAudioFormat對象,設(shè)置音頻格式。創(chuàng)建QAudioInput或QAudioOutput對象,設(shè)置音頻設(shè)備和音頻格式。開始錄音或播放音頻數(shù)據(jù)。 以下是一個簡單的錄音示例: QAudioFormat format; format.setSampleRate(44100); format.setChannelCount(2); format.setSampleSize(16); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleType(QAudioFormat::SignedInt); QAudioDeviceInfo info(QAudioDeviceInfo::defaultInputDevice()); if (!info.isFormatSupported(format)) { qWarning() << "Default format not supported, trying to use nearest format."; format = info.nearestFormat(format); } QAudioInput *audioInput = new QAudioInput(info, format); QBuffer *buffer = new QBuffer(this); buffer->open(QIODevice::ReadWrite); audioInput->start(buffer); 174、QML鼠標(biāo)與事件處理? QML布局? Loader 動態(tài)加載組件? 在 QML 中,可以通過鼠標(biāo)與事件處理來響應(yīng)用戶的操作。常見的鼠標(biāo)事件包括: 點擊事件(MouseArea)懸停事件(hover)按下事件(pressed)松開事件(released)移動事件(MouseArea) 在 QML 中,可以使用 MouseArea 來處理鼠標(biāo)事件。例如: Rectangle { width: 100 height: 100 color: "blue" MouseArea { anchors.fill: parent onClicked: { console.log("Clicked!") } } } 上面的代碼創(chuàng)建了一個藍(lán)色的矩形,并在其上添加了一個 MouseArea 來處理鼠標(biāo)事件。當(dāng)用戶點擊矩形時,會在控制臺輸出一條消息。 QML布局 在 QML 中,可以使用各種布局來管理控件的位置和大小。常見的布局包括: ColumnLayoutRowLayoutGridLayoutStackLayout 以 ColumnLayout 為例: ColumnLayout { spacing: 10 Rectangle { width: 100 height: 50 color: "red" } Rectangle { width: 100 height: 50 color: "green" } Rectangle { width: 100 height: 50 color: "blue" } } 上面的代碼創(chuàng)建了一個 ColumnLayout,并在其中添加了三個矩形。這些矩形會依次排列在垂直方向上,并且它們之間的間距為 10。 Loader 動態(tài)加載組件 在 QML 中,可以使用 Loader 組件來動態(tài)加載其他組件。例如: Loader { sourceComponent: Rectangle { width: 100 height: 100 color: "red" } } 上面的代碼創(chuàng)建了一個 Loader,并將其 sourceComponent 屬性設(shè)置為一個紅色的矩形。在運行時,Loader 會動態(tài)加載這個矩形,并顯示在其內(nèi)部??梢酝ㄟ^改變 sourceComponent 屬性來動態(tài)加載不同的組件。 175、23 種設(shè)計模式應(yīng)用場景? 設(shè)計模式是一種被廣泛應(yīng)用于軟件設(shè)計中的經(jīng)驗總結(jié)和最佳實踐。以下是常見的23種設(shè)計模式及其應(yīng)用場景: 單例模式:需要確保一個類只有一個實例時,例如配置類、數(shù)據(jù)庫連接類等。工廠模式:需要根據(jù)不同的參數(shù)創(chuàng)建不同的對象時,例如不同類型的數(shù)據(jù)庫連接、日志記錄器等。抽象工廠模式:需要創(chuàng)建一組相關(guān)的對象時,例如不同類型的 UI 控件、不同類型的主題等。建造者模式:需要創(chuàng)建復(fù)雜對象時,例如汽車、電腦、房屋等。原型模式:需要創(chuàng)建大量相似對象時,例如游戲中的敵人、粒子等。適配器模式:需要將一個類的接口轉(zhuǎn)換成另一個類的接口時,例如兼容不同版本的 API、使用第三方庫等。橋接模式:需要將抽象部分和實現(xiàn)部分分離開來時,例如不同類型的圖形界面控件、不同類型的數(shù)據(jù)存儲方式等。組合模式:需要以樹形結(jié)構(gòu)組織對象時,例如目錄結(jié)構(gòu)、圖形界面中的控件等。裝飾器模式:需要動態(tài)地給對象添加額外的功能時,例如添加日志、緩存等。外觀模式:需要簡化復(fù)雜系統(tǒng)的接口時,例如封裝底層庫、封裝復(fù)雜的業(yè)務(wù)邏輯等。享元模式:需要共享大量細(xì)粒度對象時,例如字符串池、對象池等。代理模式:需要控制對原始對象的訪問時,例如權(quán)限控制、遠(yuǎn)程訪問等。職責(zé)鏈模式:需要將請求發(fā)送給多個對象時,并動態(tài)確定哪個對象處理該請求時,例如日志記錄器、異常處理器等。命令模式:需要將操作封裝成對象,并支持撤銷、重做等操作時,例如 GUI 應(yīng)用中的操作歷史記錄、文本編輯器中的撤銷、重做操作等。解釋器模式:需要解釋一種語言或表達(dá)式時,例如正則表達(dá)式、數(shù)學(xué)表達(dá)式等。迭代器模式:需要遍歷一個對象集合時,例如集合類、文件系統(tǒng)等。中介者模式:需要將多個對象之間的通信進行解耦時,例如 GUI 應(yīng)用中的組件之間的交互、多人游戲中的玩家之間的交互等。備忘錄模式:需要保存和恢復(fù)對象的狀態(tài)時,例如文本編輯器中的撤銷、重做操作等。觀察者模式:需要實現(xiàn)對象之間的消息通信時,例如事件驅(qū)動的 GUI 應(yīng)用、發(fā)布訂閱模式等。狀態(tài)模式:需要根據(jù)對象的狀態(tài)改變其行為時,例如游戲中的角色狀態(tài)、多線程中的任務(wù)狀態(tài)等。策略模式:需要在運行時根據(jù)不同的情況選擇不同的算法時,例如排序算法、加密算法等。模板方法模式:需要定義一個算法的框架,并在子類中實現(xiàn)具體的步驟時,例如 GUI 應(yīng)用中的生命周期、游戲中的角色行為等。訪問者模式:需要對一組對象執(zhí)行相同的操作時,例如編譯器中的 AST、圖形界面中的控件等。 176、Qt相機和視頻處理技術(shù)? Qt相機和視頻處理技術(shù)是Qt提供的一組API和庫,用于在Qt應(yīng)用程序中訪問攝像頭和處理視頻。以下是一些常用的Qt相機和視頻處理技術(shù): QtMultimedia模塊:提供了訪問音頻、視頻、相機等多媒體設(shè)備的API??梢杂脕碓赒t應(yīng)用程序中訪問相機,捕捉視頻和音頻流,以及播放音頻和視頻文件。QtCamera模塊:是QtMultimedia模塊的一部分,提供了訪問相機的API。可以用來獲取相機的設(shè)備信息、預(yù)覽相機的圖像、捕捉相機的圖像和視頻等。QtAV庫:是一個基于Qt的多媒體框架,提供了高性能的視頻和音頻處理能力??梢杂脕聿シ鸥鞣N格式的視頻和音頻文件,以及進行視頻編解碼和處理。OpenCV庫:是一個開源的計算機視覺庫,提供了豐富的圖像和視頻處理算法??梢杂脕碓赒t應(yīng)用程序中實現(xiàn)圖像和視頻的處理,例如人臉識別、目標(biāo)跟蹤、圖像濾波等。FFmpeg庫:是一個開源的音視頻編解碼庫,支持多種音視頻格式和編解碼器??梢杂脕碓赒t應(yīng)用程序中實現(xiàn)音視頻的錄制、播放和轉(zhuǎn)碼等功能。 通過使用這些技術(shù),開發(fā)者可以方便地在Qt應(yīng)用程序中訪問相機和處理視頻,實現(xiàn)各種有趣的功能,例如人臉識別、視頻監(jiān)控、視頻編輯等。 177、OpenCV人臉識別技術(shù)方法? OpenCV是一個開源的計算機視覺庫,提供了多種圖像處理和分析的算法和工具。其中包括人臉識別技術(shù),以下是一些常用的OpenCV人臉識別技術(shù)方法: Haar特征分類器:Haar特征分類器是一種基于Haar小波變換的人臉檢測算法。該算法通過對圖像中的不同區(qū)域進行Haar小波變換,提取出不同的Haar特征,然后使用AdaBoost算法對這些特征進行分類,最終得到一個可以用于檢測人臉的分類器。LBP特征分類器:LBP特征分類器是一種基于局部二值模式(LBP)的人臉檢測算法。該算法通過對圖像中的不同區(qū)域進行LBP特征提取,然后使用級聯(lián)分類器對這些特征進行分類,最終得到一個可以用于檢測人臉的分類器。相對于Haar特征分類器,LBP特征分類器具有更快的檢測速度和更高的檢測精度。人臉識別:人臉識別是一種基于人臉特征提取和匹配的技術(shù),用于識別和驗證人臉身份。常用的人臉識別方法包括基于特征的方法、基于模型的方法、基于深度學(xué)習(xí)的方法等。其中,基于特征的方法通常使用PCA、LDA等技術(shù)對人臉圖像進行特征提取和降維,然后使用SVM、KNN等算法進行分類和識別。人臉跟蹤:人臉跟蹤是一種用于跟蹤人臉運動的技術(shù),通常用于視頻監(jiān)控、人機交互等應(yīng)用場景。常用的人臉跟蹤方法包括基于模板匹配的方法、基于卡爾曼濾波的方法、基于粒子濾波的方法等。 通過使用以上的OpenCV人臉識別技術(shù)方法,可以實現(xiàn)人臉檢測、人臉識別和人臉跟蹤等功能,應(yīng)用于視頻監(jiān)控、人機交互等領(lǐng)域。 178、OpenCV實現(xiàn)圖片美化原理機制? OpenCV實現(xiàn)圖片美化可以通過以下幾個步驟實現(xiàn): 圖像增強:使用直方圖均衡化或CLAHE(對比度受限的自適應(yīng)直方圖均衡化)算法增強圖像的對比度和亮度。銳化:使用高斯濾波器或雙邊濾波器對圖像進行模糊處理,然后使用拉普拉斯濾波器或Sobel算子對圖像進行銳化處理,以增強圖像的細(xì)節(jié)和清晰度。去噪:使用中值濾波器或均值濾波器對圖像進行去噪處理,以減少圖像中的噪聲和雜點。美白:使用顏色空間轉(zhuǎn)換技術(shù)將圖像從RGB空間轉(zhuǎn)換到Y(jié)CbCr空間,然后對亮度分量進行調(diào)整,以實現(xiàn)圖像的美白效果。磨皮:使用雙邊濾波器或高斯雙邊濾波器對圖像進行平滑處理,以減少皮膚細(xì)節(jié)的噪聲和雜點,從而實現(xiàn)磨皮的效果。美顏:使用圖像融合技術(shù)將磨皮和美白的效果與原圖像進行融合,以實現(xiàn)圖像的美顏效果。 上述步驟中,圖像增強、銳化和去噪處理可以使用OpenCV中的濾波器和邊緣檢測算法實現(xiàn);美白和磨皮處理可以使用OpenCV中的顏色空間轉(zhuǎn)換和雙邊濾波器實現(xiàn);美顏處理可以使用OpenCV中的圖像融合技術(shù)實現(xiàn)。 總的來說,OpenCV實現(xiàn)圖片美化的原理機制是通過對圖像進行增強、銳化、去噪、美白、磨皮和美顏等多個處理步驟,從而實現(xiàn)對圖像的優(yōu)化和美化。 179、OpenCV多圖合成技術(shù)原理? OpenCV多圖合成技術(shù)可以通過以下幾個步驟實現(xiàn): 讀取多張圖像:使用OpenCV中的imread函數(shù)讀取多張圖像,存儲為Mat對象。圖像融合:使用OpenCV中的圖像融合技術(shù)將多張圖像進行融合,實現(xiàn)多圖合成的效果。常用的圖像融合方法包括加權(quán)平均融合、拉普拉斯金字塔融合、圖像拼接等。圖像調(diào)整:根據(jù)實際需求,對融合后的圖像進行調(diào)整,如旋轉(zhuǎn)、縮放、裁剪等處理。圖像輸出:使用OpenCV中的imwrite函數(shù)將處理后的圖像輸出為文件,以便后續(xù)使用。 在以上步驟中,圖像融合是多圖合成的核心步驟。常用的圖像融合方法包括:加權(quán)平均融合:將多張圖像按照一定的權(quán)重進行加權(quán)平均,得到一張融合后的圖像。加權(quán)平均融合方法簡單易懂,但需要手動設(shè)置權(quán)重,且容易出現(xiàn)過度或不足的情況。拉普拉斯金字塔融合:將多張圖像依次進行高斯金字塔分解和拉普拉斯金字塔重建,然后按照一定的權(quán)重進行融合,得到一張融合后的圖像。拉普拉斯金字塔融合方法可以自動調(diào)整權(quán)重,融合效果較好,但計算復(fù)雜度較高。圖像拼接:將多張圖像按照一定的拼接方式進行拼接,得到一張融合后的大圖。常見的拼接方式包括水平拼接、垂直拼接、矩形拼接等。圖像拼接方法可以實現(xiàn)較大的圖像拼接,但容易出現(xiàn)拼接痕跡和形變。 總的來說,OpenCV多圖合成技術(shù)原理是通過讀取多張圖像,使用圖像融合技術(shù)實現(xiàn)多圖合成,然后根據(jù)實際需求進行圖像調(diào)整和輸出。常用的圖像融合方法包括加權(quán)平均融合、拉普拉斯金字塔融合和圖像拼接等。 180、OpenCV 的視頻中反投影圖像技術(shù)原理 OpenCV的視頻中反投影圖像技術(shù)(Video-based Image Analysis and PerspectivE Rectification,簡稱VIPER)是指利用視頻序列中的圖像信息來對場景進行重建和跟蹤的一種技術(shù)。其中,反投影圖像技術(shù)是VIPER技術(shù)的核心之一,其原理如下: 構(gòu)建模板:首先,根據(jù)需要跟蹤的目標(biāo),從視頻序列中選擇一幀圖像,并手動選定目標(biāo)區(qū)域,將其作為模板。計算直方圖:對模板進行顏色直方圖計算,得到目標(biāo)顏色的分布情況。計算反投影圖像:對視頻序列中的每一幀圖像,進行顏色直方圖計算,并將其與模板的直方圖進行比較,得到每個像素點的概率值,即反投影圖像。反投影圖像中,像素點的值越大,表明該像素點屬于目標(biāo)的概率越高。二值化處理:通過設(shè)置閾值,將反投影圖像中的像素值大于閾值的像素點標(biāo)記為目標(biāo)點,其余像素點標(biāo)記為背景點。目標(biāo)追蹤:根據(jù)目標(biāo)點的位置信息,對目標(biāo)進行跟蹤。在后續(xù)的視頻幀中,重復(fù)以上步驟,實現(xiàn)目標(biāo)的實時追蹤。 反投影圖像技術(shù)利用了目標(biāo)區(qū)域的顏色信息,通過計算直方圖的方式將其轉(zhuǎn)換為概率分布,在視頻序列中的每一幀圖像中進行比較,得到每個像素點的概率值,從而實現(xiàn)對目標(biāo)的跟蹤。它在目標(biāo)跟蹤方面具有一定的優(yōu)勢,但也存在一些問題,如對光照變化和背景干擾比較敏感,需要進行后續(xù)的優(yōu)化和改進。 181、數(shù)據(jù)庫的常用范式有那些? 數(shù)據(jù)庫的常用范式有以下幾種: 第一范式(1NF):確保每個屬性都是原子性的,即不可再分。滿足1NF的關(guān)系稱為平凡關(guān)系。第二范式(2NF):在滿足1NF的基礎(chǔ)上,消除非主屬性對主鍵的部分依賴關(guān)系。即每個非主屬性都完全依賴于主鍵。第三范式(3NF):在滿足2NF的基礎(chǔ)上,消除非主屬性對主鍵的傳遞依賴關(guān)系。即非主屬性不依賴于其他非主屬性。巴斯-科德范式(BCNF):在滿足3NF的基礎(chǔ)上,消除主屬性對主鍵的部分依賴關(guān)系。即每個主屬性都完全依賴于主鍵。第四范式(4NF):在滿足BCNF的基礎(chǔ)上,消除多值依賴關(guān)系。即每個非主屬性都不依賴于其他非主屬性的多值集合。第五范式(5NF):在滿足4NF的基礎(chǔ)上,消除關(guān)聯(lián)依賴關(guān)系。即每個非主屬性都不依賴于其他非主屬性的多值集合的子集。高斯范式(GNF):在滿足5NF的基礎(chǔ)上,消除無損分解的冗余依賴關(guān)系。 范式越高,數(shù)據(jù)冗余越少,數(shù)據(jù)更新異常越少,數(shù)據(jù)的一致性和完整性就越好。但是,高范式的設(shè)計也需要考慮查詢操作的效率和數(shù)據(jù)存儲的空間占用。因此,在實際應(yīng)用中,需要根據(jù)需求和實際情況進行范式的選擇和權(quán)衡。 182、MySQL 架構(gòu)的Server層的執(zhí)行過程? MySQL的架構(gòu)分為Server層和存儲引擎層兩部分,其中Server層負(fù)責(zé)處理客戶端請求、管理連接、查詢解析和優(yōu)化、權(quán)限管理等任務(wù),而存儲引擎層則負(fù)責(zé)數(shù)據(jù)的存儲和管理。下面是MySQL Server層的執(zhí)行過程: 連接管理:MySQL Server接收客戶端的連接請求,根據(jù)配置文件中的參數(shù)設(shè)置和連接請求中攜帶的信息,對連接進行驗證和管理,包括同時連接數(shù)、連接超時、最大連接數(shù)等。查詢解析和優(yōu)化:當(dāng)客戶端發(fā)送SQL語句請求時,MySQL Server會對SQL語句進行解析和優(yōu)化,生成執(zhí)行計劃。解析階段會將SQL語句轉(zhuǎn)換為內(nèi)部數(shù)據(jù)結(jié)構(gòu)——解析樹,優(yōu)化階段會根據(jù)解析樹進行優(yōu)化,包括索引選擇、關(guān)聯(lián)查詢優(yōu)化等。查詢執(zhí)行:生成執(zhí)行計劃后,MySQL Server將執(zhí)行計劃交給存儲引擎層執(zhí)行,存儲引擎負(fù)責(zé)具體的數(shù)據(jù)訪問和操作。MySQL Server會將執(zhí)行結(jié)果緩存在內(nèi)存中,以提高查詢效率。權(quán)限管理:MySQL Server會根據(jù)用戶的權(quán)限,對查詢和操作進行授權(quán)和管理,包括用戶的登錄授權(quán)、資源訪問授權(quán)等。錯誤處理和日志記錄:MySQL Server會對執(zhí)行過程中出現(xiàn)的錯誤進行處理和記錄,并輸出錯誤信息。同時,MySQL Server會將執(zhí)行日志、慢查詢?nèi)罩?、錯誤日志等信息記錄下來,以便后續(xù)的優(yōu)化和分析。 MySQL Server層的執(zhí)行過程涉及到多個組件和模塊,需要協(xié)同工作,才能完成客戶端請求的處理和查詢操作的執(zhí)行。 183、常用存儲弓|擎? InnoDB 與MylSAM的區(qū)別? 常用的存儲引擎包括InnoDB、MyISAM、Memory、Archive、CSV、Blackhole等。其中,InnoDB和MyISAM是MySQL中最常用的兩種存儲引擎,它們的主要區(qū)別如下: 事務(wù)支持:InnoDB支持事務(wù),而MyISAM不支持事務(wù)。鎖機制:InnoDB采用行級鎖,支持多版本并發(fā)控制(MVCC),可以實現(xiàn)更高的并發(fā)性和更好的讀寫性能;而MyISAM采用表級鎖,只有在讀取和更新表時才會對整個表加鎖,因此并發(fā)性相對較低。索引結(jié)構(gòu):InnoDB的主鍵索引采用B+樹結(jié)構(gòu),數(shù)據(jù)文件本身就是索引文件,因此查詢效率較高;而MyISAM的索引采用B樹結(jié)構(gòu),數(shù)據(jù)文件與索引文件分開存儲,因此查詢效率相對較低??臻g占用:InnoDB占用的空間相對較大,因為它需要維護事務(wù)日志、回滾日志等;而MyISAM占用的空間相對較小,因為它不需要維護這些額外的信息。支持全文檢索:MyISAM支持全文檢索,而InnoDB不支持。 綜上所述,InnoDB適合于要求數(shù)據(jù)完整性和事務(wù)支持的應(yīng)用,如電子商務(wù)、金融等;而MyISAM適合于讀寫比較少、對性能要求較高的應(yīng)用,如博客、新聞等。當(dāng)然,選擇存儲引擎還要根據(jù)具體的應(yīng)用場景和需求進行選擇。 184、事務(wù)的ACID與實現(xiàn)原理? 事務(wù)是指一組邏輯操作,要么全部執(zhí)行成功,要么全部執(zhí)行失敗,是保證數(shù)據(jù)完整性和一致性的重要機制。ACID是事務(wù)的四個基本特性,分別是原子性、一致性、隔離性和持久性。 原子性:事務(wù)是一個原子操作,要么全部執(zhí)行成功,要么全部執(zhí)行失敗,不存在部分執(zhí)行的情況。一致性:事務(wù)執(zhí)行前后,數(shù)據(jù)的完整性和一致性都得到保證,不會破壞數(shù)據(jù)的完整性和一致性。隔離性:事務(wù)之間是相互隔離的,一個事務(wù)的執(zhí)行不會影響其他事務(wù)的執(zhí)行。持久性:事務(wù)執(zhí)行成功后,對數(shù)據(jù)的修改將被永久保存在數(shù)據(jù)庫中,即使系統(tǒng)崩潰也不會丟失數(shù)據(jù)。 實現(xiàn)原理: 實現(xiàn)事務(wù)的ACID特性需要數(shù)據(jù)庫管理系統(tǒng)提供一些機制和技術(shù)來支持,主要包括以下幾個方面:原子性:實現(xiàn)原子性需要使用事務(wù)日志(Transaction Log),將所有的事務(wù)操作記錄到日志中,保證在事務(wù)執(zhí)行過程中,任何一個操作失敗或者出現(xiàn)錯誤,可以通過回滾日志將數(shù)據(jù)庫恢復(fù)到事務(wù)執(zhí)行前的狀態(tài)。一致性:實現(xiàn)一致性需要使用鎖機制,將事務(wù)執(zhí)行過程中涉及的數(shù)據(jù)進行鎖定,防止其他事務(wù)對數(shù)據(jù)進行修改,保證數(shù)據(jù)的完整性和一致性。隔離性:實現(xiàn)隔離性需要使用并發(fā)控制機制,包括鎖機制和多版本并發(fā)控制(MVCC)等,在保證數(shù)據(jù)一致性的前提下,提高并發(fā)性能。持久性:實現(xiàn)持久性需要使用事務(wù)日志和緩沖區(qū)管理等技術(shù),將事務(wù)操作記錄到日志中,并在提交事務(wù)后將數(shù)據(jù)寫入磁盤,保證即使系統(tǒng)崩潰也不會丟失數(shù)據(jù)。 總之,事務(wù)的ACID特性是數(shù)據(jù)庫管理系統(tǒng)的基本特性之一,它保證了數(shù)據(jù)的完整性和一致性,是保證數(shù)據(jù)質(zhì)量和安全性的重要機制。 185、數(shù)據(jù)庫中的鎖機制? 數(shù)據(jù)庫中的鎖機制是為了保證多個事務(wù)之間的并發(fā)執(zhí)行時,數(shù)據(jù)的一致性和完整性。常見的鎖機制包括行級鎖、表級鎖、頁級鎖等,常用的鎖有:共享鎖(S鎖)、排他鎖(X鎖)、意向鎖(IS鎖和IX鎖)等。 行級鎖:是在行級別上對數(shù)據(jù)進行加鎖,只有需要修改該行數(shù)據(jù)的事務(wù)才會加上排他鎖,其他事務(wù)可以繼續(xù)讀取該行數(shù)據(jù),但是不能修改。行級鎖可以提高并發(fā)性能,降低鎖沖突的概率,但是會增加數(shù)據(jù)庫的開銷和復(fù)雜性。表級鎖:是在表級別上對數(shù)據(jù)進行加鎖,當(dāng)一個事務(wù)對表進行修改時,會對整個表加上排他鎖,其他事務(wù)不能讀取和修改該表的數(shù)據(jù)。表級鎖簡單、易于實現(xiàn),但是會導(dǎo)致并發(fā)性能下降,鎖沖突的概率增加。頁級鎖:是在頁級別上對數(shù)據(jù)進行加鎖,當(dāng)一個事務(wù)對某個數(shù)據(jù)頁進行修改時,會對該數(shù)據(jù)頁加上排他鎖,其他事務(wù)不能訪問該數(shù)據(jù)頁的數(shù)據(jù)。頁級鎖可以降低鎖沖突的概率,但是會增加數(shù)據(jù)庫的開銷和復(fù)雜性。共享鎖(S鎖):允許多個事務(wù)同時讀取同一份數(shù)據(jù),但是不能修改數(shù)據(jù)。共享鎖可以提高并發(fā)性能,但是會導(dǎo)致讀取數(shù)據(jù)的事務(wù)需要等待排他鎖釋放。排他鎖(X鎖):只允許一個事務(wù)對數(shù)據(jù)進行修改,其他事務(wù)不能讀取和修改該數(shù)據(jù)。排他鎖可以保證數(shù)據(jù)的一致性和完整性,但是會導(dǎo)致并發(fā)性能下降,因為其他事務(wù)需要等待排他鎖釋放。意向鎖(IS鎖和IX鎖):是一種輔助鎖,用于協(xié)助數(shù)據(jù)庫管理系統(tǒng)判斷需要加什么級別的鎖。IS鎖用于表級別,表示一個事務(wù)需要在表中加共享鎖;IX鎖用于表級別,表示一個事務(wù)需要在表中加排他鎖。 總之,鎖機制是數(shù)據(jù)庫管理系統(tǒng)實現(xiàn)事務(wù)隔離性的重要手段,不同的鎖級別和鎖類型可以提高并發(fā)性能,降低鎖沖突的概率,但是也會增加數(shù)據(jù)庫的開銷和復(fù)雜性。 186、MySQL索引的實現(xiàn)原理? MySQL的索引是為了加快查詢速度而設(shè)計的,它通過將數(shù)據(jù)存儲在特定的數(shù)據(jù)結(jié)構(gòu)中,以便快速訪問和檢索數(shù)據(jù)。MySQL支持多種類型的索引,包括B樹索引、哈希索引、全文索引等,其中最常用的是B樹索引。 B樹索引的實現(xiàn)原理如下: B樹是一種平衡二叉樹,它的每個節(jié)點可以存儲多個關(guān)鍵字和對應(yīng)的指針,通常稱為B樹節(jié)點。B樹的節(jié)點分為根節(jié)點、葉子節(jié)點和中間節(jié)點,其中葉子節(jié)點存儲了索引的值和對應(yīng)的指針,中間節(jié)點存儲了索引的值和指向子節(jié)點的指針。B樹節(jié)點的大小通常與磁盤頁的大小相同,可以減少磁盤I/O的次數(shù),提高訪問速度。B樹的插入和刪除操作需要遵循平衡二叉樹的規(guī)則,當(dāng)節(jié)點達(dá)到一定大小時,需要進行分裂或合并操作,以保持平衡。B樹的查找操作可以采用二分查找的方式,從根節(jié)點開始逐層查找,直到找到目標(biāo)節(jié)點的位置。B樹索引支持范圍查找和模糊查找,可以提高查詢效率。 總之,B樹索引是MySQL最常用的索引類型之一,它通過平衡二叉樹的結(jié)構(gòu)和節(jié)點的大小優(yōu)化磁盤I/O,提高訪問速度和查詢效率。 187、SQL優(yōu)化和索引優(yōu)化、表結(jié)構(gòu)優(yōu)化? SQL優(yōu)化、索引優(yōu)化和表結(jié)構(gòu)優(yōu)化是提高數(shù)據(jù)庫性能的重要手段,它們可以減少數(shù)據(jù)庫的開銷和提高查詢效率。下面分別介紹它們的優(yōu)化方法: SQL優(yōu)化: 避免使用SELECT *,只查詢需要的列;避免使用子查詢,可以使用JOIN語句代替;避免使用OR,可以使用IN和UNION代替;避免使用LIKE ‘%!v(MISSING)alue%!’(MISSING),可以使用LIKE ‘value%!’(MISSING)或全文索引代替;避免使用ORDER BY RAND(),可以使用ORDER BY id DESC + LIMIT代替;避免使用大量的UNION,可以使用UNION ALL代替;避免使用GROUP BY,可以使用DISTINCT代替;避免使用HAVING,可以使用WHERE代替。 2.索引優(yōu)化: 選擇合適的索引類型,如B樹索引、哈希索引、全文索引等;選擇合適的索引列,如經(jīng)常查詢的列、聯(lián)合查詢的列、分組和排序的列等;避免使用過多的索引,會增加寫入數(shù)據(jù)的開銷;避免使用過長的索引,會增加磁盤I/O的次數(shù);避免使用NULL值,會降低索引的效率;避免使用函數(shù)和表達(dá)式,會降低索引的效率。 3.表結(jié)構(gòu)優(yōu)化: 采用合適的數(shù)據(jù)類型,如INT、VARCHAR、DATETIME等;避免使用大量的TEXT和BLOB類型,會增加磁盤I/O的次數(shù);避免使用NULL值,會增加存儲空間和查詢開銷;采用合適的表結(jié)構(gòu),如垂直拆分、水平拆分等;避免使用過多的表關(guān)聯(lián),會增加查詢開銷;避免使用過多的觸發(fā)器和存儲過程,會增加寫入數(shù)據(jù)的開銷。 總之,SQL優(yōu)化、索引優(yōu)化和表結(jié)構(gòu)優(yōu)化是提高數(shù)據(jù)庫性能的重要手段,需要根據(jù)具體的情況進行優(yōu)化,以達(dá)到最佳的查詢效率和性能。 188、數(shù)據(jù)庫參數(shù)優(yōu)先? 數(shù)據(jù)庫參數(shù)優(yōu)化是提高數(shù)據(jù)庫性能的重要手段之一,通過調(diào)整數(shù)據(jù)庫的參數(shù)設(shè)置可以達(dá)到提高性能的效果。在優(yōu)化數(shù)據(jù)庫參數(shù)時,需要遵循以下的優(yōu)先級: 業(yè)務(wù)需求優(yōu)先:數(shù)據(jù)庫參數(shù)優(yōu)化的目的是為了提高業(yè)務(wù)性能,因此需要優(yōu)先考慮業(yè)務(wù)需求,包括數(shù)據(jù)庫的讀寫比例、業(yè)務(wù)的瓶頸等。硬件資源優(yōu)先:數(shù)據(jù)庫參數(shù)優(yōu)化需要結(jié)合硬件資源來進行,包括CPU、內(nèi)存、磁盤等,需要根據(jù)硬件資源的實際情況來調(diào)整參數(shù)設(shè)置。數(shù)據(jù)庫版本優(yōu)先:不同版本的數(shù)據(jù)庫在參數(shù)設(shè)置上可能存在差異,因此需要根據(jù)具體的數(shù)據(jù)庫版本來進行參數(shù)優(yōu)化。數(shù)據(jù)庫引擎優(yōu)先:不同的數(shù)據(jù)庫引擎在參數(shù)設(shè)置上也存在差異,例如MySQL的InnoDB和MyISAM引擎,需要針對不同的引擎進行參數(shù)優(yōu)化。優(yōu)化建議優(yōu)先:數(shù)據(jù)庫廠商、第三方工具以及其他DBA等都會提供一些優(yōu)化建議,需要根據(jù)實際情況進行參考和調(diào)整。 在進行數(shù)據(jù)庫參數(shù)優(yōu)化時,需要結(jié)合上述優(yōu)先級進行綜合考慮,以達(dá)到最佳的優(yōu)化效果。此外,還需要進行測試和監(jiān)測,以確保優(yōu)化后的數(shù)據(jù)庫性能達(dá)到預(yù)期的目標(biāo)。 189、explain 的執(zhí)行計劃? EXPLAIN是一個用于分析MySQL查詢語句的關(guān)鍵字,可以幫助我們了解MySQL是如何執(zhí)行查詢語句的。執(zhí)行EXPLAIN查詢后,MySQL將返回一張包含查詢執(zhí)行計劃的結(jié)果集,執(zhí)行計劃描述了MySQL是如何處理查詢語句的、使用哪些索引以及使用了哪些優(yōu)化器等信息。 執(zhí)行EXPLAIN查詢時,需要在查詢語句前加上EXPLAIN關(guān)鍵字,例如: EXPLAIN SELECT * FROM table WHERE id = 1; 執(zhí)行計劃的結(jié)果集包含以下列: id:查詢中每個SELECT子句和操作表的唯一標(biāo)識符,可以用來標(biāo)識哪些操作是相關(guān)的。select_type:查詢的類型,包括簡單查詢、聯(lián)合查詢、子查詢等。table:操作的表名。partitions:匹配到的分區(qū)。type:訪問類型,包括全表掃描、索引掃描、范圍掃描等。possible_keys:可能使用的索引。key:實際使用的索引。key_len:使用的索引長度。ref:連接匹配條件的列。rows:MySQL估計需要掃描的行數(shù)。filtered:過濾行的百分比。Extra:包含MySQL解決查詢的詳細(xì)信息,如使用的優(yōu)化器、是否使用了臨時表等。 通過分析執(zhí)行計劃,可以了解到MySQL是如何處理查詢語句的、使用哪些索引以及使用了哪些優(yōu)化器等信息,從而可以根據(jù)執(zhí)行計劃進行SQL性能優(yōu)化。 190、MySQL的主從復(fù)制? MySQL的主從復(fù)制是一種數(shù)據(jù)復(fù)制技術(shù),可以將一個MySQL主服務(wù)器上的數(shù)據(jù)以異步方式復(fù)制到一個或多個MySQL從服務(wù)器上,從服務(wù)器上的數(shù)據(jù)與主服務(wù)器上的數(shù)據(jù)保持一致。主從復(fù)制可以幫助我們實現(xiàn)數(shù)據(jù)備份、讀寫分離、負(fù)載均衡等功能。 主從復(fù)制的基本原理如下: 主服務(wù)器將寫操作記錄到二進制日志(binary log)中,包括對表的增刪改操作。從服務(wù)器連接到主服務(wù)器,請求復(fù)制二進制日志中的數(shù)據(jù)。主服務(wù)器將二進制日志中的數(shù)據(jù)發(fā)送給從服務(wù)器,從服務(wù)器接收并應(yīng)用這些數(shù)據(jù)。從服務(wù)器定期輪詢主服務(wù)器,查找更新的二進制日志數(shù)據(jù),并將這些數(shù)據(jù)應(yīng)用到從服務(wù)器上。 在MySQL的主從復(fù)制中,主服務(wù)器是唯一的寫入節(jié)點,負(fù)責(zé)更新數(shù)據(jù),而從服務(wù)器是只讀的,只負(fù)責(zé)復(fù)制數(shù)據(jù)。主服務(wù)器和從服務(wù)器之間的復(fù)制可以是異步的,因此主服務(wù)器上的數(shù)據(jù)更新可能不會立即反映到從服務(wù)器上,但一般情況下這種延遲是可控的,并不會影響系統(tǒng)的穩(wěn)定性和安全性。 需要注意的是,在主從復(fù)制中,從服務(wù)器只是主服務(wù)器數(shù)據(jù)的復(fù)制品,不能對其進行寫入操作,否則可能會導(dǎo)致數(shù)據(jù)不一致。如果需要對數(shù)據(jù)進行寫入操作,則需要在主服務(wù)器上進行操作。 191、讀寫分離? 讀寫分離是一種數(shù)據(jù)庫優(yōu)化方案,通過將讀操作和寫操作分離到不同的服務(wù)器上,從而達(dá)到提高數(shù)據(jù)庫性能和可用性的目的。讀寫分離的基本原理是:將寫入操作集中在主服務(wù)器上,而將讀操作分散到多個從服務(wù)器上,從而降低主服務(wù)器的讀負(fù)載,提高數(shù)據(jù)庫的并發(fā)處理能力。 讀寫分離的主要實現(xiàn)方式是:在應(yīng)用程序中使用多個數(shù)據(jù)庫連接,其中一個連接用于寫操作,而其他連接則用于讀操作。讀寫分離的優(yōu)點包括: 提高性能:通過將讀操作分散到多個從服務(wù)器上,可以降低主服務(wù)器的讀負(fù)載,提高數(shù)據(jù)庫的并發(fā)處理能力。提高可用性:在主服務(wù)器出現(xiàn)故障時,可以通過從服務(wù)器提供讀服務(wù)來保證系統(tǒng)的可用性。提高擴展性:通過增加從服務(wù)器的數(shù)量,可以提高系統(tǒng)的擴展性,滿足不斷增長的訪問需求。 讀寫分離的缺點包括:數(shù)據(jù)不一致:由于主服務(wù)器和從服務(wù)器之間的數(shù)據(jù)復(fù)制是異步的,因此在數(shù)據(jù)復(fù)制過程中可能會出現(xiàn)數(shù)據(jù)不一致的情況。配置復(fù)雜:讀寫分離需要配置多個服務(wù)器,管理和維護比較復(fù)雜。代碼改動:應(yīng)用程序需要對讀寫操作進行區(qū)分,代碼改動比較大。 需要注意的是,在讀寫分離中,寫操作只能在主服務(wù)器上執(zhí)行,而讀操作可以在主服務(wù)器和從服務(wù)器上執(zhí)行。如果需要保證數(shù)據(jù)的一致性,可以通過采用主從復(fù)制的方式來實現(xiàn)。 192、 分庫分表(垂直分表、垂直分庫、水平分表、水平分庫) ? 分庫分表是一種常見的數(shù)據(jù)庫水平和垂直擴展方案,用于解決單一數(shù)據(jù)庫性能瓶頸問題。分庫分表的實現(xiàn)方式有垂直分表、垂直分庫、水平分表和水平分庫四種方式。 垂直分表:將一張表按照列拆分成多個表,每個表只存儲部分列的數(shù)據(jù),不同的表可以存儲在不同的數(shù)據(jù)庫中。垂直分表的優(yōu)點是可以將不同的關(guān)注點拆分到不同的表中,減少表的冗余和重復(fù),提高查詢效率。缺點是如果需要查詢多個表的數(shù)據(jù)則需要進行關(guān)聯(lián)查詢,增加了查詢的復(fù)雜度。垂直分庫:將不同的業(yè)務(wù)數(shù)據(jù)存儲在不同的數(shù)據(jù)庫中,每個數(shù)據(jù)庫只處理特定的業(yè)務(wù)邏輯。垂直分庫的優(yōu)點是可以提高數(shù)據(jù)庫讀寫的并發(fā)性,減少單庫數(shù)據(jù)量,降低單點故障的風(fēng)險。缺點是增加了系統(tǒng)的復(fù)雜性,增加了數(shù)據(jù)一致性的難度。水平分表:將一張表按照行進行拆分,將不同的行數(shù)據(jù)存儲在不同的表中,每個表可以存儲在不同的數(shù)據(jù)庫中。水平分表的優(yōu)點是可以將數(shù)據(jù)分散到多個表中,降低單表數(shù)據(jù)量,提高查詢效率和寫入性能。缺點是需要進行數(shù)據(jù)的路由和管理,增加了系統(tǒng)的復(fù)雜性。水平分庫:將一張表按照主鍵的哈希值進行拆分,將不同的數(shù)據(jù)存儲在不同的數(shù)據(jù)庫中。水平分庫的優(yōu)點是可以將數(shù)據(jù)分散到多個庫中,提高系統(tǒng)的并發(fā)能力和可用性。缺點是增加了系統(tǒng)的復(fù)雜性,增加了數(shù)據(jù)一致性的難度。 需要根據(jù)實際情況選擇合適的分庫分表方案,綜合考慮性能、可用性、數(shù)據(jù)一致性和系統(tǒng)復(fù)雜度等因素。 193、分區(qū)? 數(shù)據(jù)庫分區(qū)是一種常見的數(shù)據(jù)庫水平擴展方案,用于解決單一數(shù)據(jù)庫性能瓶頸問題。數(shù)據(jù)庫分區(qū)將一張表的數(shù)據(jù)劃分到多個物理存儲位置上,每個存儲位置可以是不同的磁盤、服務(wù)器或者數(shù)據(jù)中心。數(shù)據(jù)庫分區(qū)可以按照不同的數(shù)據(jù)分區(qū)策略進行劃分,包括: 范圍分區(qū):按照數(shù)據(jù)的范圍進行分區(qū),例如按照時間、地區(qū)、部門等進行分區(qū),可以將相似的數(shù)據(jù)劃分到同一個分區(qū)中,提高查詢效率。哈希分區(qū):按照數(shù)據(jù)的哈希值進行分區(qū),可以將數(shù)據(jù)均勻地分散到多個分區(qū)中,避免單個分區(qū)數(shù)據(jù)量過大,提高寫入性能和查詢效率。列表分區(qū):按照數(shù)據(jù)列表進行分區(qū),可以將相似的數(shù)據(jù)劃分到同一個分區(qū)中,提高查詢效率。復(fù)合分區(qū):按照多個分區(qū)策略進行組合,可以更加靈活地進行數(shù)據(jù)分區(qū)。 數(shù)據(jù)庫分區(qū)的優(yōu)點是可以提高數(shù)據(jù)庫的性能、可用性和擴展性,降低單個數(shù)據(jù)庫的壓力和風(fēng)險。缺點是增加了系統(tǒng)的復(fù)雜度和運維成本,需要考慮數(shù)據(jù)一致性、路由和管理等問題。 需要根據(jù)實際情況選擇合適的數(shù)據(jù)庫分區(qū)策略,綜合考慮性能、可用性、數(shù)據(jù)一致性和系統(tǒng)復(fù)雜度等因素。 194、 主鍵一般用自增ID還是UUID? 主鍵可以使用自增ID或UUID,具體使用哪種方式需要根據(jù)實際情況進行選擇。 使用自增ID的優(yōu)點是: 查詢效率高:自增ID是單調(diào)遞增的整數(shù),可以通過B+樹等數(shù)據(jù)結(jié)構(gòu)快速定位到指定的記錄。存儲空間?。鹤栽鯥D通常使用整數(shù)類型存儲,存儲空間較小。易于管理:自增ID可以自動分配,不需要手動指定主鍵值,避免因主鍵沖突導(dǎo)致的錯誤。 使用UUID的優(yōu)點是:全局唯一:UUID是128位的二進制數(shù),可以保證全局唯一性,避免因主鍵沖突導(dǎo)致的錯誤。分布式:UUID可以在分布式系統(tǒng)中使用,避免因主鍵重復(fù)導(dǎo)致的數(shù)據(jù)同步問題。不可預(yù)測:UUID是隨機生成的,不容易被猜測,可以增加數(shù)據(jù)安全性。 需要根據(jù)實際情況選擇合適的主鍵方式,綜合考慮查詢效率、存儲空間、數(shù)據(jù)安全性和分布式支持等因素。如果主鍵不需要全局唯一性,且查詢效率是優(yōu)先考慮的因素,可以考慮使用自增ID;如果主鍵需要全局唯一性,或者需要在分布式系統(tǒng)中使用,可以考慮使用UUID。 195、視圖View? 視圖(View)是一種虛擬的表,它本身并不存儲任何數(shù)據(jù),而是由查詢語句定義的結(jié)果集。視圖可以像表一樣被查詢,但查詢的結(jié)果實際上是從基本表中檢索出來的數(shù)據(jù)。視圖可以將多個表的數(shù)據(jù)組合在一起,形成一個邏輯上的表,簡化了復(fù)雜查詢的編寫和維護。 視圖的優(yōu)點包括: 簡化查詢:視圖可以將多個表的數(shù)據(jù)組合在一起,形成一個邏輯上的表,簡化了復(fù)雜查詢的編寫和維護。數(shù)據(jù)安全:視圖可以對表進行過濾,只暴露需要訪問的數(shù)據(jù),增強了數(shù)據(jù)的安全性和隱私性。數(shù)據(jù)獨立:視圖可以隱藏底層表的結(jié)構(gòu)和數(shù)據(jù),使得應(yīng)用程序和查詢不受基礎(chǔ)表結(jié)構(gòu)的變化影響。性能優(yōu)化:視圖可以對常用的復(fù)雜查詢進行優(yōu)化和緩存,提高查詢性能。 視圖的缺點包括:更新限制:視圖只是一個虛擬的表,不能直接進行更新操作,需要通過更新底層表來實現(xiàn)。查詢性能:由于視圖需要動態(tài)生成結(jié)果集,查詢性能可能會受到影響。存儲空間:視圖本身不存儲任何數(shù)據(jù),但需要占用一定的存儲空間來存儲定義和元數(shù)據(jù)。 需要根據(jù)實際情況進行視圖的設(shè)計和使用,綜合考慮數(shù)據(jù)安全、數(shù)據(jù)獨立、查詢性能和更新限制等因素。視圖是一個非常常用的數(shù)據(jù)庫對象,可以提高查詢和數(shù)據(jù)管理的效率。 196、存儲過程procedure? 存儲過程(Procedure)是一種預(yù)編譯的數(shù)據(jù)庫對象,包含一組SQL語句和程序邏輯,可以像函數(shù)一樣被調(diào)用。存儲過程通常被用來完成特定的任務(wù)或操作,例如數(shù)據(jù)校驗、數(shù)據(jù)轉(zhuǎn)換、復(fù)雜計算等。存儲過程可以接受參數(shù)和返回值,也可以包含流程控制語句和異常處理機制,具有很強的靈活性和可重用性。 存儲過程的優(yōu)點包括: 提高性能:存儲過程可以預(yù)編譯和緩存,可以減少SQL語句的解析和編譯時間,提高查詢和操作的性能。提高安全性:存儲過程可以對數(shù)據(jù)進行訪問控制和安全檢查,增強了數(shù)據(jù)的安全性和完整性。提高可維護性:存儲過程可以封裝復(fù)雜的業(yè)務(wù)邏輯和數(shù)據(jù)訪問,簡化了應(yīng)用程序的開發(fā)和維護。增強可擴展性:存儲過程可以被多個應(yīng)用程序復(fù)用,可以減少重復(fù)開發(fā)和代碼冗余,增強了系統(tǒng)的可擴展性。 存儲過程的缺點包括:存儲過程的調(diào)試和測試相對困難。存儲過程的部署和維護需要DBA等專業(yè)人員進行管理。存儲過程的更新和修改需要謹(jǐn)慎處理,可能會影響到其他應(yīng)用程序。 需要根據(jù)實際情況進行存儲過程的設(shè)計和使用,綜合考慮性能、安全性、可維護性和可擴展性等因素。存儲過程是一個非常常用的數(shù)據(jù)庫對象,可以提高應(yīng)用程序的性能和可維護性。 197、觸發(fā)器Trigger? 觸發(fā)器(Trigger)是一種特殊的存儲過程,它與數(shù)據(jù)庫表有關(guān)聯(lián)關(guān)系,當(dāng)表中的數(shù)據(jù)發(fā)生變化時,觸發(fā)器會自動地執(zhí)行一些定義好的操作。觸發(fā)器通常被用來實現(xiàn)數(shù)據(jù)約束、日志記錄、數(shù)據(jù)同步等功能。觸發(fā)器可以綁定在表的插入、更新、刪除操作上,也可以在事務(wù)提交前或后執(zhí)行。 觸發(fā)器的優(yōu)點包括: 數(shù)據(jù)約束:觸發(fā)器可以實現(xiàn)數(shù)據(jù)的約束和完整性檢查,確保數(shù)據(jù)的正確性和一致性。日志記錄:觸發(fā)器可以實現(xiàn)對數(shù)據(jù)的變更進行日志記錄,便于數(shù)據(jù)的審計和追蹤。數(shù)據(jù)同步:觸發(fā)器可以實現(xiàn)數(shù)據(jù)的同步和復(fù)制,確保數(shù)據(jù)的一致性和可靠性。靈活性:觸發(fā)器可以根據(jù)業(yè)務(wù)需求自定義操作和邏輯,具有很強的靈活性。 觸發(fā)器的缺點包括:性能影響:觸發(fā)器的執(zhí)行會增加數(shù)據(jù)庫的負(fù)載和延遲,可能會影響系統(tǒng)的性能。調(diào)試和維護困難:觸發(fā)器的調(diào)試和維護相對困難,需要特定的工具和技能。復(fù)雜性:觸發(fā)器的邏輯和操作比較復(fù)雜,需要仔細(xì)考慮和測試。 需要根據(jù)實際情況進行觸發(fā)器的設(shè)計和使用,綜合考慮性能、數(shù)據(jù)約束、日志記錄和數(shù)據(jù)同步等因素。觸發(fā)器是一個非常強大的數(shù)據(jù)庫對象,可以提高數(shù)據(jù)的安全性和完整性,并實現(xiàn)復(fù)雜的業(yè)務(wù)邏輯和數(shù)據(jù)操作。 198、虛析函數(shù)的作用? Vector 底層實現(xiàn)原理? 虛析函數(shù)的作用是防止在子類析構(gòu)時只調(diào)用了父類的析構(gòu)函數(shù),而沒有調(diào)用子類的析構(gòu)函數(shù),導(dǎo)致內(nèi)存泄漏和資源釋放不完全的問題。通過將基類析構(gòu)函數(shù)聲明為虛析函數(shù),可以確保在子類析構(gòu)時會先調(diào)用子類的析構(gòu)函數(shù),再調(diào)用父類的析構(gòu)函數(shù),從而避免這種問題。 Vector是一種動態(tài)數(shù)組,底層實現(xiàn)原理是使用連續(xù)的內(nèi)存塊來存儲元素,并且在需要擴容時重新分配更大的內(nèi)存塊,并將原有元素拷貝到新的內(nèi)存塊中。具體來說,Vector在創(chuàng)建時會分配一塊初始大小的內(nèi)存,當(dāng)元素個數(shù)超過該內(nèi)存大小時,會重新分配一塊更大的內(nèi)存,并將原有元素拷貝到新內(nèi)存中,同時釋放原有內(nèi)存。此過程涉及到內(nèi)存分配、拷貝和釋放,因此Vector的效率受到內(nèi)存管理的影響。為了避免頻繁的內(nèi)存分配和拷貝,Vector通常會預(yù)先分配一定的內(nèi)存空間,以減少擴容的次數(shù)。 199、deque底層實現(xiàn)原理? deque(雙端隊列)底層實現(xiàn)原理是使用一段連續(xù)的存儲空間,被分配為多個內(nèi)存塊,每個內(nèi)存塊獨立分配,內(nèi)部使用指針互相連接。deque中包含一個中控器,中控器中存放著指向第一塊內(nèi)存塊和最后一塊內(nèi)存塊的迭代器,以及指向每個內(nèi)存塊的迭代器,中控器的作用是管理內(nèi)存塊的分配和釋放,并提供訪問內(nèi)存塊的接口。 deque的元素在內(nèi)存中并不是連續(xù)存儲的,而是分散存儲在不同的內(nèi)存塊中,但是中控器提供的接口使得deque的使用者可以像訪問連續(xù)存儲空間一樣訪問deque的元素,即可以使用迭代器、下標(biāo)操作符等對deque進行遍歷和訪問。 對于deque的插入和刪除操作,由于元素在內(nèi)存中是分散存儲的,因此需要在中控器中進行內(nèi)存塊的分配和釋放,并更新迭代器指向。由于deque的實現(xiàn)比較復(fù)雜,因此相比于vector等容器,deque的效率相對較低,但是deque在某些場景下具有優(yōu)勢,比如插入和刪除操作比較頻繁、需要在deque的兩端進行操作等。 200、左值引用與右值引用區(qū)別?右值引用意義? 左值引用和右值引用是C++11中引入的新特性,它們的主要區(qū)別在于引用的類型和綁定的對象。 左值引用是指對一個左值(可以取地址、有名字的變量或者對象、表達(dá)式或函數(shù)返回值)進行的引用,它的類型為T&,可以對其進行修改或者取地址等操作。 右值引用是指對一個右值(不能取地址、沒有名字的臨時對象或者表達(dá)式)進行的引用,它的類型為T&&,通常用于移動語義和完美轉(zhuǎn)發(fā)等場景。右值引用的一個重要作用是支持移動語義,即通過將右值引用參數(shù)移動到目標(biāo)對象中,避免了昂貴的內(nèi)存拷貝操作,提高了程序的效率。 右值引用的意義在于提供了一種新的引用類型,使得我們可以更加高效地處理臨時對象和表達(dá)式,同時也支持了新的語言特性,比如移動語義和完美轉(zhuǎn)發(fā)等。右值引用還可以用于實現(xiàn)移動構(gòu)造函數(shù)和移動賦值運算符等操作,從而提高程序的效率和性能。 柚子快報邀請碼778899分享:c++ QT經(jīng)驗手記 相關(guān)閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。