天天看点

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));