如果你正要學習或者對于C++比較上心的朋友可以看看此文,或許能給你意想不到的收獲喔,那閑話不多說了,一起來了解下C++文件的依賴關系。
首先我不給出依存關系的定義,我給出一個例子。
?class Peopel{
?public:
???? People(const std::string & name,const Date& brithday,Image Img)
???? std::string name( ) const;
???? Date birthDate( ) const;
???? Image img( ) const;
???? ...
?private:
???? std::string theName;?????????????? //名字
???? Date theBirthDate;???????????????? //生日
???? Image img;???????????????????????? //圖片
?};
如果編譯器沒有知道類string,Date和Image的定義,class People是無法通過編譯的。一般該定義式是由#include包含的頭文件所提供的,所以一般People上面有這些預處理命令
?
?
? #include
? #include "date.h"
? #inblude "image.h"
?class Peopel{
?public:
???? People(const std::string & name,const Date& brithday,Image Img)
???? std::string name( ) const;
???? Date birthDate( ) const;
???? Image img( ) const;
???? ...
?private:
???? std::string theName;?????????????? //名字
???? Date theBirthDate;???????????????? //生日
???? Image img;???????????????????????? //圖片
?};
那么這樣People定義文件與該三個文件之間就形成了一種編譯依存關系。如果這些頭文件任何一個文件被改變,或這些頭文件所依賴其他頭文件任何改變,那么每一個包含People類的文件就需要重新編譯,使用People類文件也需要重新編譯。想想如果一個項目包含一個上千的文件,每個文件包含其他幾個文件,依次這樣下來,改動一個文件內容,那么就需要幾乎重新編譯整個項目了,這可以說很槽糕了。
?
我們可以進行如下改動
?namespace std {
???? class string;
?}
?class Date;
?class Image;
?class Peopel{
?public:
???? People(const std::string & name,const Date& brithday,Image& Img)
??? std::string name( ) const;
??? Date birthDate( ) const;
??? Image img( ) const;
??? ...
private:
??? std::string theName;??????????????? //名字
??? Date theBirthDate;???????????????? //生日
??? Image img;???????????????????????? //圖片
};
這樣只有People該接口被改變時才會重新編譯,但是這樣有連個問題,第一點string不是class,它是個typedef basic_string string。因此上述前置聲明不正確(附其在stl完全代碼);,正確的前置聲明比較復雜。其實對于標準庫部分,我們僅僅通過#include預處理命令包括進來就可以了。
?
?
?#ifndef __STRING__
?#define __STRING__
?#include
?extern "C++" {
?typedef basic_string string;
?// typedef basic_string wstring;
?} // extern "C++"?
#endif
前置聲明還有一個問題,就是編譯器必須在編譯期間知道對象的大小,以便分配空間。
例如:
?
?
? int main(int argv,char * argc[ ])
??? {
??????? int x;
??????? People p( 參數 );
??????? ...
??? }
當編譯器看到x的定義式,它知道必須分配多少內存,但是看到p定義式就無法知道了。但是如果設置為指針的話,就清楚了,因為指針本身大小編譯器是知道的。
?
?
#include
#include ?
class PeopleImpl;
class Date;
class Image;
class People{
public:
?? People(const std::string & name, const Date& brithday, const Image &Img);
?? std::string name( ) const;
?? Date birthDate( ) const;
?? Imge img( ) const;
?? ...
private:
?? PeopleImpl * pImpl;
}
PeopleImpl包含下面這三個數據,而People的成員變量指針指向這個PeopleImpl,那么現在編譯器通過People定義就知道了其分配空間的大小了,一個指針的大小。
?
?
?public PeopleImpl
?{
???? public:
???????? PeopleImple(...)
???????? ...
???? private:
???????? std::string theName;??????????????? //名字
???????? Date theBirthDate;???????????????? //生日
???????? Image img;???????????????????????? //圖片
}
這樣,People就完全與Date、Imge以及People的實現分離了上面那些類任何修改都不需要重新編譯People文件了。另外這樣寫加強了封裝。這樣也就降低了文件的依存關系。
這里總結下降低依存性方法:
?
?
?
1.如果可以類聲明就不要使用類定義了。
2.將數據通過一個指向該數據的指針表示。
3.為聲明式和定義式提供不同的頭文件。
這兩個文件必須保持一致性,如果有個聲明式被改變了,兩個文件都得改變。因此一般會有一個#include一個聲明文件而不是前置聲明若干函數。
像People這樣定?
?
?#include "People.h"
?#include "PeopleImpl.h"
?People::People(const std::string& name, const Date& brithday, const Image& Img)
?:pImpl(new PersonImpl(name,brithday,addr))
?{ }
?std::string People::name( ) const
?{
???? return pImpl->name( );
?}
而另外一種Handle類寫法是令People成為一種特殊的abstract base class稱為Interface類。看到interface這個關鍵字或許熟悉C#、java的同學可能已經恍然大悟了。這種接口它不帶成員變量,也沒有構造函數,只有一個virtual析構函數,以及一組純虛函數,用來表示整個接口。針對People而寫的interface class看起來是這樣的。
?
?
?class People{
?public:
???? virtual ~People( );
???? virtual std::string name( ) const = 0;
???? virtual Date brithDate( ) const =0;
???? virtual Image address( ) const =0;
???? ...
?};
怎么創建對象呢?它們通常調用一個特殊函數。這樣的函數通常稱為工廠函數或者虛構造函數。它們返回指針指向動態分配所得對象,而該對象支持interface類的接口。
?
?
?? class People {
???? public:
???????? ...
???????? static People* create(const std::string& name,const Date& brithday, const Image& Img);
???? };
支持interface類接口的那個類必須定義出來,而且真正的構造函數必須被調用
?
?
?class RealPeople:public People{
?public:
???? RealPeople(const std::string& name,const Date& birthday,const Image& Img)
???? :theName(name),theBrithDate(brithday),theImg(Img)
?{}
???? virtual ~RealPeople() { }
???? std::string name( ) const;
???? Date birthDate( ) const;
???? Image img( ) const;
?private:
???? std::string theName;
???? Date theBirthDate;
???? Image theImg;
?}
有了RealPeople類,我們People::create可以這樣寫
?
?
?People* People::create(const std::string& name, const Date& birthday, const Image& Img)
?{
???? return static_cast
(new RealPerson(name,birthday,Img));
?}
Handle類與interface類解除了接口和實現之間的耦合關系,從而降低了文件間的編譯依存性。但同時也損耗了一些性能與空間。
以上就是C++文件的依賴關系的介紹,如果大家想了解更多相關內容,請持續關注本站,本站小編將在第一時間為大家帶來更好的經典內容。