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

khalidwind 2006-02-26 09:41:22
比如。
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类啊。
...全文
266 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
khalidwind 2006-02-28
  • 打赏
  • 举报
回复
搞定了。谢谢
khalidwind 2006-02-28
  • 打赏
  • 举报
回复
感谢ox_thedarkness()。
我想我懂你的意思了。
但还有最后一个实现上的问题。
你也说了Person.h没有 include MyString,实际上它也不能include MyString,它只是将MyString前置声明了。那请问当它需要MyString的实现的时候该怎么办。
PersonImpl该如何实现呢。
我现在就是无法编译Person.cpp,换句话说,我无法提供obj给client
ox_thedarkness 2006-02-27
  • 打赏
  • 举报
回复
- __ -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。


什么叫大师? 海纳百川,有容乃大。
YufengShi 2006-02-27
  • 打赏
  • 举报
回复
Person.cpp在源代码一级依赖于Mystring
则Mystring有任何变化,Person.cpp一定要重新编译的.
除非用COM,在二进制一级使用Mystring.
xiayuxia 2006-02-27
  • 打赏
  • 举报
回复
作者的原话我 看 过
马上 实践下 看看
Jiana 2006-02-27
  • 打赏
  • 举报
回复
mark
khalidwind 2006-02-27
  • 打赏
  • 举报
回复
感谢大家的回复。
首先,我将作者的思路整理一下,大家看对不对。
要解决的问题是:在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的定义
---------------
这是作者的原话。
逸学堂 2006-02-27
  • 打赏
  • 举报
回复
文件编译是,如果把包含文件放入
.h文件中,会增加编译负担(因为编译器从.h文件开始编译)。
所以需要把.h文件放入cpp文件中,这样可以加速编译时间,并且降低编译
依赖型。

~~~~~~~~~~~
当在头文件中.h需要使用一个类时,
可以
class AA;// 声明一个类
但是必须在
.cpp文件中包括这个类的定义,即声明文件。

这样才可以达到上面的目的
Mephisto_76 2006-02-27
  • 打赏
  • 举报
回复
前向声明只有在不需要知道对象内存布局时才能有用。你这里的属性Mystring name_;定义使得编译器在编译时需要知道Mystring的定义,所以需要包含头文件,如果改用MyString* name_;
并且Mystring name() const;也改成Mystring* name() const;且头文件中不引用Mystring的任何方法和属性时就可以了。
xiayuxia 2006-02-27
  • 打赏
  • 举报
回复
PersonImple.h
里面还是要有 String.h的啊
这一节我也看过 ,只是没有去 实践下 ;
xiayuxia 2006-02-27
  • 打赏
  • 举报
回复
指针只要 声明就可以了的啊 ;

pongba 2006-02-27
  • 打赏
  • 举报
回复
弄清楚接口依赖跟实现依赖之间的关系。pImpl模式是为了解开实现依赖而不是接口依赖。
ox_thedarkness()说的是对的。
ox_thedarkness 2006-02-27
  • 打赏
  • 举报
回复
继续看看这个模式,你不是说 MyString 的实现么?

假如你后面那位短发效率狂、数据结构狂负责维护 MyString (以及一大堆其他底层数据,比如你们自己的Vector ),他同样使用这种技术: 他提供

MyString.h 和 .obj
Vector.h 和 .obj


你维护自己的 Search.h 和 Search.obj
你们的头文件中都不包含其他 .h 的引用。


现在,他如何修改 MyString 的实现和你同样没关系,只要接口无需修改 —— 你的 obj 只需要他们的.h 就可以生成。
ox_thedarkness 2006-02-27
  • 打赏
  • 举报
回复
降低的是 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 拉....


ericqxg007 2006-02-27
  • 打赏
  • 举报
回复
mark 我也不大懂
khalidwind 2006-02-27
  • 打赏
  • 举报
回复
谢谢楼上。但我还是不大明白:(
也许作者只是演义一个方法,但起码这个方法要弄通吧。
我就是不理解,Person.cpp肯定需要PersonImpl的实现,所以Person.cpp必须包含PerseonImpl.h。但是PersonImpl又必须包含Mystring的实现细节。Person.cpp如果不包含Mystring.h的话,如何能通过编译呢?
作者想达到降低编译依赖性的目的,那这个被降低的依赖性到底是哪个文件和哪个文件的依赖型呢?
ox_thedarkness 2006-02-26
  • 打赏
  • 举报
回复
- -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当然要包含了。
ox_thedarkness 2006-02-26
  • 打赏
  • 举报
回复
- - 很简单阿。为了更明确目的,我们把PersonImpl* 换成void*
你看下面的头文件:

class MyClass{
void* pImp;
public:
MyClass();
func();
};

头文件仅有这些外部接口。 很明显,MyClass 的创建和 func调用可以完成,因为MyClass的大小为4字节, 函数调用则只需要外部函数调用占位符。 连接的时候,他会寻找 obj文件并且产生真正的调用地址。

又因为,它不包含实现,所以你无论如何修改实现,都只影响这个类对应的obj。

再看实现。我们有一个 void* 可以指向任何动态创建的东西,我们把它指向实现文件中自定义的类实体; 所有函数都可以自己定义... 你想怎么做都可以... 还缺什么呢?
qhfu 2006-02-26
  • 打赏
  • 举报
回复
指针的话,只要加一个前置声明就可以了, 只需要知道类的声明不需要类的定义,所以就不需要#include *.h 文件了。类可以声明多次,但是只能定义一次
Mystring name() const;//这里是返回一个对象,因此需要#include *.h

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧