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( 
intchar* );
        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 高兴 阅读(347) 评论(0)  编辑 收藏 引用 所属分类: GotW


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2012年2月>
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910

统计

常用链接

留言簿

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜