Effective C++笔记_条款31将文件间的编译依存关系降至最低
? ? ? 這個章節,讀了兩遍還是不是很清楚,有一種沒法和作者溝通的感覺,看來我還是一個C++的初學者呀。好吧,不多說了,回歸主題,今天的筆記。
? ? ? 章節一開始就弄了個例子來說明文件間的編譯依存關系以及串聯的依存關系。代碼如下:
1 #include<string> 2 #include"date.h" 3 #include"address.h" 4 class Person { 5 public: 6 Person(const std::string& name, const Date& birthday, const Address& addr); 7 std::string name() const; 8 std::string birthDate() const; 9 std::string address() const; 10 //... 11 private: 12 /*實現條目*/ 13 std::string theName; 14 Date theBirthDate; 15 Address theAddress; 16 };1.Person 定義文件和其含入文件之間形成:編譯依存關系,這些頭文件有任何一個被改變,?或這些頭文件所依賴的其他頭文件有任何改變,那么每一個含有Person class的文件就得重新編譯,任何使用Person class的文件也必須編譯:連串編譯依存關系。
2. 為了解決上述問題,可以把Person分割為兩個classes,一個只提供接口(Person),另一個負責實現該接口(PersonImpl)。具體示例代碼如下:
1 // Person 接口 2 #include <string> 3 #include <memory> 4 5 // 前置聲明 6 class PersonImpl; // Handle classes 7 class Date; 8 class Address; 9 10 class Person { 11 public: 12 Person(const std::string& name, const Date& birthday, const Address& addr); 13 std::string name() const; 14 std::string birthDate() const; 15 std::string address() const; 16 //... 17 private: 18 // pimpl idiom(pointer to implementation) 19 std::tr1::shared_ptr<PersonImpl> pImpl; // 指針,指向實現物,shared_ptr 在memory頭文件 20 }; 3.編譯依存性最小化的本質:現實中讓頭文件盡可能自我滿足,萬一做不到,則讓它與其他文件內的聲明式(而非定義式)相依。
(1)如果使用object reference或object pointers可以完成任務,就不要使用objects。
(2)如果能夠,盡量以class聲明式替換class定義式。
(3)為聲明式和定義式提供不同的頭文件。
4.方案二:Interface class ,令Person成為一種特殊的abstract base class
? Interface class:
(1) ? 目的:詳細一一描述derived classes的接口
?(2)通常不帶成員變量,也沒有構造函數,只有一個virtual析構函數以及一組pure virtual 函數,用來敘述整個接口。
? ? ? Interface class 的客戶,通常調用一個特殊函數(factory函數或virtual構造函數):扮演“真正將被具現化”的那個derived classes的構造函數角色。它們返回指針(智能指針),指向動態分配所得對象,而該對象支持Interface class的接口。它們通常被聲明為static。
1 class Person { 2 //... 3 static std::tr1::shared_ptr<Person> 4 create(const std::string& name, const Date& birthday, const Address& addr); 5 };? ??支持Interface class 接口的那個具體類必須被定義出來,而且真正的構造函數必須被調用。一切都在virtual構造函數實現碼所在的文件中。
1 class RealPerson: public Person { 2 public: 3 RealPerson(const std::string& name, const Date& birthday, const Address& addr) 4 :theName(name), theBirthDate(birthday), theAddress(addr) 5 {} 6 virtual ~RealPerson() {} 7 std::string name() const; 8 std::string birthDate() const; 9 std::string address() const; 10 //... 11 private: 12 /*實現條目*/ 13 std::string theName; 14 Date theBirthDate; 15 Address theAddress; 16 17 }; 18 19 std::tr1::shared_ptr<Person> Person::create(const std::string& name, 20 const Date& birthday, 21 const Address& addr) 22 { 23 return std::tr1::shared_ptr<Person>(new RealPerson(name, birthday, addr)); 24 }RealPerson示范實現了Interface class的兩個常見機制:
(1)從Interface class繼承接口規格,然后實現出接口所覆蓋的函數.
(2)實現法涉及多重繼承。
5.Handle classes 和Interface classes解除了接口和實現之間的耦合關系,從而降低文件間的編譯依存性。但是存在的缺點是:它使你在運行期喪失若干速度,又讓你為每個對象超額付出若干內存。
具體如下:
? ? ?在Handle classes身上,成員函數必須通過implementation pointer 取得對象數據。那會為每一次
訪問增加一層間接性。而每一個對象消耗的內存數量必須增加implmentation pointer的大小。最后,
implementation pointer必須初始化。指向一個動態分配得來的implementation object,所以你
將蒙受因動態內存分配(及后的釋放動作)而來的額外開銷,以及遭遇bad_alloc異常(內存不足)
的可能性。
? ? ?Interface classes,由于每個函數都是virtual,所以你必須為每次函數調用付出一個間接跳躍
成本。此外Interface class派生的對象必須內含一個vptr,這個指針可能會增加放對象所需的內存
數量---實際取決于這個對象除了interface class之外是否還有其他virtual函數來源。
------------------------------------------------------------------------------------------------------------------------------------------
總結:
?支持“編譯依存性最小化”的一般構想:相依于聲明式,不要相依與定義式。基于次構想的兩個手段是Handle classes和Interface classes。
?程序庫頭文件應該以“完全且僅有聲明式”的形式存在。這種做法不論是否涉及templates都適用。
?
聲明:全文文字都是來源《Effective C++》 3th。這里所寫都是自己的讀書的時候梳理做的筆記罷了。希望對他人有用,那是我的榮幸。
posted on 2015-03-23 20:28?CloudFeng 閱讀(...) 評論(...) 編輯 收藏轉載于:https://www.cnblogs.com/cloudfeng/p/4360909.html
總結
以上是生活随笔為你收集整理的Effective C++笔记_条款31将文件间的编译依存关系降至最低的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (9)MSP430F5529 定时器Ti
- 下一篇: 如果某一运行的服务在/var/lock/