您的位置:首页 > 编程语言 > C语言/C++

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

2012-04-12 22:04 302 查看
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域中,外部不再可见
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐