happia
GotW #7:编译时依赖
原文:
http://www.gotw.ca/gotw/007.htm
难度:7/10
很多程序员会#include尽可能多的头文件,你呢?本节就是研究这个问题的。
问题:
[警告:这道题比看起来要难得多。仔细看题!]
很多程序员会#include尽可能多的头文件,这会严重增加编译时间,尤其是当一个头文件里包含了很多的头文件。
在接下来的头文件中,哪些#include语句可以放心的删掉,而不出任何问题?第二,哪些#include语句可以通过稍微修改代码而去掉?(你不能修改类X和Y的公共界面,就是说,你所做的一切都不能影响客户代码。)
//
gotw007.h (implementation file is gotw007.cpp)
//
#include
"
a.h
"
//
class A
#include
"
b.h
"
//
class B
#include
"
c.h
"
//
class C
#include
"
d.h
"
//
class D
//
(note: only A and C have virtual functions)
#include
<
iostream
>
#include
<
ostream
>
#include
<
sstream
>
#include
<
list
>
#include
<
string
>
class
X :
public
A
{
public
:
X (
const
C
&
);
D Function1(
int
,
char
*
);
D Function1(
int
, C );
B
&
Function2( B );
void
Function3( std::wostringstream
&
);
std::ostream
&
print( std::ostream
&
)
const
;
private
:
std::
string
name_;
std::list
<
C
>
clist_;
D d_;
}
;
std::ostream
&
operator
<<
( std::ostream
&
os,
const
X
&
x )
{
return
x.print(os); }
class
Y :
private
B
{
public
:
C Function4( A );
private
:
std::list
<
std::wostringstream
*>
alist_;
}
;
1.立刻删除的有:
-iostream,因为尽管用到了stream但没用到iostream的特殊东西。
-ostream和sstream,因为作参数或返回类型只需要前置式声明就够了,所以只需要包含iosfwd头文件。(如果不懂这句话,自己看看iosfwd的内容就知道了。)注意,没有stringfwd或者listfwd头文件,iosfwd的目的是向后兼容。
不能立刻删除的有:
-a.h,因为A是X的基类。
-b.h,因为B是Y的基类。
-c.h,因为现在很多编译器在list<C>时需要看到C的定义(这些编译器应该改掉这个问题)
-d.h,list,string。因为X要知道D和string的大小,X和Y需要知道list的大小。
开始考虑通过隐藏X和Y的实现细节来减少#include:
2.使用pimpl_就能删掉d.h,list和string(就是把private部分用一个指向前置声明的实际对象的指针来代替),因为这样X和Y不需要知道D、list和string的大小。这招也能搞定c.h,因为包括X::clist_中在内的所有C都只是参数或者返回类型。
要点:inline operator<<仍将是inline,并且使用ostream,即使ostream并没有定义。这是由于调用成员函数只需要它的定义,而当你只用到了对象,但只拿它来当成调用其他函数时的参数时,不需要它的定义。
最后,考虑还能怎么改改
3.注意到B是Y的private基类,而B中有虚函数这个现象,我们可以去掉b.h。使用private继承的唯一理由是要去覆盖虚函数。所以,与其private继承B,不如增加一个B对象作为Y的成员。要去掉b.h,这个成员要放在Y的隐藏pimpl_部分。
[指导意见]:用pimpl_(指向实施的指针)来隔离实现细节。
摘自GotW编码标准:
封装和隔离;
在类声明中避免露出private成员。
用一个不透明的指针,类似struct Xxxlmpl *pimpl_去储存private成员(包括状态变量和成员函数)。例如:class Map{private:struct Maplmpl* pimpl_;}(Lakos96:398-405;Meyers92:111-116;Murray93:72-74)
4.对于a.h我们无能为力。因为A是public基类,而且很可能会用到“是A”的关系,因为A有虚函数。然而,注意到X和Y是完全无关的,我们至少还可以做点事情,把X和Y的定义分到两个头文件里(让现在的头文件包含了x.h和y.h)。这样,因为y只把A用作函数的参数,这不需要A的定义,所以至少y.h不需要包含a.h。
整理完后,给出修改后的版本。
//
---------------------------------------------------------------
//
new file x.h: only TWO includes!
//
#include
"
a.h
"
//
class A
#include
<
iosfwd
>
class
C;
class
D;
class
X :
public
A
{
public
:
X (
const
C
&
);
D Function1(
int
,
char
*
);
D Function1(
int
, C );
B
&
Function2( B );
void
Function3( std::wostringstream
&
);
std::ostream
&
print( std::ostream
&
)
const
;
private
:
class
XImpl
*
pimpl_;
}
;
inline std::ostream
&
operator
<<
( std::ostream
&
os,
const
X
&
x )
{
return
x.print(os); }
//
NOTE: this does NOT require ostream's definition!
//
---------------------------------------------------------------
//
new file y.h: ZERO includes!
//
class
A;
class
C;
class
Y
{
public
:
C Function4( A );
private
:
class
YImpl
*
pimpl_;
}
;
//
---------------------------------------------------------------
//
gotw007.h is now just a compatibility stub with two lines, and
//
pulls in only TWO extra secondary includes (through x.h)
//
#include
"
x.h
"
#include
"
y.h
"
//
---------------------------------------------------------------
//
new structures in gotw007.cpp
note that the impl objects
//
will be new'd by the X/Y ctors and delete'd by the X/Y dtors
//
and X/Y member functions will access the data through their
//
pimpl_ pointers
//
struct
XImpl
//
yes, this can be called "struct" even
{
//
though the forward-decl says "class"
std::
string
name_;
std::list
<
C
>
clist_;
D d_;
}
struct
YImpl
{
std::list
<
std::wostringstream
*>
alist_;
B b_;
}
这样,X的客户只包含了a.h和iosfwd。Y的客户只包含了a.h和iosfwd。如果将来要用y.h代替gotw007.h,也不用再去包含什么文件了。步子迈的真大!(还没扯着蛋...)
posted on 2012-02-22 20:47
高兴
阅读(405)
评论(0)
编辑
收藏
引用
所属分类:
GotW
只有注册用户
登录
后才能发表评论。
【推荐】100%开源!大型工业跨平台软件C++源码提供,建模,组态!
相关文章:
GotW #9: 内存管理 上部
GotW #8:竞态:异常安全
GotW #7:编译时依赖
GotW #6: const的正确用法
GotW #5: 覆盖虚函数
GotW #4 类技术
Gotw#3 使用标准库,或者重访问临时对象
GotW #2 临时对象
GotW #1 变量初始化
网站导航:
博客园
IT新闻
BlogJava
知识库
博问
管理
导航
C++博客
首页
新随笔
联系
聚合
管理
<
2012年2月
>
日
一
二
三
四
五
六
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1
2
3
4
5
6
7
8
9
10
统计
随笔 - 10
文章 - 0
评论 - 0
引用 - 0
常用链接
我的随笔
我的评论
我参与的随笔
留言簿
给我留言
查看公开留言
查看私人留言
随笔分类
GotW(9)
(rss)
随笔档案
2012年2月 (10)
搜索
最新评论
阅读排行榜
1. 旧文新译:Herb Sutter纪念Dennis Ritchie(433)
2. GotW #7:编译时依赖(405)
3. GotW #1 变量初始化(296)
4. GotW #9: 内存管理 上部(262)
5. GotW #5: 覆盖虚函数(234)
评论排行榜
1. GotW #1 变量初始化(0)
2. GotW #2 临时对象(0)
3. Gotw#3 使用标准库,或者重访问临时对象(0)
4. GotW #4 类技术(0)
5. GotW #5: 覆盖虚函数(0)