`
tempsitegoogle
  • 浏览: 862197 次
文章分类
社区版块
存档分类
最新评论

c++笔记 降低头文件间的编译依存关系

 
阅读更多
1.编译依存关系
某些时候只修改了某个类的private,结果却有一大堆文件需要编译。问题出在c++并没有把“将接口从实现中分离”这事做好。Class的定义式不只详细叙述了class接口,还包括十足的实现细目。例如:
class Perpson {
public:
    std::string name() const;
    std::string birthData() const;
    std::string address() const;
...
private:
    std::string theName;    //实现细目
    Date theBirthDate;
    Address theAddress;
};
在Person定义文件的最上方很可能存在这样的东西:
#include <string>
#include "date.h"
#include "address.h"
不幸的是,这么一来便是在Person定义文件和其含入文件之间形成了一种编译储存关系。如果这些头文件中有任何一个被改变,或者这些文件文件所做事的其他头文件有任何改变,那么每一个含入Person class的文件就得重新编译。这样的连串编译依存关系会对许多项目造成难以形容的灾难。
考虑加上前置声明,以去掉#include包含的头文件:
class Date;
class Address;
这样做有一个困难,编译器必须在编译期间知道对象的大小。考虑这个:
int main()
{
    int x;
    Person p;
    ...
}
当编译器看到x的定义式,它知道必须分配多少内存才够持有一个int。没问题,每个编译器都知道一个int有多大。当编译器看到p的定义式,它也知道必须分配空间以旋转一个Person,但它如何知道一个Person对象有多大呢?唯一的办法就是询问class定义式。然而如果class定义式可以合法地不列出实现细目(使用前置声明),编译器如何知道该分配多少空间?


2.Handle class
我们可以使用pimpl,“将对象实现细目隐藏于一个指针背后”的游戏。针对Person我们可以这样做:把Person分割为两个class,一个只提供接口,另一个负责实现该接口。负责实现的那个类取名为PersonImpl,Person定义如下:
#include <string>
#include <memory>

class PersonImpl;
class Date;
class Address;

class Person {
public:
    std::string name() const;
    ...
private:
    std::tr1::shared_ptr<PersonImpl> pImpl; //指针,
};
上面的这般设计常被称为pimpl idiom(pimpl 是“pointer to implementation”),像Person这样使用pimpl idiom的class,往往被称为Handle class。这样的设置计之下,Person的客户就完全与Dates等实现细目分离,真正实现了“接口与实现分离”。这个分离的关键在于以“声明的依存性”替换“定义依存性”,其实每一件事都源自这几个简单的设计策略:
1)如果使用object references 或object pointers可以完成任务,就不要使用objects。
2)如果能够,尽量以class声明式替换class定义式。注意,当你声明一个函数而它用到某个class时,你并不需要该class的定义;纵使以by value方式传递该类型的参数(或返回值)亦然:
class Date;                     //class 声明式
Date today();                   //没问题--这里并不需要Date的定义式
void clearAppointments(Date d); 
3)为声明和定义提供不同的头文件


3.Interface class
令Person成为一种特殊的abstract base class,称为Interface class。如下:
class Person {
public:
    static std::tr1::shared_ptr<Person> create();   //使用一个工厂方法创建真实对像
    virtual ~Person();
    virtual std::string name() const = 0;
    virtual std::string birthDate() const = 0;
    virtual std::string address() const = 0;    
}
class RealPerson: public Person {
...
}


static std::tr1::shared_ptr<Person> create {
    return std::tr1::shared_prt<Person>(new RealPerson());
}

注,将工厂方法提出来,放到单独的工厂类中,效果应该更好。


摘自Effective c++


PS:关于pimpl的实现方式
pimpl可以以下面的方式实现:
//Person.h
#include <memory>
class Person
{
public:
Person(void);
~Person(void);
private:
class PersonImpl;
std::tr1::shared_ptr<PersonImpl> pImpl;
};


//Person.cpp
#include "Person.h"
class Person::PersonImpl {


};


Person::Person(void)
{
pImpl = std::tr1::shared_ptr<PersonImpl>(new PersonImpl);
}
Person::~Person(void)
{
}
PersonImpl定义在Person域中,外部不再可见





分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics