天天看點

Effective C++ Item 31 降低檔案間編譯依存關系

經驗:支援”編譯依存性最小化“的一般構想是:相依于聲明式,不要相依于定義式。

基于此構想的兩個手段是 Handle classes 和 Interface classes.

示例:相依于定義式

[cpp] view plain copy

  1. #include <string>  
  2. #include "date.h"  
  3. #include "address.h"  
  4. class Person{  
  5. public:  
  6.     Person(const std::string &name, const Data &birthday, const Address &addr);  
  7.     std::string name() const;  
  8.     std::string birthDate() const;  
  9.     std::string address() const;  
  10.     //...  
  11. private:  
  12.     std::string theName; //實作細目  
  13.     Date theBirthDate;   //實作細目   
  14.     Address theAddress;  //實作細目  
  15. }  

解析:

一旦include 的檔案有了改變,任何使用Person class的檔案應得重新編譯連結

糾正:相依于聲明式。 pointer to implementation,接口與實作分離

[cpp] view plain copy

  1. #include <string>  
  2. #include <memory>  
  3. class PersonImpl;  //Person實作  
  4. class Date;  
  5. class Address;  
  6. class Person{ //Person 接口  
  7. public:  
  8.     Person(const std::string &name, const Date &birthday, const Address &addr);  
  9.     std::string name() const;  
  10.     std::string birthDate() const;  
  11.     std:;string address() const;  
  12.     //...  
  13. private:  
  14.     std::tr1::shared_ptr<PersonImpl> pImpl; //指針,指向實作物;  
  15. }  

解析:

1.如果使用 object references 或 object pointers 可以完成任務,就不要使用 objects。

定義指向類型的references 和 pointers --> 需要類型聲明式

定義類型的 object --> 需要類型定義式

2.如果能夠,盡量以 class 聲明式替換 class 定義式。

#include "datefwd.h" //這個頭檔案内聲明(但未定義)class Date

Date today(); //ok. 不需要Date 的定義式

void clearAppointment(Date d); //ok. 不需要 Date的定義式

void clearAppointment(Date d){} //error. imcomplete type is not allowed.

3.為聲明式和定義式提供不同的頭檔案。可将“并非真正必要之類型定義”與用戶端之間的編譯依存性去除掉。

如。 <iosfwd>内含 iostream 各元件的聲明式,其對應定義則分布在基于不同的頭檔案内,包括<sstream>,

<streambuf>,<fstream>和<iostream>

實作類似 Person 這類 handle classes

方法之一: 将它們所有函數轉交給相應的實作類并由後者完成實際工作。

[cpp] view plain copy

  1. #include "Person.h"  
  2. #include "PersonImpl.h" //實作 Person class 必須 include Person 和 PersonImpl 的聲明式。  
  3.                         //? 不應該 include PersonImpl.cpp, 不然怎麼調用其成員函數  
  4. Person::Person(const std::string &name, const Date &birthday, const Address &addr)  
  5.     :pImpl(new PersonImpl(name, birthday, addr)  
  6. {};  
  7. std::string Person::name() const {  
  8.     return pImpl->name();  
  9. }  

方法二:Interface class。從 Interface class 繼承接口規格,然後實作出接口所覆寫的函數。

[cpp] view plain copy

  1. class Person{  
  2. public:  
  3.     static std::tr1::shared_ptr<Person> //傳回一個tr1::shared_ptr,指向一個新的person,Item 18說這能消除客戶的資源管理責任  
  4.         create(const std::string &name, const Date &birthday, const Address &addr);  
  5.     virtual ~Person();  
  6.     virtual std::string name() const = 0;  
  7.     virtual std::string birthDate() const = 0;  
  8.     virtual std::string address() const = 0;  
  9.     //...  
  10. };  
  11. class RealPerson: public Person{  
  12.     //...  
  13. }  
  14. std::tr1::shared_ptr<Person> Person::create(const std::string &name,  
  15.                                             const Date &birthday,  
  16.                                             const Address &addr)  
  17. {  
  18.     return std::tr1::shared_ptr<Person>(new RealPerson(name, birthday, addr));