做軟體,都希望能夠重用已經存在的代碼,而重用方式大概有以下兩種吧:1. 以代碼的形式複用,也就是直接複製原始碼,這可能是最原始的了吧;2. 以類的形式複用,也就是平常所說的繼承和組合了。而今天,我想說說這第二種代碼複用的方式。
如果是在一個項目中,這沒什麼好說的,因為同在一個項目中,各個類之間彼此都可訪問——大不了就是加個include而已嗎。而如果是想複用其他項目中的類呢?先看一個例子吧(本文環境為Visual C++ 2005):
建立一個空的控制台項目MyClass,並加入兩個檔案(其實就是類Hi的聲明和實現檔案):
Hi.h檔案:
#pragma once
class Hi...{
public:
void hi();
};
Hi.cpp檔案:
#include "Hi.h"
#include <iostream>
using namespace std;
void Hi::hi()...{
cout<<"Hi!"<<endl;
}
為了驗證這個類可以正常運行,再添加一個Run.cpp檔案:
#include "Hi.h"
#include <iostream>
using namespace std;
int main()...{
Hi hi;
hi.hi();
cin.get();
}
運行,可以看到程式向控制台輸出了 Hi! ,說明類Hi是沒有任何問題的。
好,那就可以進行下一步了。現在,我想在另外一個項目中使用類Hi。很簡單,建立了一個空的控制台項目Reuse,添加了檔案UseHi.cpp,如下:
#include "Hi.h"
#include <iostream>
using namespace std;
int main()...{
Hi hi;
hi.hi();
cin.get();
}
大家可以看到,這和MyClass項目中的Run.cpp檔案是一模一樣的。其實也應該這樣,它們不都是匯入"Hi.h"檔案,然後再執行Hi.hi()函數嗎?不過……按F5運行,竟然編譯出錯,提示如下:
----- 已啟動產生: 項目: Reuse, 配置: Debug Win32 ------
正在編譯...
UseHi.cpp
d:/cpp/myobjects/reuse/usehi.cpp(1) : fatal error C1083: 無法開啟包括檔案:“Hi.h”: No such file or directory
組建記錄檔儲存在“file://d:/Cpp/MyObjects/Reuse/Debug/BuildLog.htm”
Reuse - 1 個錯誤,0 個警告
========== 產生: 0 已成功, 1 已失敗, 0 最新, 0 已跳過 ==========
看來是找不到Hi.h檔案。不過也是,include只能是在目前的目錄和系統指定目錄下尋找標頭檔,而我們所要匯入的Hi.h檔案是在我們自己建立的檔案夾中,按理說也找不到它。不過這個倒也好解決:依次點擊“項目——ReUse 屬性——配置屬性——C/C++——常規”,在“附加元件封裝含目錄”中加入Hi.h所在的檔案夾,本例中為D:/Cpp/MyObjects/MyClass。確定,編譯運行。——沒有了剛才的錯誤提示,不過,呵呵~
------ 已啟動產生: 項目: Reuse, 配置: Debug Win32 ------
正在編譯...
UseHi.cpp
正在編譯資源清單...
正在連結...
UseHi.obj : error LNK2019: 無法解析的外部符號 "public: void __thiscall Hi::hi(void)" (?hi@Hi@@QAEXXZZ),該符號在函數 _main 中被引用
D:/Cpp/MyObjects/Reuse/Debug/Reuse.exe : fatal error LNK1120: 1 個無法解析的外部命令
組建記錄檔儲存在“file://d:/Cpp/MyObjects/Reuse/Debug/BuildLog.htm”
Reuse - 2 個錯誤,0 個警告
========== 產生: 0 已成功, 1 已失敗, 0 最新, 0 已跳過 ==========
竟然又出現了其他的錯誤……
而這個錯誤,當時我是拿它一點辦法都沒有,只能是鬱悶的避開它。後來不斷的在網上尋找,才稍微看到了點解決的眉目。現在先把解決辦法寫下來,再談談我的感受吧。
開啟MyClass項目,依次點擊“項目——MyClass 屬性——配置屬性——常規”,將“配置類型”更改為“靜態庫(.lib)”,然後F7(產生解決方案),以建立檔案MyClass.lib。再回到ReUse項目,依次點擊“項目——ReUse 屬性——配置屬性——連結器——常規”,在“附加庫目錄”中加入MyClass.lib所在目錄,本例中為D:/Cpp/MyObjects/MyClass/Debug。還沒完,在“連結器”中找到“輸入”,在“附加依賴項”中加入MyClass.lib,確定,F5,結果……呵呵,成功!
分析:
檔案UseHi.cpp中的 #include "Hi.h" ,只是匯入了標頭檔Hi.h中的內容,也就是匯入了類Hi的聲明。而在項目ReUse中不可能再去到MyClass項目所在檔案夾中去尋找類Hi的實現檔案,所以才會彈出錯誤提示“無法解析的外部符號/命令”。而在MyClass中Run.cpp可以正常啟動並執行原因在於:它匯入了類Hi的聲明後,會從目前的目錄中尋找看有沒有類Hi的實現,而Hi.cpp就是在MyClass項目中,所以才能夠正常將聲明與實現相連結。
所以,按這樣說來,如果在項目ReUse中加入類Hi.h的實現後,即便是不更該項目ReUse的連結器中的預設選項,還是可以正常啟動並執行,呵呵,那就嘗試一下吧。將之前更改的項目ReUse中連結器中的選項都改回預設值,再向項目ReUse中添加檔案Implement.cpp檔案。其中Implement.cpp檔案檔案如下(其實就和Hi.cpp一模一樣):
#include "Hi.h"
#include <iostream>
using namespace std;
void Hi::hi()...{
cout<<"Hi!"<<endl;
}
F5,呵呵,真的是這樣,可以正常運行了。
所以,可以這麼說:如果是include了某個標頭檔,那麼它會在目前的目錄中去找其實現檔案,如果找不到,就只有是“無法解析的外部符號/命令”了,而不會再去別的檔案夾中去搜尋其實現檔案了。而如果我們是用的.lib庫的方式複用的代碼,那麼我們就需要將該.lib庫的路徑加入到在“連結器”的“附加庫目錄”中,並將該庫的名稱加入到“連結器”的“附加依賴項”中。這樣,當我們匯入了某個標頭檔時,就可以去該.lib庫中去尋找其實現檔案了。
呵呵,終於完了,睡覺去~