引入
考虑这样一个接口类:
class Date;
class Person{
public:
Person(const string& name, const Date& birthday);
const string name() const;
const string birthDate() const;
private:
string theName;
Date birthDate;
};
客户将这样的接口(常作为“person.h" 头文件)引入自己的代码当中。如果对person类的 private 成员做了修改,那么客户的全部代码都需要重新编译。这种代价通常很大,需要避免。
分析
这一问题的形成,根源在于 person 作为接口类却包含了具体的实现细节(private部分)。
为什么person 作为接口类却要包含了具体的实现细节呢?
因为编译器必须在编译期间知道对象的大小。如果Person类中不指名实现细目,编译器无法知道一个person对象的具体大小。
所以问题的解决方法就是,将接口类与实现类分开实现。具体来说有两种方法:
- handle class
- interface class
代码
https://gitee.com/niushifu/item31
内含handle与interface两种手法的文件组织形式。
handle class
接口类与实现类分开实现,接口类包含实现类的指针,接口类与实现类具有相同的public接口。
class PersonImpl{ //作为”PersonImpl.h"
public:
PersonImpl(const string& name, const Date& birthday) : theBirthday(birthday), theName(name) {}
const string name() const;
const string birthDate() const;
private:
Date theBirthday;
string theName;
};
class Person{ //作为"Person.h"
public:
Person(const string& name, const Date& birthday) : pImpl(new PersonImpl(name, birthday)){}
const string name() const;
const string birthDate() const;
private:
PersonImpl* pImpl;
};
使用这种手法,客户使用person类时只需要引入“person.h"头文件,personImpl类的改变不需要客户代码重新编译,只需重新连接即可。
interface class
接口类作为实现类的基类,用户通过基类指针实现多态。
class Person {
public:
virtual ~Person() {};
virtual const string name() const = 0;
virtual const string birthDate() const = 0;
static Person* create(const string& name, const string& birthday);
};
class RealPerson : public Person {
public:
RealPerson(const string& name, const string& birthday);
const string name() const override;
const string birthDate() const override;
private:
string theBirthday;
string theName;
};
Person* Person::create(const string& name, const string& birthday)
{
return new RealPerson(name, birthday);
}
设计策略
如果使用引用和指针就可以完成任务,就不要使用对象本身:引用与指针只需要类型的声明式,对象本身需要类型的定义式。换句话说,如果使用声明式可以完成任务,就不要使用定义式。