条款29:GotW#24 编译级防火墙(Compilation Firewalls)

【问题】

在C++中,如果类定义中的任何部分被改变(即使是私有成员),那么这个类所有的使用者代码都必须重新编译。问了降低这种依赖性,使用一种常见的技术就是利用一个不透明指针(opaque operator)来隐藏一部分实现细节:

class X
{
public:
	/*公有成员*/
protected:
	/*保护成员*/
private:
	/*私有成员*/
	class XImpl* pimpl_;	//非透明指针,指向已前置声明的类
};

【提问】

1.那些部分应该放入Ximpl?有常见的四种规则,它们是:

  • 将全部私有数据(但不是函数)放入Ximpl;
  • 将全部的私有成员(即包括函数)放入Ximpl;
  • 将全部的私有成员和保护成员放入Ximpl;
  • 使Ximpl完全变成为原来的X,将X编写为一个完全由简单的前置函数组成的公共接口(一个句柄/本体的变体)。

它们有什么优缺点,你如何从中选择合适的?

2.Ximpl需要指向一个X对象的“反向指针(back pointer)”吗?

【解答】

两个定义:

  • 可见类(visible class):客户代码所见并操纵的类(这里即X)。
  • pimpl:可见类隐藏了一个透明指针(也称为pimpl_)下的类实现(在这里指XImpl)。

这个习惯的手法一大优势是:它打破了编译期依存性,系统编译速度更快,因为清理了额外的#include;二是它使代码打来的编译影响范围缩小了,因为类位于Pimpl内部的部分可以自动改变,而不需要重新编译代码。

1.答案:

有常见的四种原则放入到Ximpl:

  • 将全部的私有数据(但不是函数)放入Ximpl——这样做我们可以对任何数据成员进行前置声明(而不是#include——这会使客户代码对其形成依赖),当然,我们通常可以做的更好;
  • 将全部的私有成员(包括函数)放入Ximpl——现在这几乎是作者常用手法;客户端不应该关心私有的的东西,私有的东西应该藏起来。

对此两条警告,这也是为什么加上几乎原因:

  1. 即使虚函数是私有的,你也不能把虚函数隐藏在pimpl中。
  2. 如果pimpl中的函数使用其他函数,其可能需要一个指向可见对象的“反指指针”——这又增加了一层间接性。这个反之指针称为self_。
  • 将全部的私有成员和保护成员放入Ximpl——如此更近一步的做法是错误的:保护成员绝不应该放入pimpl,因为这样做就是对其弃之不用,保护成员正是为了派生类能看到并使用而存在,如果放入pimpl,它们基本无用了。
  • 使Ximpl完全成为原来的X,将X编写为一个完全由简单的前置函数组成的公共接口——这只是少数几个有限情况下使用,其好处可以避免使用反向指针,因为所有的服务全部都在pimpl类中提供了。其主要的缺点是:这样做会是可见类对于继承而言完全没用,无论是基类还是派生类。

2.答案:

不幸的是回答为是的。无论如何,我们都会把每一个对象分裂为两部分——只因为我们隐藏了其中一部分。

当可见类中的函数被调用的时候,经常要需要使用隐藏部分(即pimpl)中的一部分函数和数据,以便完成调用者的需求。这很好也合理。然而可能不太明显的情况是:在pimpl中的函数也经常必须调用可见类的中的函数——通常是因为需要调用的函数是公有成员或者虚函数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值