SOUI相對於DuiEngine一個重要的變化就是很多模塊變成了一個單獨的DLL。
然后很多情況下用戶可能希望整個產品就是一個EXE,原來DuiEngine提供了LIB編譯模式,此時鏈接LIB模式的DuiEngine就行了。
但是SOUI默認至少Utilities那個模塊是不提供LIB編譯模式的。
utilities之所以默認只提供DLL編譯是因為SString類是由utilities實現的。
字符串是編譯中碰到的最最見的基本對象之一。在運行庫(CRT)動態編譯(MD,MDd)時這不是問題,因為所有模塊的內存分配都是在一個相同的運行庫(CRT)上,這時在不同模塊之間傳遞對象相對簡單。如果項目采用運行庫靜態編譯(MT or MTd),在不同模塊之間傳遞字符串對象是非常困難的,因為一不小心就會發生在A模塊中分配的字符串對象被B模塊釋放。
utilities采用DLL編譯就是為了解決這個字符串對象的跨模塊傳遞。
采用運行庫動態編譯的情況就不說了,這里主要介紹采用靜態庫編譯的CRT的情況。
SOUI中使用的字符串對象采用了一點技巧:每一個String對象中只有一個指針成員變量:
template <class tchar, class tchar_traits> class TStringT { public: typedef tchar _tchar; typedef const _tchar * pctstr; protected: tchar* m_pszData; // pointer to ref counted string data };
雖然TStringT是一個模板類,在SOUI中采用類導出的方式將該模板的兩個特化類導出:
#ifdef UTILITIES_EXPORTS # define EXPIMP_TEMPLATE #else # define EXPIMP_TEMPLATE extern #endif #pragma warning (disable : 4231) EXPIMP_TEMPLATE template class UTILITIES_API TStringT<char, char_traits>; EXPIMP_TEMPLATE template class UTILITIES_API TStringT<wchar_t, wchar_traits>;
通過將string類導出,保證string的所有運行代碼都是在utilities這個模塊內部,這也就保證了string對象的唯一成員變量:
tchar* m_pszData;
的內存分配及釋放固定在utilities這個模塊里。
通過這樣處理,無論用戶定義string是在哪一個模塊,真正的內存管理還是在utilities里,從而使得string對象可以方便的在不同模塊之間傳遞。
比較一下std::string就可以發現,如果使用std::string在不同模塊之間傳遞對象將是非常危險的,因為std::string是模板類,它的代碼將會被編譯到不同的模塊中,也就是說在不同的模塊中調用std::string的成員函數執行的代碼是不一樣的,這樣在A模塊中聲明的string傳遞到B模塊再被B模塊釋放程序就崩潰了。
這就是為什么utilities模塊默認只提供DLL編譯的原因。
知道了原因就好辦了。
對於那些希望整個項目就是一個EXE的情況,直接修改utilities模塊的編譯類型為LIB就行了,因為這種情況下根本不存在跨模塊對象傳遞的問題。