我是靠谱客的博主 粗暴睫毛膏,最近开发中收集的这篇文章主要介绍31 将文件的编译依存关系降至最低一、class定义式中包含实现细目二、如何分离接口和实现三、总结,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
一、class定义式中包含实现细目
class Person{
public:
Person(const std::string& name,const Date&birthday, const Address& addr);
std::string name() const;
std::string birthDate() const;
std::string address() const;
...
private:
//实现细目
std::string theName;
Date theBirthDate;
Address theAddress;
};
int main(){
int x;
int Person(params);//编译器编译到此时会对Person分配内存,因此需要知道Person的大小(静态分配内存)
}
当Class的定义式不只详细叙述了class接口,还包括实现细目时,轻微的Class改动都会导致实现细目中相关的依赖重新编译。上述Person的定义中包含了string、Date、Address实现细节。编译时需要对Person文件中的成员变量分配内存,编译器要知道分配多大内存,就需要去查找他们的定义式,因此需要包含成员变量所属类型的定义式,而其定义式中又可能包含其他类型的成员变量,又需要包含其他类型的定义式…如此这样,一个编译单元可能包含了很多的文件,因此这个编译单元有任何改动这些依赖的文件都需要重新编译。
二、如何分离接口和实现
包含class定义式会递归式的增强文件之间的编译依赖关系,下边的两种方式介绍了如何降低文件之间编译的依赖关系:pImp和接口。
2.1 pImpl idiom(pimpl是“pointer同implementation”)
- 设计策略:
- 如果使用object references 或object pointers可以完成任务,就不要使用objects。
可以只靠一个类型声明式就定义出指向该类型的references和pointers;但如果定义某类型的object,就需要用到该类型的定义式。 - 如果能够,尽量以class声明式替换class定义式。
当你声明一个函数而用到某个class时,你并不需要该class的定义;纵使函数以by value方式传递该类型的参数(或返回值)。
- 如果使用object references 或object pointers可以完成任务,就不要使用objects。
- 为声明式和定义式提供不同的头文件。
需要两个头文件,一个用于声明式,一个用于定义式。这两个文件必须保持一致性。程序客户应该总是#include一个声明文件而非前置声明若干函数。程序库坐着也应该提供这两个头文件。如:<iosfwd>内涵iostream各组件的声明式,其对应定义则分布在若干不同的头文件内,包括<sstream>,<streambuf>,<fstream>,<iostream>。 - C++提供关键字export,允许将template声明式和template定义式分割于不同的文件内。但是支持这个关键字的编译器目前非常少。
像Person这样使用pimpl idiom的class,被称为Handle class。另一种制作Handle Class的办法是abstract base class(抽象基类),称为Interface class。见2.2。
2.2 接口
- 为什么接口路能降低文件间的编译依存关系?
接口的目的是详细一一描述derived classes的接口,因此他们通常不带成员变量,也没有构造函数,只有一个virtual析构函数以及一组pure virtual函数,用来叙述整个接口。所以接口中不需要class的定义式,文件的依赖关系变低。 - 接口如何创建对象?
Interface class的客户通过特殊的函数来创建新对象。这样的函数通常称为factory函数或virtual构造函数(构造函数不能为虚函数,此处只是将这种特殊的函数称为virtual构造函数)。这样的函数往往被声明为static,且返回智能指针。
三、总结
- 支持“编译依存性最小化”的一般构想是:相依于声明式,不要相依于定义式。基于此构想的两个手段是Handle classes和Interface classes。
- 程序库头文件应该以“完全且仅有声明式”的形式存在。这种做法不论是否涉及templates都适用。
- Handle class 的成本:
- 成员函数必须通过implementation pointer取得对象数据,每一次访问都增加了一层间接性
- 每一个对象消耗的内存数量必须增加一个implementation pionter的大小。
- implementation pointer必须在Handle class的构造函数内初始化,导致动态分配内存带来的额外开销,以及遭遇bad_alloc异常的可能性。
- Interface classes成本:
- 每次virtual函数调用增加一次间接跳跃成本。
- Interface classes派生的对象必须内含一个vptr。
- 不论Handle classes 或Interface classes,一旦脱离了inline函数都无法有太大作为(?)。
最后
以上就是粗暴睫毛膏为你收集整理的31 将文件的编译依存关系降至最低一、class定义式中包含实现细目二、如何分离接口和实现三、总结的全部内容,希望文章能够帮你解决31 将文件的编译依存关系降至最低一、class定义式中包含实现细目二、如何分离接口和实现三、总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复