CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
山寨机中的战斗机! 程序优化工程师到底对IT界有没有贡献
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  C/C++ >  C++ 语言

关于effective c++中用句柄类降低编译依赖型,百思不得其解

楼主khalidwind(追风浪子)2006-02-26 21:41:22 在 C/C++ / C++ 语言 提问

比如。  
  Person类用句柄类  
   
  Person.h  
  #ifndef   PERSON_H  
  #define   PERSON_H  
  class   Mystring;  
  class   PersonImpl;  
  class   Person  
  {  
  public:  
  Person();  
  Mystring   name()   const;  
  private:  
  PersonImpl   *impl;  
  };  
  #endif  
  ------------------------------------  
  PersonImple.h  
  #ifndef   PERSONIMPL_H  
  #define   PERSONIMPL_H  
  class   Mystring;  
  class   PersonImpl  
  {  
  public:  
  Mystring   name()   const;  
  private:  
  Mystring   name_;  
  };  
  #endif  
  ------------------------------------  
  PersonImple.cpp  
  #include   "String.h"  
  #include   "PersonImpl.h"  
   
  Mystring   PersonImpl::name()   const  
  {  
  return   name_;  
  }  
  ------------------------------------  
  String.h  
  #ifndef   STRING_H  
  #define   STRING_H  
   
  class   Mystring  
  {  
  public:  
  Mystring();  
  };  
  #endif  
  这是不是就是作者所说的意思呢。可是我如果使用Person类还是会报出编译错误,说没有定义Mystring类啊。  
  因为Person类的实现cpp依然要include   PersonImpl.h,依然需要知道Mystring类啊。 问题点数:100、回复次数:19Top

1 楼qhfu(改个名字)回复于 2006-02-26 23:12:03 得分 0

指针的话,只要加一个前置声明就可以了,   只需要知道类的声明不需要类的定义,所以就不需要#include   *.h   文件了。类可以声明多次,但是只能定义一次  
  Mystring   name()   const;//这里是返回一个对象,因此需要#include   *.hTop

2 楼ox_thedarkness()回复于 2006-02-26 23:21:45 得分 100

-     -   很简单阿。为了更明确目的,我们把PersonImpl*   换成void*  
  你看下面的头文件:  
   
  class   MyClass{  
      void*   pImp;  
  public:  
      MyClass();  
      func();  
  };  
   
  头文件仅有这些外部接口。   很明显,MyClass   的创建和   func调用可以完成,因为MyClass的大小为4字节,   函数调用则只需要外部函数调用占位符。   连接的时候,他会寻找   obj文件并且产生真正的调用地址。  
   
  又因为,它不包含实现,所以你无论如何修改实现,都只影响这个类对应的obj。  
   
  再看实现。我们有一个   void*   可以指向任何动态创建的东西,我们把它指向实现文件中自定义的类实体;   所有函数都可以自己定义...   你想怎么做都可以...   还缺什么呢?Top

3 楼ox_thedarkness()回复于 2006-02-26 23:53:45 得分 0

-     -b    
  作者说的是,  
   
  假如不这么做,数据成员都是类的直接成员,比如下面的实现:  
   
  class   Person{  
  public:  
  Person();  
  Mystring   name()   const;  
  private:  
  Mystring   name_;  
  };  
   
  当你决定往里面增加一个成员   int   id   ,那么就需要修改   Person.h   。   这会引起所有引用(以及间接引用)   Person.h   的   cpp   文件重新编译成   obj,然后连接。   可以想象,一个比较底层的类,搞不好会令所有   cpp   都重新连接。    
   
   
  而用作者的最终版本,Person的实现全部在   PersonImple   和   Person.cpp   中,所以修改其内部,比如增加一个int   之类时候,都不需要修改   Person.h,只需要编译   Person.cpp,然后连接即可。    
   
  只有修改其外部接口,即其成员方法的时候,才需要修改.h,引起全部编译。  
   
   
  ===============================================================  
  而   String,   他是返回值好不好,   你的main当然要包含了。Top

4 楼xiayuxia(LP_ME)回复于 2006-02-27 07:55:45 得分 0

指针只要   声明就可以了的啊     ;  
   
  Top

5 楼xiayuxia(LP_ME)回复于 2006-02-27 08:03:54 得分 0

PersonImple.h  
  里面还是要有   String.h的啊      
  这一节我也看过   ,只是没有去   实践下   ;Top

6 楼Mephisto_76((望美人如梦))回复于 2006-02-27 09:03:58 得分 0

前向声明只有在不需要知道对象内存布局时才能有用。你这里的属性Mystring   name_;定义使得编译器在编译时需要知道Mystring的定义,所以需要包含头文件,如果改用MyString*   name_;  
  并且Mystring   name()   const;也改成Mystring*   name()   const;且头文件中不引用Mystring的任何方法和属性时就可以了。Top

7 楼ugg(逸学堂(exuetang.net))回复于 2006-02-27 09:30:44 得分 0

文件编译是,如果把包含文件放入  
  .h文件中,会增加编译负担(因为编译器从.h文件开始编译)。  
  所以需要把.h文件放入cpp文件中,这样可以加速编译时间,并且降低编译  
  依赖型。  
   
  ~~~~~~~~~~~  
  当在头文件中.h需要使用一个类时,  
  可以  
  class   AA;//   声明一个类  
  但是必须在  
  .cpp文件中包括这个类的定义,即声明文件。  
   
  这样才可以达到上面的目的  
  Top

8 楼khalidwind(追风浪子)回复于 2006-02-27 09:59:02 得分 0

感谢大家的回复。  
  首先,我将作者的思路整理一下,大家看对不对。  
  要解决的问题是:在Person.h中不用include那么多辅助类(比如Mystring)的头文件,而采用前向声明的办法。这样当Mystring的头文件改动的时候,不需要编译Person.cpp。  
  但这样出现问题,就是在定义一个实例的时候,无法确定该实例的大小。所以在private中用一个PersonImpl指针代替具体的实现细节。  
  作者是这个意思吧?如果是这样的话那ox_thedarkness()   的解释就不对了。Person变成句柄类当然可以使得所有inclue   Person.h的CPP不需要重新编译。但是任何Mystring.h的变动仍然会引起Person.cpp的重新编译啊。这并没有解决问题啊。更何况该如何实现PersonImpl类呢。  
  事实上,如果PersonImple.h不去include   Mystring.h的话还是不能通过编译的。  
  而PersonImple.h   引用了Mystring.h。   Person.cpp必然要include   PersonImpl.h,这不还是一样吗,还是相当于Person.cpp   include了   Mystring.h啊。  
   
  MD,一会中文一会英文,累!  
  其实我倒觉得Mephisto_76((望美人如梦))   的说法是对的,可是太麻烦了。也不是作者的代码。难道不能迷信大师?  
   
  另外,关于返回一个对象的时候是否需要知道这个类的实现,请看此节中的一句话  
  --------------  
  因为在声明一个函数时,如果用到某个类,是绝对不需要这个类的定义的,即使函数是通过传值来传递和返回这个类:  
   
      class   Date;                                         //   类的声明  
      Date   returnADate();                         //   正确   ----   不需要Date的定义  
  ---------------  
  这是作者的原话。  
  Top

9 楼xiayuxia(LP_ME)回复于 2006-02-27 11:10:58 得分 0

作者的原话我   看   过      
  马上   实践下   看看Top

10 楼YufengShi(浪子)回复于 2006-02-27 11:40:23 得分 0

Person.cpp在源代码一级依赖于Mystring  
  则Mystring有任何变化,Person.cpp一定要重新编译的.  
  除非用COM,在二进制一级使用Mystring.Top

11 楼ox_thedarkness()回复于 2006-02-27 12:24:28 得分 0

-       __       -b    
   
  我手头的是2nd,   侯捷中译版,华工出版社。  
   
   
  1   作者只是演示一种方法,降低文件依赖性。降低,不是去掉。你要是头文件都改了,其他文件可能不需要重新编译么?   所以他建议1   头文件尽量不要放实现。   2   头文件尽量不   include   头文件。  
   
  2   他演示了,降低   Person   的用户与其实现的编译依赖,以及降低Person   所使用的类与其依赖性。  
   
  3   作者用的   std::string,   当然你可以用你的   MyString。  
   
  4   作者说,如果不依赖内部实现,一个前部声明就可以连接了。但是最终文件必须包含所有头文件。  
   
  下面是   P145   上部的原文摘录:  
   
  class   string;       //   针对   string   型别的“观念性”前置声明  
                                  //   条款   49   会有更清楚地说明  
                                  //   [摘注:即,这样声明是错误的,string不是class   而是模板,49正确声明解释]  
  class   Date;  
  class   Address;  
  class   Country;  
  class   Person{  
  public:  
      Person(   const   string&   name,   const   Data&   birthday,  
                      const   Address&   addr,   const   Country&   countery   );  
    //....  
  }  
   
   
  但是,如何使用呢?   作者在P147和   P148做了解释:  
   
      “如果你奇怪为什么...不需要   Data的定义,我告诉你,其实没那么神奇。当你调用那些函数时,Data的定义必须可见才行。...”  
      “不要再在文件中再#include   其他头文件,除非你的头文件不这样就无法编译。你应该尽可能手动声明你需要的classes,把   #include   其他头文件(从而使整个程序能够编译)的责任让给   clients....   ”  
   
   
  -     -   明白了么?  
   
  顺便说一句,如果你真地认为找到了错误,前言中作者这么说:  
      “因此,如果有人挑出本书的任何错误并告诉我   ——   不论是技术、文法、错别字或其他任何东西   ——   我将在本书重新印刷的时候,把第一位挑出错误的读者大名加到致谢名单中。  
        ...  
        ...或者传送电子邮件到   ec++@awl.com。  
  ”  
   
  什么叫大师?   海纳百川,有容乃大。Top

12 楼khalidwind(追风浪子)回复于 2006-02-27 14:34:14 得分 0

谢谢楼上。但我还是不大明白:(  
  也许作者只是演义一个方法,但起码这个方法要弄通吧。  
  我就是不理解,Person.cpp肯定需要PersonImpl的实现,所以Person.cpp必须包含PerseonImpl.h。但是PersonImpl又必须包含Mystring的实现细节。Person.cpp如果不包含Mystring.h的话,如何能通过编译呢?  
  作者想达到降低编译依赖性的目的,那这个被降低的依赖性到底是哪个文件和哪个文件的依赖型呢?Top

13 楼ericqxg007(还有很多东西要学(卡卡一米阳光))回复于 2006-02-27 14:56:27 得分 0

mark       我也不大懂Top

14 楼ox_thedarkness()回复于 2006-02-27 17:25:27 得分 0

降低的是   Person.h   和其他文件的依赖性。  
   
  想象你们是一个20人开发的项目,   你负责数据查询模块,   你左边这位负责数据结构模块,他管   Person   的实现,即:他提供两个文件:  
   
    Person.h     (包括所有对外接口,即   protected、   public   方法等。但是数据方面只包含一个指针。接口是你们开会讨论的结果,不允许修改)  
    Person.   obj  
   
   
  你根本不知道(   当然,既然他坐在你旁边,你也许听说了;说不定他还找你咨询技术问题呢?   ),也不需要知道   PersonImpl   。   你只需要知道,   Person::name   返回一个   MyString   表示他的名字。   而且你知道Person.h没有   include   MyString。   你需要自己   #include   <MyString.h>。  
   
   
   
  现在,只要   Person   的接口不变   ——   除非下次开会,否则有什么会变呢?     他只需要提交新的   Person.   obj   即可。   他怎么修改   PersonImpl   和你有什么关系呢?  
   
   
   
  -       -   现在假如,你左边那位都不这么做,   把所有数据成员写在对外的头文件里面。   好嘛。   他决定把给内部的数据改为一个智能指针。   你不知道这么回事,但是当你编译   exe的时候,   20个人(他们都用到了   Person.h   )的   cpp   都开始重新编译生成   obj   拉....  
   
   
  Top

15 楼ox_thedarkness()回复于 2006-02-27 17:37:59 得分 0

继续看看这个模式,你不是说   MyString   的实现么?  
   
  假如你后面那位短发效率狂、数据结构狂负责维护   MyString   (以及一大堆其他底层数据,比如你们自己的Vector   ),他同样使用这种技术:   他提供    
   
  MyString.h   和   .obj  
  Vector.h   和   .obj  
   
   
  你维护自己的   Search.h   和   Search.obj  
  你们的头文件中都不包含其他   .h   的引用。  
   
   
  现在,他如何修改   MyString   的实现和你同样没关系,只要接口无需修改   ——   你的   obj   只需要他们的.h   就可以生成。Top

16 楼pongba(刘未鹏|http://blog.csdn.net/pongba)回复于 2006-02-27 19:48:48 得分 0

弄清楚接口依赖跟实现依赖之间的关系。pImpl模式是为了解开实现依赖而不是接口依赖。  
  ox_thedarkness()说的是对的。Top

17 楼Jiana(Robin.English)回复于 2006-02-27 23:07:10 得分 0

markTop

18 楼khalidwind(追风浪子)回复于 2006-02-28 10:11:16 得分 0

感谢ox_thedarkness()。  
  我想我懂你的意思了。  
  但还有最后一个实现上的问题。  
  你也说了Person.h没有   include   MyString,实际上它也不能include   MyString,它只是将MyString前置声明了。那请问当它需要MyString的实现的时候该怎么办。  
  PersonImpl该如何实现呢。  
  我现在就是无法编译Person.cpp,换句话说,我无法提供obj给clientTop

19 楼khalidwind(追风浪子)回复于 2006-02-28 10:16:35 得分 0

搞定了。谢谢Top

相关问题

  • c编译器
  • Visual C++ 2005 Express 能否编译出不依赖.net平台的程序
  • c++编译问题
  • C++编译系统
  • C#的编译器
  • C的编译器
  • dev-c++编译器
  • 求c#编译器
  • C#如何编译?
  • C++&C编译问题

关键词

  • c++
  • 编译
  • 文件
  • cpp
  • 函数
  • 修改
  • 接口
  • 作者
  • 降低
  • 指针

得分解答快速导航

  • 帖主:khalidwind
  • ox_thedarkness

相关链接

  • C/C++ Blog
  • C/C++类图书
  • C/C++类源码下载

广告也精彩

反馈

请通过下述方式给我们反馈
反馈
提问
网站简介|广告服务|VIP资费标准|银行汇款帐号|网站地图|帮助|联系方式|诚聘英才|English|问题报告
北京创新乐知广告有限公司 版权所有, 京 ICP 证 070598 号
世纪乐知(北京)网络技术有限公司 提供技术支持
Copyright © 2000-2008, CSDN.NET, All Rights Reserved
GongshangLogo