MSVC與CRT的恩怨情仇

很久沒有寫程式設計入門知識的相關文章了,這篇文章要來談談程式庫 (Library) 連結,以及關於 MSVC 與 CRT 之間的種種恩怨情仇。

如果你使用的作業系統是 Linux、Mac 或其他非 Windows 平台,你可以忽略這篇文章;如果你使用的作業系統是 Windows 平台,但沒有用 Microsoft Visual Studio C++(以下簡稱為 MSVC)軟體撰寫 C++ 程式的話,這篇文章對你的幫助可能很有限;但如果你的作業系統是 Windows,而且你使用的程式整合開發環境是 MSVC 軟體撰寫 C++ 程式的話,這篇文章應該能夠幫助你釐清一些重要的基礎觀念。

身為程式設計者,在學習程式設計的過程中,你是否曾經遇過某些看起來不知所云的錯誤訊息,卻不知該如何解決?例如當你快快樂樂地寫完程式,並且確認所有的程式碼都能成功通過編譯之後,接著執行「建置方案」(Build Solution) 的步驟,結果卻跑出一堆莫名其妙的錯誤:

LIBCMTD.lib(mlock.obj) : error LNK2005: __lock 已在 MSVCRTD.lib(MSVCR80D.dll) 中定義過了
LIBCMTD.lib(mlock.obj) : error LNK2005: __unlock 已在 MSVCRTD.lib(MSVCR80D.dll) 中定義過了
LIBCMTD.lib(crt0.obj) : error LNK2005: _mainCRTStartup 已在 MSVCRTD.lib(crtexe.obj) 中定義過了

…………

LINK : warning LNK4098: 預設的程式庫 ‘MSVCRTD’ 與其他使用的程式庫衝突,請使用 /NODEFAULTLIB:library
LINK : warning LNK4098: 預設的程式庫 ‘LIBCMTD’ 與其他使用的程式庫衝突,請使用 /NODEFAULTLIB:library
D:\Workspace\CrtLibTest\Debug\CrtLibTest.exe : fatal error LNK1169: 找到有一或多個已定義的符號

以一般的情況來說,如果在你的程式專案中有使用某些由他人所撰寫的第三方程式庫或是開源專案的程式庫,比較容易會發生上述的錯誤狀況。從上述這些看似離奇而令人摸不著頭緒的錯誤訊息中,我們大概可以猜測問題點應該在於 LIBCMTD.lib 與 MSVCRTD.lib 這兩個程式庫身上。但到底什麼是 LIBCMTD.lib 和 MSVCRTD.lib?在我們的程式碼中有使用這些程式庫嗎?

Continue reading

物件導向設計新思維:探索Policy-Based Class Design新視界

Policy-Based Class Design 到底是什麼樣的東西?又能夠用來解決什麼樣的問題?本文將由一個在程式專案中常見的現實案例,帶領讀者進入 Policy-Based Class Design 的全新思維領域。

學習一項新的思維方法或實作技術,最容易的方法就是從需求面出發。所以在介紹 Policy-Based Design 之前,先來看看一個現實中經常會遇到的程式設計案例:假設在處理 Input/Output 功能的系統層,原來存在著 BinaryReader 與 BinaryWriter 兩個類別,分別用來讀取以及寫入二進位制的資料串流。

現在,在新的專案項目中,我們希望能夠利用這兩個既存的 IO 類別,但是又不希望將檔案的讀取與寫入動作,分成 BinaryReader 與 BinaryWriter 兩個不同的類別進行處理。因此,程式設計者需要設計一個新的 IO 管理者類別,將原有的讀取與寫入功能合併在一起,以達到集中操作以及檔案資源管理的功用。

而為了盡量減少對既存程式碼的修改,其中一種設計方法是使新的 IOManager 類別,同時繼承自 BinaryReader 與 BinaryWriter 類別,然後藉由虛擬函式的宣告,修改專案所需的行為程序:

class IOManager
    :
    public BinaryReader,
    public BinaryWriter
{
public:
    virtual void Read(); // 覆寫自 BinaryReader
    virtual void Write(); // 覆寫自 BinaryWriter
}

這樣設計的方法確實能夠在不修改原有類別的前提下,達到 IO 系統介面集中的目的。程式系統的撰寫者無須再同時照料 BinaryReader 與 BinaryWriter 兩個類別,只要一個 IOManager 類別就能夠處理 IO 層面的功能操作。

Continue reading

容器們,奮起吧!—實做 STL Containers 的包裝介面

做為一個程式設計者,在 C++ 的程式設計領域中,我們無可避免的會經常使用到各種 STL 的容器 (Container),包括 vector、map、set、list 等等。熟習這些容器的功能特徵與操作行為並且和它們成為好朋友,是每個 C++ 程式設計者必備的基礎知識。在大多數的情況下,我們都能心滿意足地使用著這些前人的心血結晶,不會產生任何的問題。

然而,如果某天,在現存運行的程式專案中,需要將 STL Container 替換成其他實做方案時怎麼辦?本文中將以 STL Container 中,最常用來儲存與索引資料用的 std::map 容器為例,探討容器包裝介面的必要性、實做目標以及各項實做細節,並且提供完整的程式碼範例下載

Continue reading

商業或自製,引擎大不同!

在前篇文章「遊戲引擎的層級架構 」中,有位訪客的迴響提到一個很關鍵的問題:

國外的某些商業繪圖引擎並沒有提供 Game 相關的編輯器,為何還是有很多公司購買商業繪圖引擎?

一言以蔽之,就是 Robust 與否的問題。

Robust:堅固的、剛健的、結實的、強壯的。

什麼樣的引擎才能稱得上是堅固結實呢?所謂的引擎技術,不僅只是在最新的顯示卡與最好的配備上展現出炫麗奪目的效果,就能夠稱得上是優秀的引擎。除了一般的運行狀況之外,更需要周全地考量各種邊界狀況 (Boundary Condition) 的情形,以及如何進行例外處理 (Exception Handling) 的程序。檔案不存在的情形如何處理?記憶體不足無法配置如何處理?檔案讀取當中發生毀損狀況怎麼辦?有沒有可能傳入空指標 (Pointer) 而造成系統當機?還有很多很多不同面向的問題。

Continue reading

Scripting系統概論與Lua簡介

Lua Programming Language基於我很愛 Lua 這個 Scripting Language(劇本描述語言←有點彆扭的翻譯 Orz)的立場,不先來篇介紹實在說不過去啊。不過在介紹 Lua 出場前,要先把老大哥 Scripting Language 推出來暖暖場才行。

一般的程式語言,在程式碼撰寫完畢後都需要經過編譯 (Compile) 這個步驟,交由程式語言工具的編譯器 (Compiler) 進行語法驗證、程式碼連結與機器碼建立,最後才能產生出所謂的可執行檔 (Executable Files),也就是在 Windows 平台下滑鼠左鍵雙擊就會開始執行的檔案類型。而使用 Scripting Language 編寫好的程式碼,無須經過編譯的步驟就能夠被直譯 (Interpreted) 並且執行。使用 Scripting Language 有點像是使用一組預先定義好的指令集,在直譯器 (Interpreter) 解析到這些指令時,就能夠去執行預先定義好的行為與程式。所以相較於 C/C++ 屬於需要經過編譯才能執行的程式語言 (Compiled Language),Scripting Language 被稱為是一種直譯式的語言 (Interpreted Language)

Continue reading

遊戲引擎的層級架構

遊戲程式的領域中,最常聽到的專有名詞,可以說是非 Game Engine(遊戲引擎)莫屬了。聽起來是個很炫很酷的名詞,但其實遊戲引擎一詞經常被過度泛稱與誤用。所謂的遊戲引擎架構,由低階 (Low-Level) 至高階 (High-Level) 可細分為以下三個層級 (Layer)

Continue reading