問題描述
有時我們在 Visual C++ 中編譯 wxWigets 的程式時,會出現類似下面的錯誤訊息,導致連結失敗:
1>BoardManager.obj: error LNK2019: unresolved external symbol "public: void __thiscallwxStringData::Free(void)" (?Free@wxStringData@@QAEXXZ) referenced infunction "public: void __thiscall wxStringData::Unlock(void)"(?Unlock@wxStringData@@QAEXXZ)
1>MyApp.obj :error LNK2001: unresolved external symbol "public: void __thiscallwxStringData::Free(void)" (?Free@wxStringData@@QAEXXZ)
1>MyFrame.obj :error LNK2001: unresolved external symbol "public: void __thiscallwxStringData::Free(void)" (?Free@wxStringData@@QAEXXZ)
1>ViewerDlg.obj: error LNK2001: unresolved external symbol "public: void __thiscallwxStringData::Free(void)" (?Free@wxStringData@@QAEXXZ)
直接從錯誤訊息字面上來解釋的話是說:「連結器在連結時找不到 void __thiscall wxStringData::Free(void) 的定義」。這個現象的主因是:wxWidgets 在使用 static run-timelibrary(意即非 DLL)時,會定義並使用 wxStringData::Free() 這個函式;而當使用 dynamic run-time library(意即 DLL)時,這個函式就沒有定義,也沒有用到了。(至於為什麼會有這樣的差別,請參閱後面的詳細解說)
因此,若是我們在一開始編譯 wxWidgets 函式庫時,"RuntimeLibrary" 這個選項使用"Multi-threaded (Debug) DLL",而編譯應用程式時使用 "Multi-threaded (Debug)"(沒有 DLL)的話,就會發生這個問題。
解決方法
1. 在編譯應用程式的時候,選用 "Multi-threaded DLL (/MD)"(若要編譯 Release 版)或是"Multi-threaded Debug DLL (/MDd)"(若要編譯 Debug 版)。
這麼做的缺點是:編譯出來的執行檔無法在「未安裝 Visual C++ 的 Cruntime 函式庫」的電腦上執行。若是你想要 release 執行檔給其他的使用者,你必須同時幫他們準備Visual C++ 的 Cruntime 函式庫。
2. 使用 "Multi-threaded (Debug) DLL" 選項,重新編譯 wxWidgets 函式庫。詳細方法請參閱這篇。
這麼做的缺點是執行檔會比較大,同時 wxString 相關的記憶體管理也會比較沒有效率。
深入探討
wxString 這個類別內部使用了wxStringData 這個結構儲存字串的內容。wxStringData 提供了 Lock() 和 Unlock() 這組函式,實作 reference counting。當 wxStringData::Unlock() 被呼叫,而且 counter 減到 0 的時候,wxStringData 就會把配置的記憶體釋放掉(呼叫free() 函式)。
為了增進效率,wxStringData::Unlock() 是以行內 (inline) 的方式實作在 wx/string.h 裡面的。因此當我們在編譯程式的時候,Unlock()的程式碼會直接產生在我們撰寫的程式中,包括釋放記憶體的相關程式碼;而配置記憶體相關的程式碼則不是以行內方式實作,因此會被包含在 wxWidgets 函式庫中。
當我們使用 Visual C++ 的時候,若是我們直接使用靜態連結的C run-time 函式庫(選用 "Multi-threaded DEBUG" 或是 "Multi-threaded"),記憶體配置和釋放必須在同一個 DLL 內完成(詳細的原因我找不到,不過我猜是為了避免連結到不同版本的配置/釋放函式)。從前段可知:若是我們仍將呼叫 free() 的程式碼放在 wxStringData::Unlock() 中的話,就會發生「配置和釋放在不同的 DLL」的問題。wxWidgets 解決這個問題的方法,另外實作一個「非行內」的wxStringData::Free(),裡面呼叫 free(),讓配置和釋放在同一個 DLL 中。但另一方面又不希望影響到其他設定下的效率,因此這個方法只有「在 Visual C++ 下,使用 Multi-threaded,同時不使用 DLL」的時候才採用。相關程式碼如下:
//------------------
// string.h
//------------------
#if defined(__VISUALC__) && defined(_MT) && !defined(_DLL)
void Unlock() { if ( !IsEmpty() && --nRefs == 0) Free(); }
void Free();
#else
void Unlock() { if ( !IsEmpty() && --nRefs == 0) free(this); }
#endif
//------------------
// string.cpp
//------------------
#if defined(__VISUALC__) && defined(_MT) && !defined(_DLL)
void wxStringData::Free()
{
free(this);
}
#endif
現在回到我們原本的問題上。當我們選用 "Multi-threadedDLL" 編譯wxWidgets 時,因為此時 _DLL 被定義了,所以 string.cpp 中的 wxStringData::Free() 不會被編譯進去。因此,當我們選用"Multi-threaded" 編譯我們的應用程式時,就會找不到 wxStringData::Free() 的實作碼了。
參考資料
- Changeset 20755 of wxWidgets Trac
- include/wx/string.h
- src/common/string.cpp
From: http://www.cclo.idv.tw/blogs/wxwidgets/2008/12/wxstringdatafree.html
本文详细解释了在使用Visual C++编译wxWidgets时遇到的链接错误问题,并提供了两种解决方案:一是选择合适的链接选项来编译应用,二是重新编译wxWidgets库以匹配应用的链接需求。深入探讨了wxString类内部的内存管理机制,以及为何会出现此链接错误。
4868

被折叠的 条评论
为什么被折叠?



