最近工作的時候有一個連結庫的對接工作,在對接時發生了一些小問題,這篇FAQ是辦公室寫這個庫的工程師戴工寫的,這裡記錄一下:
一、編譯工程時報連結錯誤“不允許dllimport靜態資料成員的定義”
1.錯誤截圖
2.錯誤原因分析
此錯誤是Q_OBJECT和Q_DECL_IMPORT宏共同作用時產生的結果。查詢微軟文件可知:靜態資料成員無法在定義dllimport類的同一程式中指定定義。
這句話表明,匯出類時,其靜態成員不可被外部覆蓋定義。舉個例子,有匯出類如下:
其中宏定義為:
根據微軟關於dllimport的說明,類靜態成員變數n不可被覆蓋定義,即進行這樣的操作
可以看到,對普通成員函式fun進行覆蓋,僅僅發出了編譯警告,但編譯器並沒有明確拒絕這種行為,但在對靜態成員n進行覆蓋定義時,編譯器報錯,拒絕了此行為。
回過頭來看Q_OBJECT宏的定義:
發現靜態成員staticMetaObject,根據QMAKE規則,QT編譯時會對包含有Q_OBJECT宏的類進行一次moc工作,而moc_*.cpp中剛好擁有staticMetaObject的定義操作:
因此,Q_DECL_IMPORT(即dllimport)宏匯出的類中包含了Q_OBJECT宏時,違反了編譯器規則,因此無法完成編譯。
3.修改策略
(1)修改匯出類,如果匯出類中含有Q_OBJECT宏,保留匯出定義,去除匯入定義;
(2)去除Q_DECL_IMPORT宣告。由QT嚮導生成的宏宣告檔案中,有條件編譯控制宏BUILD_STATIC,當工程中定義了該宏時,便可取消Q_DECL_IMPORT宏的匯入宣告;也可以不定義BUILD_STATIC宏,而是手動修改檔案,去除Q_DECL_IMPORT宏。
(3)去除/註釋Q_OBJECT宏。匯入類時,可以在工程程式碼中將匯入庫標頭檔案中的Q_OBJECT宏逐個註釋。
(4)從工程中移除相關標頭檔案。
二、建立dll匯入類時報警告“QObject: Cannot create children for a parent that is in a different thread.”
1. 錯誤截圖
略
2.錯誤原因分析
這可能是由於工程編譯版本型別與連結庫的型別不一致導致,比如工程當前為debug版本,但連結庫編譯生成版本為release
3.錯誤修改策略
將工程生成版本調整為與連結庫版本一致
三、單例寫法:
原先的單例寫法比較簡單,程式碼大概如下:
static Test&Test::Singleton(){
static Test Instance;
return Instance
}
這樣的單例模式看上去還行,這個也是我之前常用的單例模式。但是這個在COM元件的開發中是不夠安全的,原因也很簡單~因為我們提交給COM元件的IDispatch是指標形式提交的,這個指標有可能會在外部被析構掉(只是提出一種可能,這裡尚未測試和驗證),所以這個單例模式是指標不安全的。
既然如此我們就需要修改一種比較好用且指標安全的單例模式。今天看了下小余的程式碼我感覺還挺好用的,拿過來抄一下:
static NetServer* NetServer::Singleton() {
static QMutex mutext;
static QSharedPointer<NetServer> inst;
if (Q_UNLIKELY(!inst)) {
if (!inst) {
inst.reset(new NetServer());
}
}
return inst.data();
}
這段程式碼就是用的QSharedPointer 智慧指標對單例指標進行維護,這樣只要主程式還在,這個單例的指標就會一直儲存在QSharedPointer內,就不會被析構了,除非你自己提供了一個析構的方法。